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:05 UTC

[10/12] mina-sshd git commit: [SSHD-864] Use 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/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
index 9775e2c..445de2e 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
@@ -31,6 +31,7 @@ import java.util.List;
 
 import org.apache.commons.ssl.PEMItem;
 import org.apache.commons.ssl.PEMUtil;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
@@ -92,7 +93,7 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
             os.close();
 
             try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
-                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(null, getCurrentTestName(), bais, null);
+                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(null, NamedResource.ofName(getCurrentTestName()), bais, null);
 
                 assertEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
                 assertEquals("Mismatched private key", prv1, kp2.getPrivate());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
index db2f1be..5e07ba0 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
@@ -37,6 +37,7 @@ import java.util.Objects;
 import java.util.Random;
 import java.util.TreeSet;
 
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.BeforeClass;
@@ -231,7 +232,7 @@ public class RootedFileSystemProviderTest extends AssertableFile {
 
     @Test
     public void testResolveRoot() throws IOException {
-        Path root = fileSystem.getRootDirectories().iterator().next();
+        Path root = GenericUtils.head(fileSystem.getRootDirectories());
         Path dir = root.resolve("tsd");
         FileHelper.createDirectory(dir);
         Path f1 = FileHelper.createFile(dir.resolve("test.txt"));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
index e6019fe..a1b525c 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
@@ -35,6 +35,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
@@ -44,6 +45,7 @@ import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
 import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.AfterClass;
@@ -158,7 +160,7 @@ public class SecurityUtilsTest extends JUnitTestSupport {
             Collection<KeyPair> kpList = bcLoader.loadKeyPairs(null, file, TEST_PASSWORD_PROVIDER);
             assertEquals(name + ": Mismatched loaded BouncyCastle keys count", 1, GenericUtils.size(kpList));
 
-            KeyPair kpBC = kpList.iterator().next();
+            KeyPair kpBC = GenericUtils.head(kpList);
             assertTrue(name + ": Mismatched BouncyCastle vs. file values", KeyUtils.compareKeyPairs(kpFile, kpBC));
         }
 
@@ -172,20 +174,22 @@ public class SecurityUtilsTest extends JUnitTestSupport {
     private static KeyPair testLoadPrivateKeyResource(
             String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
                 throws IOException, GeneralSecurityException {
-        return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
+        return testLoadPrivateKey(
+            NamedResource.ofName(name), new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
     }
 
     private static KeyPair testLoadPrivateKeyFile(
             Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
                 throws IOException, GeneralSecurityException {
-        return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
+        return testLoadPrivateKey(new PathResource(file), new FileKeyPairProvider(file), pubType, prvType);
     }
 
     private static KeyPair testLoadPrivateKey(
-            String resourceKey, AbstractResourceKeyPairProvider<?> provider,
+            NamedResource resourceKey, AbstractResourceKeyPairProvider<?> provider,
             Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
                 throws IOException, GeneralSecurityException {
         provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
+
         Iterable<KeyPair> iterator = provider.loadKeys(null);
         List<KeyPair> pairs = new ArrayList<>();
         for (KeyPair kp : iterator) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
index 66ecb05..7cd690c 100644
--- a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
@@ -27,6 +27,7 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
@@ -69,13 +70,13 @@ public class AbstractGeneratorHostKeyProviderTest extends JUnitTestSupport {
 
         @Override
         protected KeyPair doReadKeyPair(
-                SessionContext session, String resourceKey, InputStream inputStream)
+                SessionContext session, NamedResource resourceKey, InputStream inputStream)
                     throws IOException, GeneralSecurityException {
             return null;
         }
 
         @Override
-        protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream)
+        protected void doWriteKeyPair(NamedResource resourceKey, KeyPair kp, OutputStream outputStream)
                 throws IOException, GeneralSecurityException {
             writes.incrementAndGet();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index 6ece3d9..fba078c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -26,6 +26,7 @@ import java.net.SocketAddress;
 import java.net.SocketTimeoutException;
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.ArrayList;
@@ -37,6 +38,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 import org.apache.sshd.agent.SshAgentFactory;
 import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
@@ -67,6 +69,7 @@ import org.apache.sshd.common.AttributeRepository;
 import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.ServiceFactory;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
@@ -81,7 +84,7 @@ import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 
 /**
@@ -511,12 +514,19 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         int port = hostConfig.getPort();
         ValidateUtils.checkTrue(port > 0, "Invalid port: %d", port);
 
-        Collection<KeyPair> keys = loadClientIdentities(hostConfig.getIdentities(), IoUtils.EMPTY_LINK_OPTIONS);
+        Collection<String> hostIds = hostConfig.getIdentities();
+        Collection<PathResource> idFiles = GenericUtils.isEmpty(hostIds)
+            ? Collections.emptyList()
+            : hostIds.stream()
+                .map(l -> Paths.get(l))
+                .map(PathResource::new)
+                .collect(Collectors.toCollection(() -> new ArrayList<>(hostIds.size())));
+        Collection<KeyPair> keys = loadClientIdentities(idFiles);
         return doConnect(hostConfig.getUsername(), new InetSocketAddress(host, port),
                 context, localAddress, keys, !hostConfig.isIdentitiesOnly());
     }
 
-    protected List<KeyPair> loadClientIdentities(Collection<String> locations, LinkOption... options) throws IOException {
+    protected List<KeyPair> loadClientIdentities(Collection<? extends NamedResource> locations) throws IOException {
         if (GenericUtils.isEmpty(locations)) {
             return Collections.emptyList();
         }
@@ -526,7 +536,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         ClientIdentityLoader loader = Objects.requireNonNull(getClientIdentityLoader(), "No ClientIdentityLoader");
         FilePasswordProvider provider = getFilePasswordProvider();
         boolean debugEnabled = log.isDebugEnabled();
-        for (String l : locations) {
+        for (NamedResource l : locations) {
             if (!loader.isValidLocation(l)) {
                 if (ignoreNonExisting) {
                     if (debugEnabled) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
index 04c05bd..9100f4e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
@@ -978,7 +978,7 @@ public class DefaultForwardingFilter
             if (after.size() > 1) {
                 throw new IOException("Multiple local addresses have been bound for " + address + "[" + bindAddress + "]");
             }
-            return (InetSocketAddress) after.iterator().next();
+            return (InetSocketAddress) GenericUtils.head(after);
         } catch (IOException bindErr) {
             Set<SocketAddress> after = acceptor.getBoundAddresses();
             if (GenericUtils.isEmpty(after)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index ea8f74a..c5a6918 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -308,7 +308,8 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
 
                     acceptor.bind(new InetSocketAddress(inetAddress, port));
                     if (port == 0) {
-                        port = ((InetSocketAddress) acceptor.getBoundAddresses().iterator().next()).getPort();
+                        SocketAddress selectedAddress = GenericUtils.head(acceptor.getBoundAddresses());
+                        port = ((InetSocketAddress) selectedAddress).getPort();
                         log.info("start() listen on auto-allocated port=" + port);
                     }
                 }
@@ -316,7 +317,8 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         } else {
             acceptor.bind(new InetSocketAddress(port));
             if (port == 0) {
-                port = ((InetSocketAddress) acceptor.getBoundAddresses().iterator().next()).getPort();
+                SocketAddress selectedAddress = GenericUtils.head(acceptor.getBoundAddresses());
+                port = ((InetSocketAddress) selectedAddress).getPort();
                 log.info("start() listen on auto-allocated port=" + port);
             }
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
index 3881890..1a767c6 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
@@ -29,6 +29,7 @@ import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -36,6 +37,7 @@ import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.config.keys.ClientIdentityLoader;
 import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
@@ -148,13 +150,13 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
         String clientIdentity = getCurrentTestName();
         client.setClientIdentityLoader(new ClientIdentityLoader() {
             @Override
-            public boolean isValidLocation(String location) throws IOException {
-                return clientIdentity.equals(location);
+            public boolean isValidLocation(NamedResource location) throws IOException {
+                return Objects.equals(clientIdentity, location.getName());
             }
 
             @Override
             public KeyPair loadClientIdentity(
-                    SessionContext session, String location, FilePasswordProvider provider)
+                    SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
                     return identity;
@@ -214,13 +216,13 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
 
         client.setClientIdentityLoader(new ClientIdentityLoader() {
             @Override
-            public boolean isValidLocation(String location) throws IOException {
-                return clientIdentity.equals(location);
+            public boolean isValidLocation(NamedResource location) throws IOException {
+                return Objects.equals(clientIdentity, location.getName());
             }
 
             @Override
             public KeyPair loadClientIdentity(
-                    SessionContext session, String location, FilePasswordProvider provider)
+                    SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
                     return specificIdentity;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
index b8b2e04..e55d150 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
@@ -45,6 +45,7 @@ import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.AttributeRepository;
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
@@ -61,6 +62,7 @@ import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.io.resource.URLResource;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
@@ -894,12 +896,19 @@ public class AuthenticationTest extends BaseTestSupport {
                 String keyLocation = "super-secret-passphrase-RSA-AES-128-key";
                 FilePasswordProvider passwordProvider = new FilePasswordProvider() {
                     @Override
+                    @SuppressWarnings("synthetic-access")
                     public String getPassword(
-                            SessionContext session, String resourceKey, int retryIndex)
+                            SessionContext session, NamedResource resourceKey, int retryIndex)
                                 throws IOException {
                         assertSame("Mismatched session context", s, session);
                         assertEquals("Mismatched retry index", 0, retryIndex);
-                        assertEquals("Mismatched location", keyLocation, resourceKey);
+
+                        String name = resourceKey.getName();
+                        int pos = name.lastIndexOf('/');
+                        if (pos >= 0) {
+                            name = name.substring(pos + 1);
+                        }
+                        assertEquals("Mismatched location", keyLocation, name);
 
                         Boolean passwordRequested = session.getAttribute(PASSWORD_ATTR);
                         assertNull("Password already requested", passwordRequested);
@@ -915,10 +924,11 @@ public class AuthenticationTest extends BaseTestSupport {
                         assertNotNull("Missing key file " + keyLocation, location);
 
                         KeyPair kp;
-                        try (InputStream keyData = location.openStream()) {
-                            kp = SecurityUtils.loadKeyPairIdentity(session, keyLocation, keyData, passwordProvider);
+                        URLResource resourceKey = new URLResource(location);
+                        try (InputStream keyData = resourceKey.openInputStream()) {
+                            kp = SecurityUtils.loadKeyPairIdentity(session, resourceKey, keyData, passwordProvider);
                         }
-                        assertNotNull("No identity loaded from " + keyLocation, kp);
+                        assertNotNull("No identity loaded from " + resourceKey, kp);
                         return Collections.singletonList(kp);
                     }
                 });

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/test/java/org/apache/sshd/common/channel/WindowTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/channel/WindowTest.java b/sshd-core/src/test/java/org/apache/sshd/common/channel/WindowTest.java
index b543328..e32f3d8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/channel/WindowTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/channel/WindowTest.java
@@ -43,6 +43,7 @@ import org.apache.sshd.common.io.IoInputStream;
 import org.apache.sshd.common.io.IoOutputStream;
 import org.apache.sshd.common.io.IoReadFuture;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.server.SshServer;
@@ -152,14 +153,17 @@ public class WindowTest extends BaseTestSupport {
         PropertyResolverUtils.updateProperty(client, FactoryManager.WINDOW_SIZE, 1024);
         client.start();
 
-        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             try (ChannelShell channel = session.createShellChannel()) {
                 channel.open().verify(5L, TimeUnit.SECONDS);
 
-                try (Channel serverChannel = sshd.getActiveSessions().iterator().next().getService(ServerConnectionService.class).getChannels().iterator().next()) {
+                try (Channel serverChannel = GenericUtils.head(GenericUtils.head(sshd.getActiveSessions())
+                        .getService(ServerConnectionService.class).getChannels())) {
                     Window clientLocal = channel.getLocalWindow();
                     Window clientRemote = channel.getRemoteWindow();
                     Window serverLocal = serverChannel.getLocalWindow();
@@ -168,8 +172,10 @@ public class WindowTest extends BaseTestSupport {
                     final String message = "0123456789";
                     final int nbMessages = 500;
 
-                    try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(channel.getInvertedIn(), StandardCharsets.UTF_8));
-                         BufferedReader reader = new BufferedReader(new InputStreamReader(channel.getInvertedOut(), StandardCharsets.UTF_8))) {
+                    try (BufferedWriter writer = new BufferedWriter(
+                            new OutputStreamWriter(channel.getInvertedIn(), StandardCharsets.UTF_8));
+                         BufferedReader reader = new BufferedReader(
+                             new InputStreamReader(channel.getInvertedOut(), StandardCharsets.UTF_8))) {
 
                         for (int i = 0; i < nbMessages; i++) {
                             writer.write(message);
@@ -200,7 +206,9 @@ public class WindowTest extends BaseTestSupport {
 
         client.start();
 
-        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
 
@@ -214,7 +222,8 @@ public class WindowTest extends BaseTestSupport {
                 channel.setOut(outPos);
                 channel.open().verify(7L, TimeUnit.SECONDS);
 
-                try (Channel serverChannel = sshd.getActiveSessions().iterator().next().getService(ServerConnectionService.class).getChannels().iterator().next()) {
+                try (Channel serverChannel = GenericUtils.head(GenericUtils.head(sshd.getActiveSessions())
+                        .getService(ServerConnectionService.class).getChannels())) {
                     Window clientLocal = channel.getLocalWindow();
                     Window clientRemote = channel.getRemoteWindow();
                     Window serverLocal = serverChannel.getLocalWindow();
@@ -223,8 +232,10 @@ public class WindowTest extends BaseTestSupport {
                     final String message = "0123456789";
                     final int nbMessages = 500;
 
-                    try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(inPos, StandardCharsets.UTF_8));
-                         BufferedReader reader = new BufferedReader(new InputStreamReader(outPis, StandardCharsets.UTF_8))) {
+                    try (BufferedWriter writer = new BufferedWriter(
+                            new OutputStreamWriter(inPos, StandardCharsets.UTF_8));
+                         BufferedReader reader = new BufferedReader(
+                             new InputStreamReader(outPis, StandardCharsets.UTF_8))) {
                         for (int i = 0; i < nbMessages; i++) {
                             writer.write(message);
                             writer.write('\n');
@@ -254,7 +265,9 @@ public class WindowTest extends BaseTestSupport {
 
         client.start();
 
-        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
 
@@ -262,7 +275,8 @@ public class WindowTest extends BaseTestSupport {
                 channel.setStreaming(ClientChannel.Streaming.Async);
                 channel.open().verify(5L, TimeUnit.SECONDS);
 
-                try (Channel serverChannel = sshd.getActiveSessions().iterator().next().getService(ServerConnectionService.class).getChannels().iterator().next()) {
+                try (Channel serverChannel = GenericUtils.head(GenericUtils.head(sshd.getActiveSessions())
+                        .getService(ServerConnectionService.class).getChannels())) {
                     Window clientLocal = channel.getLocalWindow();
                     Window clientRemote = channel.getRemoteWindow();
                     Window serverLocal = serverChannel.getLocalWindow();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index 5c3889f..11b0927 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -333,11 +333,11 @@ public class ServerTest extends BaseTestSupport {
             assertTrue("No changes in activated channels", channelListener.waitForActiveChannelsChange(5L, TimeUnit.SECONDS));
             assertTrue("No changes in open channels", channelListener.waitForOpenChannelsChange(5L, TimeUnit.SECONDS));
 
-            try (AbstractSession serverSession = sshd.getActiveSessions().iterator().next()) {
+            try (AbstractSession serverSession = GenericUtils.head(sshd.getActiveSessions())) {
                 AbstractConnectionService service = serverSession.getService(AbstractConnectionService.class);
                 Collection<? extends Channel> channels = service.getChannels();
 
-                try (Channel channel = channels.iterator().next()) {
+                try (Channel channel = GenericUtils.head(channels)) {
                     final long maxTimeoutValue = idleTimeoutValue + disconnectTimeoutValue + TimeUnit.SECONDS.toMillis(3L);
                     final long maxWaitNanos = TimeUnit.MILLISECONDS.toNanos(maxTimeoutValue);
                     Window wRemote = channel.getRemoteWindow();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java b/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
index 1390fcf..e6ae158 100644
--- a/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
+++ b/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
@@ -56,6 +56,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
+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.FilePasswordProvider.ResourceDecodeResult;
@@ -103,7 +104,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             List<String> lines)
@@ -128,7 +129,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)
@@ -186,7 +187,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         }
     }
 
-    public List<KeyPair> extractKeyPairs(String resourceKey, Collection<? extends Subkey> subKeys)
+    public List<KeyPair> extractKeyPairs(NamedResource resourceKey, Collection<? extends Subkey> subKeys)
             throws IOException, GeneralSecurityException {
         if (GenericUtils.isEmpty(subKeys)) {
             return Collections.emptyList();
@@ -249,7 +250,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         return kpList;
     }
 
-    public PublicKey extractPublicKey(String resourceKey, Subkey sk) throws IOException, GeneralSecurityException {
+    public PublicKey extractPublicKey(NamedResource resourceKey, Subkey sk) throws IOException, GeneralSecurityException {
         if (sk == null) {
             return null;
         }
@@ -268,7 +269,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         }
     }
 
-    public RSAPublicKey extractRSAPublicKey(String resourceKey, RSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+    public RSAPublicKey extractRSAPublicKey(NamedResource resourceKey, RSAPublicBCPGKey bcKey) throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
         }
@@ -278,7 +279,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         return generatePublicKey(KeyUtils.RSA_ALGORITHM, RSAPublicKey.class, new RSAPublicKeySpec(n, e));
     }
 
-    public PublicKey extractECPublicKey(String resourceKey, ECPublicBCPGKey bcKey) throws GeneralSecurityException {
+    public PublicKey extractECPublicKey(NamedResource resourceKey, ECPublicBCPGKey bcKey) throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
         } else if (bcKey instanceof EdDSAPublicBCPGKey) {
@@ -290,7 +291,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         }
     }
 
-    public ECPublicKey extractECDSAPublicKey(String resourceKey, ECDSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+    public ECPublicKey extractECDSAPublicKey(NamedResource resourceKey, ECDSAPublicBCPGKey bcKey) throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
         }
@@ -326,7 +327,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         return generatePublicKey(KeyUtils.EC_ALGORITHM, ECPublicKey.class, new ECPublicKeySpec(w, paramSpec));
     }
 
-    public PublicKey extractEdDSAPublicKey(String resourceKey, EdDSAPublicBCPGKey bcKey)  throws GeneralSecurityException {
+    public PublicKey extractEdDSAPublicKey(NamedResource resourceKey, EdDSAPublicBCPGKey bcKey)  throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
         }
@@ -338,7 +339,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         throw new NoSuchAlgorithmException("Unsupported EdDSA public key type: " + bcKey.getClass().getSimpleName());
     }
 
-    public DSAPublicKey extractDSSPublicKey(String resourceKey, DSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+    public DSAPublicKey extractDSSPublicKey(NamedResource resourceKey, DSAPublicBCPGKey bcKey) throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
         }
@@ -357,7 +358,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         return keyType.cast(pubKey);
     }
 
-    public PrivateKey extractPrivateKey(String resourceKey, Subkey sk, PublicKey pubKey)
+    public PrivateKey extractPrivateKey(NamedResource resourceKey, Subkey sk, PublicKey pubKey)
             throws IOException, GeneralSecurityException, PGPException {
         if (sk == null) {
             return null;
@@ -378,7 +379,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         }
     }
 
-    public ECPrivateKey extractECDSAPrivateKey(String resourceKey, ECPublicKey pubKey, ECSecretBCPGKey bcKey)
+    public ECPrivateKey extractECDSAPrivateKey(NamedResource resourceKey, ECPublicKey pubKey, ECSecretBCPGKey bcKey)
             throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
@@ -393,7 +394,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         return generatePrivateKey(KeyUtils.EC_ALGORITHM, ECPrivateKey.class, new ECPrivateKeySpec(x, params));
     }
 
-    public PrivateKey extractEdDSAPrivateKey(String resourceKey, PublicKey pubKey, EdSecretBCPGKey bcKey)
+    public PrivateKey extractEdDSAPrivateKey(NamedResource resourceKey, PublicKey pubKey, EdSecretBCPGKey bcKey)
             throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
@@ -406,7 +407,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
         throw new NoSuchAlgorithmException("Unsupported EdDSA private key type: " + bcKey.getClass().getSimpleName());
     }
 
-    public RSAPrivateKey extractRSAPrivateKey(String resourceKey, RSAPublicKey pubKey, RSASecretBCPGKey bcKey)
+    public RSAPrivateKey extractRSAPrivateKey(NamedResource resourceKey, RSAPublicKey pubKey, RSASecretBCPGKey bcKey)
             throws GeneralSecurityException {
         if (bcKey == null) {
             return null;
@@ -424,7 +425,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
                         bcKey.getCrtCoefficient()));
     }
 
-    public DSAPrivateKey extractDSSPrivateKey(String resourceKey, DSAPublicKey pubKey, DSASecretBCPGKey bcKey)
+    public DSAPrivateKey extractDSSPrivateKey(NamedResource resourceKey, DSAPublicKey pubKey, DSASecretBCPGKey bcKey)
             throws GeneralSecurityException {
         if (bcKey == null) {
             return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java b/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
index 27bbfef..cd13da5 100644
--- a/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
+++ b/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
@@ -31,6 +31,7 @@ import java.util.Collections;
 import java.util.List;
 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.config.keys.FilePasswordProvider.ResourceDecodeResult;
 import org.apache.sshd.common.config.keys.KeyUtils;
@@ -70,7 +71,7 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
         this.passwordProvider = new FilePasswordProvider() {
             @Override
             @SuppressWarnings("synthetic-access")
-            public String getPassword(SessionContext session, String resourceKey, int retryIndex) throws IOException {
+            public String getPassword(SessionContext session, NamedResource resourceKey, int retryIndex) throws IOException {
                 switch (result) {
                     case IGNORE:
                     case TERMINATE:
@@ -94,7 +95,7 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
             @Override
             @SuppressWarnings("synthetic-access")
             public 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 {
                 if (err == null) {
                     return null;
@@ -142,7 +143,7 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
 
         Collection<KeyPair> keys;
         try {
-            keys = PGPKeyPairResourceParser.INSTANCE.loadKeyPairs(null, resourceName, passwordProvider, stream);
+            keys = PGPKeyPairResourceParser.INSTANCE.loadKeyPairs(null, NamedResource.ofName(resourceName), passwordProvider, stream);
         } catch (Exception e) {
             if (result != ResourceDecodeResult.TERMINATE) {
                 fail("Mismatched result mode for " + e.getClass().getSimpleName() + "[" + e.getMessage() + "]");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/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
index 8faed0b..4a72a01 100644
--- 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
@@ -36,6 +36,7 @@ import java.util.List;
 
 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.impl.AbstractIdentityResourceLoader;
@@ -59,7 +60,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
     }
 
     @Override
-    public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+    public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
             throws IOException, GeneralSecurityException {
         if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) {
             return false;
@@ -86,7 +87,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
 
     @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 {
         List<String> pubLines = Collections.emptyList();
         List<String> prvLines = Collections.emptyList();
@@ -124,7 +125,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
     }
 
     public static List<String> extractDataLines(
-            String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines)
+            NamedResource 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);
@@ -147,7 +148,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
     }
 
     public Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             List<String> pubLines, List<String> prvLines, String prvEncryption,
             FilePasswordProvider passwordProvider)
                 throws IOException, GeneralSecurityException {
@@ -157,7 +158,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
     }
 
     public Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String pubData, String prvData, String prvEncryption,
             FilePasswordProvider passwordProvider)
                 throws IOException, GeneralSecurityException {
@@ -225,7 +226,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
         }
     }
 
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData)
+    public Collection<KeyPair> loadKeyPairs(NamedResource 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);
@@ -235,7 +236,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
         }
     }
 
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData)
+    public Collection<KeyPair> loadKeyPairs(NamedResource resourceKey, InputStream pubData, InputStream prvData)
             throws IOException, GeneralSecurityException {
         try (PuttyKeyReader pubReader =
                 new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey));
@@ -245,6 +246,6 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
         }
     }
 
-    public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+    public abstract Collection<KeyPair> loadKeyPairs(NamedResource resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
             throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
index 366aead..78aedc2 100644
--- a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
@@ -33,6 +33,7 @@ import java.security.spec.DSAPublicKeySpec;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -48,7 +49,7 @@ public class DSSPuttyKeyDecoder extends AbstractPuttyKeyDecoder<DSAPublicKey, DS
     }
 
     @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+    public Collection<KeyPair> loadKeyPairs(NamedResource resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
             throws IOException, GeneralSecurityException {
         pubReader.skip();   // skip version
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
index f5860f1..4ae9922 100644
--- a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
@@ -36,6 +36,7 @@ import java.security.spec.InvalidKeySpecException;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -54,7 +55,7 @@ public class ECDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<ECPublicKey, E
     }
 
     @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+    public Collection<KeyPair> loadKeyPairs(NamedResource resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
             throws IOException, GeneralSecurityException {
         if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchAlgorithmException("ECC not supported for " + resourceKey);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
index f610983..e461cc3 100644
--- a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
@@ -28,6 +28,7 @@ import java.security.spec.InvalidKeySpecException;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
@@ -48,7 +49,7 @@ public class EdDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<EdDSAPublicKey
     }
 
     @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+    public Collection<KeyPair> loadKeyPairs(NamedResource resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
             throws IOException, GeneralSecurityException {
         if (!SecurityUtils.isEDDSACurveSupported()) {
             throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported for " + resourceKey);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
index f098b0f..28570df 100644
--- a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
@@ -37,6 +37,7 @@ import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.IdentityResourceLoader;
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
 import org.apache.sshd.common.digest.BuiltinDigests;
@@ -112,7 +113,7 @@ public interface PuttyKeyPairResourceParser<PUB extends PublicKey, PRV extends P
     String NO_PRIVATE_KEY_ENCRYPTION_VALUE = "none";
 
     @Override
-    default boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+    default boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
             throws IOException, GeneralSecurityException {
         if (GenericUtils.isEmpty(lines)) {
             return false;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
index 0a55d55..d99b8a0 100644
--- a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
@@ -34,6 +34,7 @@ import java.security.spec.RSAPublicKeySpec;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -49,7 +50,7 @@ public class RSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<RSAPublicKey, RS
     }
 
     @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+    public Collection<KeyPair> loadKeyPairs(NamedResource resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
             throws IOException, GeneralSecurityException {
         pubReader.skip();   // skip version
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java b/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
index 795e053..0e0580a 100644
--- a/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
+++ b/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
@@ -29,6 +29,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeResult;
@@ -97,7 +98,8 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
             }
 
             List<String> lines = IoUtils.readAllLines(url);
-            assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resource, lines));
+            NamedResource resourceKey = NamedResource.ofName(resource);
+            assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resourceKey, lines));
 
             for (PuttyKeyPairResourceParser<?, ?> other : PuttyKeyUtils.BY_KEY_TYPE.values()) {
                 if (parser == other) {
@@ -105,7 +107,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
                 }
 
                 assertFalse(other.getClass().getSimpleName() + "/" + resource + " - unexpected extraction capability",
-                    other.canExtractKeyPairs(resource, lines));
+                    other.canExtractKeyPairs(resourceKey, lines));
             }
         }
     }
@@ -117,7 +119,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
 
         Collection<KeyPair> keys = parser.loadKeyPairs(null, url, null);
         assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys));
-        assertLoadedKeyPair(regularFile, keys.iterator().next());
+        assertLoadedKeyPair(regularFile, GenericUtils.head(keys));
     }
 
     @Test
@@ -130,7 +132,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
         Collection<KeyPair> keys = parser.loadKeyPairs(null, url, (s, r, index) -> PASSWORD);
         assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys));
 
-        assertLoadedKeyPair(encryptedFile, keys.iterator().next());
+        assertLoadedKeyPair(encryptedFile, GenericUtils.head(keys));
     }
 
     @Test
@@ -146,7 +148,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
             AtomicInteger retriesCount = new AtomicInteger(0);
             FilePasswordProvider provider = new FilePasswordProvider() {
                 @Override
-                public String getPassword(SessionContext session, String resourceKey, int retryIndex) throws IOException {
+                public String getPassword(SessionContext session, NamedResource resourceKey, int retryIndex) throws IOException {
                     assertSame("Mismatched session context", mockSession, session);
 
                     switch (result) {
@@ -171,7 +173,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
 
                 @Override
                 public 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 {
                     assertSame("Mismatched session context", mockSession, session);
                     if (err == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
index 6ede1e3..5754a2a 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
@@ -200,15 +200,18 @@ public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSuppor
                     }
                 }
 
-                validateHashResult(file, file.checkFileName(srcPath, algorithms, 0L, 0L, hashBlockSize), algorithms.get(0), expectedHash);
+                String hashAlgo = algorithms.get(0);
+                validateHashResult(file, file.checkFileName(srcPath, algorithms, 0L, 0L, hashBlockSize), hashAlgo, expectedHash);
                 try (CloseableHandle fileHandle = sftp.open(srcPath, SftpClient.OpenMode.Read)) {
-                    validateHashResult(hndl, hndl.checkFileHandle(fileHandle, algorithms, 0L, 0L, hashBlockSize), algorithms.get(0), expectedHash);
+                    validateHashResult(hndl, hndl.checkFileHandle(fileHandle, algorithms, 0L, 0L, hashBlockSize), hashAlgo, expectedHash);
                 }
             }
         }
     }
 
-    private void validateHashResult(NamedResource hasher, Map.Entry<String, ? extends Collection<byte[]>> result, String expectedAlgorithm, byte[] expectedHash) {
+    private void validateHashResult(
+            NamedResource hasher, Map.Entry<String, ? extends Collection<byte[]>> result,
+            String expectedAlgorithm, byte[] expectedHash) {
         String name = hasher.getName();
         assertNotNull("No result for hash=" + name, result);
         assertEquals("Mismatched hash algorithms for " + name, expectedAlgorithm, result.getKey());
@@ -217,7 +220,7 @@ public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSuppor
             Collection<byte[]> values = result.getValue();
             assertEquals("Mismatched hash values count for " + name, 1, GenericUtils.size(values));
 
-            byte[] actualHash = values.iterator().next();
+            byte[] actualHash = GenericUtils.head(values);
             if (!Arrays.equals(expectedHash, actualHash)) {
                 fail("Mismatched hashes for " + name
                     + ": expected=" + BufferUtils.toHex(':', expectedHash)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
index 478d3cc..b2d29b0 100644
--- a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
+++ b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
@@ -20,7 +20,6 @@
 package org.apache.sshd.spring.integration.sftp;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
@@ -367,11 +366,8 @@ public class ApacheSshdSftpSessionFactory
         }
 
         FilePasswordProvider provider = resolveFilePasswordProvider(session, keyResource, keyPassword);
-        Collection<KeyPair> keyPairs;
-        try (InputStream inputStream = keyResource.getInputStream()) {
-            keyPairs = PEMResourceParserUtils.PROXY.loadKeyPairs(session, keyResource.toString(), provider, inputStream);
-        }
-
+        SpringIoResource location = new SpringIoResource(keyResource);
+        Collection<KeyPair> keyPairs = PEMResourceParserUtils.PROXY.loadKeyPairs(session, location, provider);
         int numLoaded = GenericUtils.size(keyPairs);
         if (numLoaded <= 0) {
             if (debugEnabled) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/SpringIoResource.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/SpringIoResource.java b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/SpringIoResource.java
new file mode 100644
index 0000000..7ab4ade
--- /dev/null
+++ b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/SpringIoResource.java
@@ -0,0 +1,51 @@
+/*
+ * 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.spring.integration.sftp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sshd.common.util.io.resource.AbstractIoResource;
+import org.springframework.core.io.Resource;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SpringIoResource extends AbstractIoResource<Resource> {
+    public SpringIoResource(Resource r) {
+        super(Resource.class, r);
+    }
+
+    public Resource getResource() {
+        return getResourceValue();
+    }
+
+    @Override
+    public String getName() {
+        Resource r = getResource();
+        return r.getFilename();
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException {
+        Resource r = getResource();
+        return r.getInputStream();
+    }
+}