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:54:56 UTC

[01/12] mina-sshd git commit: [SSHD-862] Propagate available session context to code that deals with loading private keys

Repository: mina-sshd
Updated Branches:
  refs/heads/master 34ac8a7ad -> 025df0e85


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 5cb409c..b8b2e04 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
@@ -19,6 +19,9 @@
 package org.apache.sshd.common.auth;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
@@ -40,15 +43,18 @@ import org.apache.sshd.client.auth.keyboard.UserInteraction;
 import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 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.PropertyResolverUtils;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.signature.BuiltinSignatures;
 import org.apache.sshd.common.signature.Signature;
@@ -56,6 +62,7 @@ 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.net.SshdSocketAddress;
+import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.SshServer;
@@ -83,6 +90,10 @@ import org.junit.runners.MethodSorters;
 
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class AuthenticationTest extends BaseTestSupport {
+    private static final long CONNECT_TIMEOUT = 7L;
+    private static final AttributeRepository.AttributeKey<Boolean> PASSWORD_ATTR =
+        new AttributeRepository.AttributeKey<>();
+
     private SshServer sshd;
     private int port;
 
@@ -114,7 +125,9 @@ public class AuthenticationTest extends BaseTestSupport {
     public void testWrongPassword() throws Exception {
         try (SshClient client = setupTestClient()) {
             client.start();
-            try (ClientSession s = client.connect("user", TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect("user", TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.addPasswordIdentity("bad password");
                 assertAuthenticationResult(getCurrentTestName(), s.auth(), false);
             }
@@ -126,9 +139,11 @@ public class AuthenticationTest extends BaseTestSupport {
         try (SshClient client = setupTestClient()) {
             client.start();
 
-            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 Collection<ClientSession.ClientSessionEvent> mask =
-                        EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH);
+                    EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH);
                 Collection<ClientSession.ClientSessionEvent> result = s.waitFor(mask, TimeUnit.SECONDS.toMillis(11L));
                 assertFalse("Timeout while waiting on session events", result.contains(ClientSession.ClientSessionEvent.TIMEOUT));
 
@@ -153,17 +168,18 @@ public class AuthenticationTest extends BaseTestSupport {
 
     @Test   // see SSHD-196
     public void testChangePassword() throws Exception {
-        final PasswordAuthenticator delegate = sshd.getPasswordAuthenticator();
-        final AtomicInteger attemptsCount = new AtomicInteger(0);
+        PasswordAuthenticator delegate = sshd.getPasswordAuthenticator();
+        AtomicInteger attemptsCount = new AtomicInteger(0);
         sshd.setPasswordAuthenticator((username, password, session) -> {
             if (attemptsCount.incrementAndGet() == 1) {
-                throw new PasswordChangeRequiredException(attemptsCount.toString(), getCurrentTestName(), ServerAuthenticationManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
+                throw new PasswordChangeRequiredException(attemptsCount.toString(),
+                        getCurrentTestName(), ServerAuthenticationManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
             }
 
             return delegate.authenticate(username, password, session);
         });
 
-        final AtomicInteger changesCount = new AtomicInteger(0);
+        AtomicInteger changesCount = new AtomicInteger(0);
         sshd.setUserAuthFactories(Collections.singletonList(
             new org.apache.sshd.server.auth.password.UserAuthPasswordFactory() {
                 @Override
@@ -172,7 +188,7 @@ public class AuthenticationTest extends BaseTestSupport {
                         @Override
                         protected Boolean handleClientPasswordChangeRequest(
                                 Buffer buffer, ServerSession session, String username, String oldPassword, String newPassword)
-                                        throws Exception {
+                                    throws Exception {
                             if (changesCount.incrementAndGet() == 1) {
                                 assertNotEquals("Non-different passwords", oldPassword, newPassword);
                                 return checkPassword(buffer, session, username, newPassword);
@@ -184,10 +200,11 @@ public class AuthenticationTest extends BaseTestSupport {
                 }
             }
         ));
-        PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+        PropertyResolverUtils.updateProperty(sshd,
+            ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
 
         try (SshClient client = setupTestClient()) {
-            final AtomicInteger updatesCount = new AtomicInteger(0);
+            AtomicInteger updatesCount = new AtomicInteger(0);
             client.setUserInteraction(new UserInteraction() {
                 @Override
                 public boolean isInteractionAllowed(ClientSession session) {
@@ -195,7 +212,9 @@ public class AuthenticationTest extends BaseTestSupport {
                 }
 
                 @Override
-                public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+                public String[] interactive(
+                        ClientSession session, String name, String instruction,
+                        String lang, String[] prompt, boolean[] echo) {
                     throw new UnsupportedOperationException("Unexpected call");
                 }
 
@@ -208,14 +227,16 @@ public class AuthenticationTest extends BaseTestSupport {
                 }
             });
 
-            final AtomicInteger sentCount = new AtomicInteger(0);
+            AtomicInteger sentCount = new AtomicInteger(0);
             client.setUserAuthFactories(Collections.singletonList(
                 new org.apache.sshd.client.auth.password.UserAuthPasswordFactory() {
                     @Override
                     public org.apache.sshd.client.auth.password.UserAuthPassword create() {
                         return new org.apache.sshd.client.auth.password.UserAuthPassword() {
                             @Override
-                            protected IoWriteFuture sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
+                            protected IoWriteFuture sendPassword(
+                                    Buffer buffer, ClientSession session, String oldPassword, String newPassword)
+                                        throws IOException {
                                 int count = sentCount.incrementAndGet();
                                 // 1st one is the original one (which is denied by the server)
                                 // 2nd one is the updated one retrieved from the user interaction
@@ -228,11 +249,14 @@ public class AuthenticationTest extends BaseTestSupport {
                         };
                     }
             }));
-            PropertyResolverUtils.updateProperty(client, ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+            PropertyResolverUtils.updateProperty(client,
+                ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
 
             client.start();
 
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.addPasswordIdentity(getCurrentTestName());
                 s.auth().verify(11L, TimeUnit.SECONDS);
                 assertEquals("No password change request generated", 2, attemptsCount.get());
@@ -250,15 +274,18 @@ public class AuthenticationTest extends BaseTestSupport {
             sshd.setPasswordAuthenticator(RejectAllPasswordAuthenticator.INSTANCE);
 
             client.start();
-            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 Collection<ClientSession.ClientSessionEvent> result =
-                        s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
-                        TimeUnit.SECONDS.toMillis(11L));
+                    s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
+                    TimeUnit.SECONDS.toMillis(11L));
                 assertFalse("Timeout while waiting for session", result.contains(ClientSession.ClientSessionEvent.TIMEOUT));
 
                 String password = getCurrentTestName();
                 try {
-                    assertAuthenticationResult(getCurrentTestName(), authPassword(s, getCurrentTestName(), password), false);
+                    assertAuthenticationResult(getCurrentTestName(),
+                        authPassword(s, getCurrentTestName(), password), false);
                 } finally {
                     s.removePasswordIdentity(password);
                 }
@@ -277,24 +304,26 @@ public class AuthenticationTest extends BaseTestSupport {
             client.start();
 
             try (ClientSession s = client.connect(null, TEST_LOCALHOST, port)
-                        .verify(7L, TimeUnit.SECONDS)
-                        .getSession()) {
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 Collection<ClientSession.ClientSessionEvent> result =
-                        s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
-                        TimeUnit.SECONDS.toMillis(11L));
+                    s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
+                    TimeUnit.SECONDS.toMillis(11L));
                 assertFalse("Timeout while waiting for session", result.contains(ClientSession.ClientSessionEvent.TIMEOUT));
 
                 KeyPairProvider provider = createTestHostKeyProvider();
                 KeyPair pair = provider.loadKey(s, KeyPairProvider.SSH_RSA);
                 try {
-                    assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
+                    assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY,
+                        authPublicKey(s, getCurrentTestName(), pair), false);
                 } finally {
                     s.removePublicKeyIdentity(pair);
                 }
 
                 String password = getCurrentTestName();
                 try {
-                    assertAuthenticationResult(UserAuthMethodFactory.PASSWORD, authPassword(s, getCurrentTestName(), password), true);
+                    assertAuthenticationResult(UserAuthMethodFactory.PASSWORD,
+                        authPassword(s, getCurrentTestName(), password), true);
                 } finally {
                     s.removePasswordIdentity(password);
                 }
@@ -313,15 +342,15 @@ public class AuthenticationTest extends BaseTestSupport {
                 public InteractiveChallenge generateChallenge(ServerSession session, String username, String lang, String subMethods) {
                     assertEquals("Mismatched user language",
                             PropertyResolverUtils.getStringProperty(
-                                    client,
-                                    org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.INTERACTIVE_LANGUAGE_TAG,
-                                    org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.DEFAULT_INTERACTIVE_LANGUAGE_TAG),
+                                client,
+                                org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.INTERACTIVE_LANGUAGE_TAG,
+                                org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.DEFAULT_INTERACTIVE_LANGUAGE_TAG),
                             lang);
                     assertEquals("Mismatched client sub-methods",
                             PropertyResolverUtils.getStringProperty(
-                                    client,
-                                    org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.INTERACTIVE_SUBMETHODS,
-                                    org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.DEFAULT_INTERACTIVE_SUBMETHODS),
+                                client,
+                                org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.INTERACTIVE_SUBMETHODS,
+                                org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive.DEFAULT_INTERACTIVE_SUBMETHODS),
                             subMethods);
 
                     InteractiveChallenge challenge = super.generateChallenge(session, username, lang, subMethods);
@@ -348,7 +377,7 @@ public class AuthenticationTest extends BaseTestSupport {
             client.start();
 
             try (ClientSession s = client.connect(null, TEST_LOCALHOST, port)
-                    .verify(7L, TimeUnit.SECONDS)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                     .getSession()) {
                 Collection<ClientSession.ClientSessionEvent> result =
                     s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
@@ -358,13 +387,15 @@ public class AuthenticationTest extends BaseTestSupport {
                 KeyPairProvider provider = createTestHostKeyProvider();
                 KeyPair pair = provider.loadKey(s, KeyPairProvider.SSH_RSA);
                 try {
-                    assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
+                    assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY,
+                        authPublicKey(s, getCurrentTestName(), pair), false);
                 } finally {
                     s.removePublicKeyIdentity(pair);
                 }
 
                 try {
-                    assertAuthenticationResult(UserAuthMethodFactory.KB_INTERACTIVE, authInteractive(s, getCurrentTestName(), getCurrentTestName()), true);
+                    assertAuthenticationResult(UserAuthMethodFactory.KB_INTERACTIVE,
+                        authInteractive(s, getCurrentTestName(), getCurrentTestName()), true);
                 } finally {
                     s.setUserInteraction(null);
                 }
@@ -376,27 +407,29 @@ public class AuthenticationTest extends BaseTestSupport {
 
     @Test   // see SSHD-563
     public void testAuthMultiChallengeKeyInteractive() throws Exception {
-        final Class<?> anchor = getClass();
-        final InteractiveChallenge challenge = new InteractiveChallenge();
+        Class<?> anchor = getClass();
+        InteractiveChallenge challenge = new InteractiveChallenge();
         challenge.setInteractionName(getCurrentTestName());
         challenge.setInteractionInstruction(anchor.getPackage().getName());
         challenge.setLanguageTag(Locale.getDefault().getLanguage());
 
-        final Map<String, String> rspMap = GenericUtils.<String, String>mapBuilder(String.CASE_INSENSITIVE_ORDER)
-                .put("class", anchor.getSimpleName())
-                .put("package", anchor.getPackage().getName())
-                .put("test", getCurrentTestName())
-                .build();
+        Map<String, String> rspMap = GenericUtils.<String, String>mapBuilder(String.CASE_INSENSITIVE_ORDER)
+            .put("class", anchor.getSimpleName())
+            .put("package", anchor.getPackage().getName())
+            .put("test", getCurrentTestName())
+            .build();
         for (String prompt : rspMap.keySet()) {
             challenge.addPrompt(prompt, (GenericUtils.size(challenge.getPrompts()) & 0x1) != 0);
         }
 
-        PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
-        final AtomicInteger genCount = new AtomicInteger(0);
-        final AtomicInteger authCount = new AtomicInteger(0);
+        PropertyResolverUtils.updateProperty(sshd,
+            ServerAuthenticationManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
+        AtomicInteger genCount = new AtomicInteger(0);
+        AtomicInteger authCount = new AtomicInteger(0);
         sshd.setKeyboardInteractiveAuthenticator(new KeyboardInteractiveAuthenticator() {
             @Override
-            public InteractiveChallenge generateChallenge(ServerSession session, String username, String lang, String subMethods) {
+            public InteractiveChallenge generateChallenge(
+                    ServerSession session, String username, String lang, String subMethods) {
                 assertEquals("Unexpected challenge call", 1, genCount.incrementAndGet());
                 return challenge;
             }
@@ -418,10 +451,11 @@ public class AuthenticationTest extends BaseTestSupport {
                 return true;
             }
         });
-        PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
+        PropertyResolverUtils.updateProperty(sshd,
+            ServerAuthenticationManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
 
         try (SshClient client = setupTestClient()) {
-            final AtomicInteger interactiveCount = new AtomicInteger(0);
+            AtomicInteger interactiveCount = new AtomicInteger(0);
             client.setUserInteraction(new UserInteraction() {
                 @Override
                 public boolean isInteractionAllowed(ClientSession session) {
@@ -429,7 +463,9 @@ public class AuthenticationTest extends BaseTestSupport {
                 }
 
                 @Override
-                public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+                public String[] interactive(
+                        ClientSession session, String name, String instruction,
+                        String lang, String[] prompt, boolean[] echo) {
                     assertEquals("Unexpected multiple calls", 1, interactiveCount.incrementAndGet());
                     assertEquals("Mismatched name", challenge.getInteractionName(), name);
                     assertEquals("Mismatched instruction", challenge.getInteractionInstruction(), instruction);
@@ -455,11 +491,14 @@ public class AuthenticationTest extends BaseTestSupport {
                     throw new UnsupportedOperationException("Unexpected call");
                 }
             });
-            PropertyResolverUtils.updateProperty(client, ServerAuthenticationManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
+            PropertyResolverUtils.updateProperty(client,
+                ServerAuthenticationManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
 
             client.start();
 
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.auth().verify(11L, TimeUnit.SECONDS);
                 assertEquals("Bad generated challenge count", 1, genCount.get());
                 assertEquals("Bad authentication count", 1, authCount.get());
@@ -472,19 +511,21 @@ public class AuthenticationTest extends BaseTestSupport {
 
     @Test   // see SSHD-196
     public void testAuthPasswordChangeRequest() throws Exception {
-        final PasswordAuthenticator delegate = Objects.requireNonNull(sshd.getPasswordAuthenticator(), "No password authenticator");
-        final AtomicInteger attemptsCount = new AtomicInteger(0);
+        PasswordAuthenticator delegate = Objects.requireNonNull(sshd.getPasswordAuthenticator(), "No password authenticator");
+        AtomicInteger attemptsCount = new AtomicInteger(0);
         sshd.setPasswordAuthenticator((username, password, session) -> {
             if (attemptsCount.incrementAndGet() == 1) {
-                throw new PasswordChangeRequiredException(attemptsCount.toString(), getCurrentTestName(), ServerAuthenticationManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
+                throw new PasswordChangeRequiredException(attemptsCount.toString(),
+                    getCurrentTestName(), ServerAuthenticationManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
             }
 
             return delegate.authenticate(username, password, session);
         });
-        PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+        PropertyResolverUtils.updateProperty(sshd,
+            ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
 
         try (SshClient client = setupTestClient()) {
-            final AtomicInteger updatesCount = new AtomicInteger(0);
+            AtomicInteger updatesCount = new AtomicInteger(0);
             client.setUserInteraction(new UserInteraction() {
                 @Override
                 public boolean isInteractionAllowed(ClientSession session) {
@@ -492,7 +533,9 @@ public class AuthenticationTest extends BaseTestSupport {
                 }
 
                 @Override
-                public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+                public String[] interactive(
+                        ClientSession session, String name, String instruction,
+                        String lang, String[] prompt, boolean[] echo) {
                     throw new UnsupportedOperationException("Unexpected call");
                 }
 
@@ -504,11 +547,14 @@ public class AuthenticationTest extends BaseTestSupport {
                     return getCurrentTestName();
                 }
             });
-            PropertyResolverUtils.updateProperty(client, ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+            PropertyResolverUtils.updateProperty(client,
+                ServerAuthenticationManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
 
             client.start();
 
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.addPasswordIdentity(getCurrentTestName());
                 s.auth().verify(11L, TimeUnit.SECONDS);
                 assertEquals("No password change request generated", 2, attemptsCount.get());
@@ -541,7 +587,9 @@ public class AuthenticationTest extends BaseTestSupport {
             });
 
             client.start();
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.addPasswordIdentity(getCurrentTestName());
 
                 AuthFuture future = s.auth();
@@ -580,7 +628,9 @@ public class AuthenticationTest extends BaseTestSupport {
             client.setPasswordIdentityProvider(provider);
 
             client.start();
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.auth().verify(11L, TimeUnit.SECONDS);
                 assertEquals("Mismatched load passwords count", 1, loadCount.get());
                 assertSame("Mismatched passwords identity provider", provider, s.getPasswordIdentityProvider());
@@ -600,7 +650,7 @@ public class AuthenticationTest extends BaseTestSupport {
         sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
         sshd.setPasswordAuthenticator(RejectAllPasswordAuthenticator.INSTANCE);
 
-        final KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256);
+        KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256);
         sshd.setPublickeyAuthenticator((username, key, session) -> {
             String keyType = KeyUtils.getKeyType(key);
             String expType = KeyUtils.getKeyType(clientIdentity);
@@ -611,25 +661,36 @@ public class AuthenticationTest extends BaseTestSupport {
 
         try (SshClient client = setupTestClient()) {
             // force server to use only the RSA key
-            final NamedFactory<Signature> kexSignature = BuiltinSignatures.rsa;
+            NamedFactory<Signature> kexSignature = BuiltinSignatures.rsa;
             client.setSignatureFactories(Collections.singletonList(kexSignature));
             client.setServerKeyVerifier((sshClientSession, remoteAddress, serverKey) -> {
                 String keyType = KeyUtils.getKeyType(serverKey);
                 String expType = kexSignature.getName();
                 assertEquals("Mismatched server key type", expType, keyType);
 
-                KeyPair kp = ValidateUtils.checkNotNull(serverKeys.loadKey(null, keyType), "No server key for type=%s", keyType);
+                KeyPair kp;
+                try {
+                    kp = ValidateUtils.checkNotNull(serverKeys.loadKey(null, keyType), "No server key for type=%s", keyType);
+                } catch (IOException | GeneralSecurityException e) {
+                    throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ")"
+                        + " keys loading exception: " + e.getMessage(), e);
+                }
                 assertKeyEquals("Mismatched server public keys", kp.getPublic(), serverKey);
                 return true;
             });
 
             // allow only EC keys for public key authentication
-            org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory factory = new org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory();
-            factory.setSignatureFactories(Arrays.asList(BuiltinSignatures.nistp256, BuiltinSignatures.nistp384, BuiltinSignatures.nistp521));
+            org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory factory =
+                new org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory();
+            factory.setSignatureFactories(
+                Arrays.asList(
+                    BuiltinSignatures.nistp256, BuiltinSignatures.nistp384, BuiltinSignatures.nistp521));
             client.setUserAuthFactories(Collections.singletonList(factory));
 
             client.start();
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.addPublicKeyIdentity(clientIdentity);
                 s.auth().verify(11L, TimeUnit.SECONDS);
             } finally {
@@ -647,13 +708,16 @@ public class AuthenticationTest extends BaseTestSupport {
                     public org.apache.sshd.server.auth.pubkey.UserAuthPublicKey create() {
                         return new org.apache.sshd.server.auth.pubkey.UserAuthPublicKey() {
                             @Override
-                            protected void sendPublicKeyResponse(ServerSession session, String username, String alg, PublicKey key,
-                                    byte[] keyBlob, int offset, int blobLen, Buffer buffer) throws Exception {
+                            protected void sendPublicKeyResponse(
+                                    ServerSession session, String username, String alg, PublicKey key,
+                                    byte[] keyBlob, int offset, int blobLen, Buffer buffer)
+                                        throws Exception {
                                 int count = challengeCounter.incrementAndGet();
                                 outputDebugMessage("sendPublicKeyChallenge(%s)[%s]: count=%d", session, alg, count);
                                 if (count == 1) {
                                     // send wrong key type
-                                    super.sendPublicKeyResponse(session, username, KeyPairProvider.SSH_DSS, key, keyBlob, offset, blobLen, buffer);
+                                    super.sendPublicKeyResponse(session, username,
+                                        KeyPairProvider.SSH_DSS, key, keyBlob, offset, blobLen, buffer);
                                 } else if (count == 2) {
                                     // send another key
                                     KeyPair otherPair = org.apache.sshd.util.test.CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
@@ -677,7 +741,9 @@ public class AuthenticationTest extends BaseTestSupport {
 
             try {
                 for (int index = 1; index <= 4; index++) {
-                    try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                    try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                            .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                            .getSession()) {
                         s.addPublicKeyIdentity(clientIdentity);
                         s.auth().verify(17L, TimeUnit.SECONDS);
                         assertEquals("Mismatched number of challenges", 3, challengeCounter.get());
@@ -711,8 +777,8 @@ public class AuthenticationTest extends BaseTestSupport {
         sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
         sshd.setPublickeyAuthenticator(RejectAllPublickeyAuthenticator.INSTANCE);
         sshd.setUserAuthFactories(
-                Collections.singletonList(
-                        org.apache.sshd.server.auth.hostbased.UserAuthHostBasedFactory.INSTANCE));
+            Collections.singletonList(
+                org.apache.sshd.server.auth.hostbased.UserAuthHostBasedFactory.INSTANCE));
 
         try (SshClient client = setupTestClient()) {
             org.apache.sshd.client.auth.hostbased.UserAuthHostBasedFactory factory =
@@ -723,7 +789,9 @@ public class AuthenticationTest extends BaseTestSupport {
 
             client.setUserAuthFactories(Collections.singletonList(factory));
             client.start();
-            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
                 s.auth().verify(11L, TimeUnit.SECONDS);
                 assertEquals("Mismatched authenticator invocation count", 1, invocationCount.get());
             } finally {
@@ -761,7 +829,9 @@ public class AuthenticationTest extends BaseTestSupport {
             client.start();
             try {
                 for (int index = 1; index < 3; index++) {
-                    try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                    try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                            .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                            .getSession()) {
                         s.addPasswordIdentity(getCurrentTestName());
                         s.addPublicKeyIdentity(kp);
 
@@ -797,7 +867,9 @@ public class AuthenticationTest extends BaseTestSupport {
                 for (String password : new String[]{
                     " ", "    ", "  " + getCurrentTestName(), getCurrentTestName() + "    "
                 }) {
-                    try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                    try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                            .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                            .getSession()) {
                         s.addPasswordIdentity(password);
 
                         AuthFuture auth = s.auth();
@@ -811,6 +883,56 @@ public class AuthenticationTest extends BaseTestSupport {
         }
     }
 
+    @Test   // see SSHD-862
+    public void testSessionContextPropagatedToKeyFilePasswordProvider() throws Exception {
+        try (SshClient client = setupTestClient()) {
+            client.start();
+
+            try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+                    .getSession()) {
+                String keyLocation = "super-secret-passphrase-RSA-AES-128-key";
+                FilePasswordProvider passwordProvider = new FilePasswordProvider() {
+                    @Override
+                    public String getPassword(
+                            SessionContext session, String resourceKey, int retryIndex)
+                                throws IOException {
+                        assertSame("Mismatched session context", s, session);
+                        assertEquals("Mismatched retry index", 0, retryIndex);
+                        assertEquals("Mismatched location", keyLocation, resourceKey);
+
+                        Boolean passwordRequested = session.getAttribute(PASSWORD_ATTR);
+                        assertNull("Password already requested", passwordRequested);
+                        session.setAttribute(PASSWORD_ATTR, Boolean.TRUE);
+                        return "super secret passphrase";
+                    }
+                };
+                s.setKeyPairProvider(new KeyPairProvider() {
+                    @Override
+                    public Iterable<KeyPair> loadKeys(SessionContext session) throws IOException, GeneralSecurityException {
+                        assertSame("Mismatched session context", s, session);
+                        URL location = getClass().getResource(keyLocation);
+                        assertNotNull("Missing key file " + keyLocation, location);
+
+                        KeyPair kp;
+                        try (InputStream keyData = location.openStream()) {
+                            kp = SecurityUtils.loadKeyPairIdentity(session, keyLocation, keyData, passwordProvider);
+                        }
+                        assertNotNull("No identity loaded from " + keyLocation, kp);
+                        return Collections.singletonList(kp);
+                    }
+                });
+                s.auth().verify(17L, TimeUnit.SECONDS);
+
+                Boolean passwordRequested = s.getAttribute(PASSWORD_ATTR);
+                assertNotNull("Password provider not invoked", passwordRequested);
+                assertTrue("Password not requested", passwordRequested.booleanValue());
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
     private static void assertAuthenticationResult(String message, AuthFuture future, boolean expected) throws IOException {
         assertTrue(message + ": failed to get result on time", future.await(5L, TimeUnit.SECONDS));
         assertEquals(message + ": mismatched authentication result", expected, future.isSuccess());
@@ -822,7 +944,7 @@ public class AuthenticationTest extends BaseTestSupport {
         return s.auth();
     }
 
-    private static AuthFuture authInteractive(final ClientSession s, String user, String pswd) throws IOException {
+    private static AuthFuture authInteractive(ClientSession s, String user, String pswd) throws IOException {
         s.setUsername(user);
         final String[] response = {pswd};
         s.setUserInteraction(new UserInteraction() {
@@ -832,7 +954,9 @@ public class AuthenticationTest extends BaseTestSupport {
             }
 
             @Override
-            public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+            public String[] interactive(
+                    ClientSession session, String name, String instruction,
+                    String lang, String[] prompt, boolean[] echo) {
                 assertSame("Mismatched session instance", s, session);
                 assertEquals("Mismatched prompt size", 1, GenericUtils.length(prompt));
                 assertTrue("Mismatched prompt: " + prompt[0], prompt[0].toLowerCase().contains("password"));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
index 3685715..0f5f191 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sshd.common.auth;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.util.Map;
@@ -57,7 +59,7 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
     private KeyPair pairRsaBad;
     private PublickeyAuthenticator delegate;
 
-    public SinglePublicKeyAuthTest() {
+    public SinglePublicKeyAuthTest() throws IOException, GeneralSecurityException {
         SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
         provider.setAlgorithm(KeyUtils.RSA_ALGORITHM);
         pairRsaBad = provider.loadKey(null, KeyPairProvider.SSH_RSA);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key b/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key
new file mode 100644
index 0000000..2b93a42
--- /dev/null
+++ b/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,D41AC063160FCC09B1E1931FB43BCEAA
+
+V218dGT2pUpimnwEVn+2ljvK6mvm2aNLlaakMunlHfIswrakJ1WTs8a61pYILOn9
+MGHrCiqe6ZI7FBJ2wXpSxhcuM3fzk6/dW4Ghh4EHG1Y94w97EizxNfyz/iI2XQw0
+i6ttaDLVzP8UcSRElqG+Zpe1A7EE/DkdkXD3f/DaGHtu1zirVeaEIggMLjfTdwnR
+sH9VnUZhe74VdPV0x16h7JjLt5fcbIjqJ6NWW4QvQpPBv3k0oiUy/nP4FXg1b7VW
+7SowuCPi+mF821hj4xSO8ETlAU1eZdtgXqtejtKm0iDtsjnTBZPvDDrq5ephBlxO
+k7JBJG1LFUiDIGnpxos5nCsKEo8UAw9a5/D4xE3C6UTocXon28XGzVCbkZBN6jcd
+UbpjCVwKMJmFL97487u9S57xrGTmJdi1AtF9Rei8juTTQY4+r3l2c7JtdtcbLUhj
+iLvdYnbh6kUEyE19/+omJaWGQlFhYp7ZMRRQSiz6TD8lhSIBPpXzs+uMfhkrifVk
+3WpjRoikmPOOFLtecee5Rp+SpGd700XgLnxwZ47l0FNfrKKqd3+nZX4JILQ2M0JP
+sBx8gcIew8aUqMzWrwZxbrt9Pd1+2kSNVG9hpLoNoA4WpQnYQMo4L0eTCeMNUOap
+f9H0Hh3QnqXTPHbcYZJCGE2RUxLzn/d7rUxUdEzER+pkhJcw9JbV/izTrpDHs9bM
+cfBLggQvs+UIBww2OFz2BztwoQzsSEuNW/SxG/y6SfRUQq5TZw9NxYnrrqfBXKtx
+svB1JVbn2fKq2Lvi8AZ1fF3tyrNot/tptDf0yDHejWDUvVx5cXsKVK2BbVjbZ88k
+mBtUbw7ea9Ev7ZsihNB2EdhPjLhhKlKLIZznPKeXL3GDTXqCgCxTVh4wLvaR8rDU
+C3Isil4WprCeynmZpOe7bxAZDm2QCobnDB8sLQqBI4zgH8X/1iyXJVdSKfK9vxcB
+sJ5pYCcS2q0C+CJkn6HVTlMQ5CyyzvPaDJukJoxwxsZ5hgCsUHFzrvyGnXqGfTBD
+qEW+oA7cj48CfweV5pXHj+mZpCrpn1zRVJRz4h1FZRsttPGtBRAlns5I3kh5BPRs
+4m1BO1jiWyp/7HkUrDRhEf/QeJsP+mTH32pQgnngZ/AGA0PUcKanMUpe1d2ju83V
+EIcTz9ycTHPiOAM6GaVt54fKj9WRBU+7pf14ZdJmfhp6twc0jNtaTh+/I6Pfb0jN
+0d6yKV//pOeJJBNhuOJgm/0vfkOnOojIJchOQCRt5Lg/a4fD/JXtLOed2zOQa+0J
+3d8Y93mQX/iN1wi95/sG79YBYF3FkJYVhjosSKbiIaxIn76zIx4IAlziycDKvgpr
+JgZcVvCDc4flwrf3Cv/uHK7UWOE+16X1CfAy8JzFg5bhiMmhgsJyHmd+zDGrY6NX
+zz+wLmwOenEwC40gpt89OXbgMcwJMtfiSusatRtZ+AAs0jb/8jExVXfcYE3m3r1/
+FqLZ7seTQT2D01YoPlwUtSPxzaZbziAJ/NaGmURnBGVibDCJxwUAiOSIQH4prIfg
+Q2FCJeMTbLV43Lanlby5nrmLkzsw3uo1MO8Of1DbcnVUHNSwrp/nNzrYdxBLIvqS
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key.pub
new file mode 100644
index 0000000..b1d66a6
--- /dev/null
+++ b/sshd-core/src/test/resources/org/apache/sshd/common/auth/super-secret-passphrase-RSA-AES-128-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/oDbs/yYxBdT02ldP6JIrcETJQ+TCml1tHYuo8cIQp0DZCgRZiEZ4foucAT8R/vLK01nnjRzrI42MXiCzyAHb1sPRD0Fsbpa4TFJczPBBRM2mp56airnArQUMmg/ZKlOf82hn+u7Kgn+ljyjYG5FrdoUBju62i0H4+oBfX+pTkd5ruUgqLyPUC3qtNLwjS4PIPAda/pfpsi9UawQ4ommWCCLlwK55NiSrPDBwKNuVWROcQps2NZRxzRLQEiiCEVBEdiUqqUQ+dg2beLV/4cCS860ZZRvCfe+ko8TUBJ7SLtcrvOEYJOKIZDVhcnQKN/wyXCHExSYytUconlFn/9YX root@devenv-aas
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 b4f7051..1390fcf 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
@@ -61,6 +61,7 @@ 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;
 import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -102,7 +103,10 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            List<String> lines)
                 throws IOException, GeneralSecurityException {
         // We need to re-construct the original data - including start/end markers
         String eol = System.lineSeparator();
@@ -119,15 +123,18 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
 
         String keyData = sb.toString();
         byte[] dataBytes = keyData.getBytes(StandardCharsets.US_ASCII);
-        return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataBytes);
+        return extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, dataBytes);
     }
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
         for (int retryCount = 0;; retryCount++) {
-            String password = (passwordProvider == null) ? null : passwordProvider.getPassword(resourceKey, retryCount);
+            String password = (passwordProvider == null) ? null : passwordProvider.getPassword(session, resourceKey, retryCount);
             Collection<KeyPair> keys;
             try {
                 if (retryCount > 0) {
@@ -144,7 +151,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
                 keys = extractKeyPairs(resourceKey, key.getSubkeys());
             } catch (IOException | GeneralSecurityException | PGPException | RuntimeException e) {
                 ResourceDecodeResult result = (passwordProvider != null)
-                    ? passwordProvider.handleDecodeAttemptResult(resourceKey, retryCount, password, e)
+                    ? passwordProvider.handleDecodeAttemptResult(session, resourceKey, retryCount, password, e)
                     : ResourceDecodeResult.TERMINATE;
                 if (result == null) {
                     result = ResourceDecodeResult.TERMINATE;
@@ -173,7 +180,7 @@ public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
             }
 
             if (passwordProvider != null) {
-                passwordProvider.handleDecodeAttemptResult(resourceKey, retryCount, password, null);
+                passwordProvider.handleDecodeAttemptResult(session, resourceKey, retryCount, password, null);
             }
             return keys;
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 6bb2f70..27bbfef 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
@@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 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;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.JUnitTestSupport;
@@ -69,7 +70,7 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
         this.passwordProvider = new FilePasswordProvider() {
             @Override
             @SuppressWarnings("synthetic-access")
-            public String getPassword(String resourceKey, int retryIndex) throws IOException {
+            public String getPassword(SessionContext session, String resourceKey, int retryIndex) throws IOException {
                 switch (result) {
                     case IGNORE:
                     case TERMINATE:
@@ -93,7 +94,7 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
             @Override
             @SuppressWarnings("synthetic-access")
             public ResourceDecodeResult handleDecodeAttemptResult(
-                    String resourceKey, int retryIndex, String password, Exception err)
+                    SessionContext session, String resourceKey, int retryIndex, String password, Exception err)
                         throws IOException, GeneralSecurityException {
                 if (err == null) {
                     return null;
@@ -141,7 +142,7 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
 
         Collection<KeyPair> keys;
         try {
-            keys = PGPKeyPairResourceParser.INSTANCE.loadKeyPairs(resourceName, passwordProvider, stream);
+            keys = PGPKeyPairResourceParser.INSTANCE.loadKeyPairs(null, 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/2b013cca/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 05a3c6f..8faed0b 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
@@ -40,6 +40,7 @@ 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;
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -85,8 +86,8 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
 
     @Override
     public Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-                    throws IOException, GeneralSecurityException {
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                throws IOException, GeneralSecurityException {
         List<String> pubLines = Collections.emptyList();
         List<String> prvLines = Collections.emptyList();
         String prvEncryption = null;
@@ -119,7 +120,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
             }
         }
 
-        return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider);
+        return loadKeyPairs(session, resourceKey, pubLines, prvLines, prvEncryption, passwordProvider);
     }
 
     public static List<String> extractDataLines(
@@ -146,15 +147,19 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
     }
 
     public Collection<KeyPair> loadKeyPairs(
-            String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider)
+            SessionContext session, String resourceKey,
+            List<String> pubLines, List<String> prvLines, String prvEncryption,
+            FilePasswordProvider passwordProvider)
                 throws IOException, GeneralSecurityException {
-        return loadKeyPairs(resourceKey,
+        return loadKeyPairs(session, resourceKey,
                 KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines),
                 prvEncryption, passwordProvider);
     }
 
     public Collection<KeyPair> loadKeyPairs(
-            String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider)
+            SessionContext session, String resourceKey,
+            String pubData, String prvData, String prvEncryption,
+            FilePasswordProvider passwordProvider)
                 throws IOException, GeneralSecurityException {
         Decoder b64Decoder = Base64.getDecoder();
         byte[] pubBytes = b64Decoder.decode(pubData);
@@ -187,7 +192,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
         }
 
         for (int retryIndex = 0;; retryIndex++) {
-            String password = passwordProvider.getPassword(resourceKey, retryIndex);
+            String password = passwordProvider.getPassword(session, resourceKey, retryIndex);
 
             Collection<KeyPair> keys;
             try {
@@ -199,7 +204,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
                 keys = loadKeyPairs(resourceKey, pubBytes, decBytes);
             } catch (IOException | GeneralSecurityException | RuntimeException e) {
                 ResourceDecodeResult result =
-                    passwordProvider.handleDecodeAttemptResult(resourceKey, retryIndex, password, e);
+                    passwordProvider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, e);
                 if (result == null) {
                     result = ResourceDecodeResult.TERMINATE;
                 }
@@ -215,7 +220,7 @@ public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends
                 }
             }
 
-            passwordProvider.handleDecodeAttemptResult(resourceKey, retryIndex, password, null);
+            passwordProvider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, null);
             return keys;
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 8f1f2eb..795e053 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
@@ -35,6 +35,7 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeRes
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
@@ -49,6 +50,7 @@ import org.junit.runners.MethodSorters;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+import org.mockito.Mockito;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -113,7 +115,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
         URL url = getClass().getResource(regularFile);
         assertNotNull("Missing test resource: " + regularFile, url);
 
-        Collection<KeyPair> keys = parser.loadKeyPairs(url, null);
+        Collection<KeyPair> keys = parser.loadKeyPairs(null, url, null);
         assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys));
         assertLoadedKeyPair(regularFile, keys.iterator().next());
     }
@@ -125,7 +127,7 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
         Assume.assumeTrue("Skip non-existent encrypted file: " + encryptedFile, url != null);
         assertNotNull("Missing test resource: " + encryptedFile, url);
 
-        Collection<KeyPair> keys = parser.loadKeyPairs(url, (r, index) -> PASSWORD);
+        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());
@@ -139,11 +141,14 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
         assertNotNull("Missing test resource: " + encryptedFile, url);
 
         int maxRetries = 3;
+        SessionContext mockSession = Mockito.mock(SessionContext.class);
         for (ResourceDecodeResult result : ResourceDecodeResult.values()) {
             AtomicInteger retriesCount = new AtomicInteger(0);
             FilePasswordProvider provider = new FilePasswordProvider() {
                 @Override
-                public String getPassword(String resourceKey, int retryIndex) throws IOException {
+                public String getPassword(SessionContext session, String resourceKey, int retryIndex) throws IOException {
+                    assertSame("Mismatched session context", mockSession, session);
+
                     switch (result) {
                         case IGNORE:
                         case TERMINATE:
@@ -166,8 +171,9 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
 
                 @Override
                 public ResourceDecodeResult handleDecodeAttemptResult(
-                        String resourceKey, int retryIndex, String password, Exception err)
+                        SessionContext session, String resourceKey, int retryIndex, String password, Exception err)
                             throws IOException, GeneralSecurityException {
+                    assertSame("Mismatched session context", mockSession, session);
                     if (err == null) {
                         return null;
                     }
@@ -188,14 +194,14 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
             };
 
             try {
-                Collection<KeyPair> keys = parser.loadKeyPairs(url, provider);
+                Collection<KeyPair> keys = parser.loadKeyPairs(mockSession, url, provider);
                 if (result == ResourceDecodeResult.IGNORE) {
                     assertEquals("Unexpected loaded keys count from " + encryptedFile, 0, GenericUtils.size(keys));
                     assertEquals("Mismatched " + result + " retries count", 0, retriesCount.get());
                 } else {
                     assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys));
                     assertEquals("Mismatched " + result + " retries count", maxRetries, retriesCount.get());
-                    assertLoadedKeyPair(encryptedFile, keys.iterator().next());
+                    assertLoadedKeyPair(encryptedFile, GenericUtils.head(keys));
                 }
             } catch (IOException | GeneralSecurityException | RuntimeException e) {
                 if (result != ResourceDecodeResult.TERMINATE) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 cbd6f22..478d3cc 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
@@ -369,7 +369,7 @@ public class ApacheSshdSftpSessionFactory
         FilePasswordProvider provider = resolveFilePasswordProvider(session, keyResource, keyPassword);
         Collection<KeyPair> keyPairs;
         try (InputStream inputStream = keyResource.getInputStream()) {
-            keyPairs = PEMResourceParserUtils.PROXY.loadKeyPairs(keyResource.toString(), provider, inputStream);
+            keyPairs = PEMResourceParserUtils.PROXY.loadKeyPairs(session, keyResource.toString(), provider, inputStream);
         }
 
         int numLoaded = GenericUtils.size(keyPairs);


[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

Posted by lg...@apache.org.
[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


[04/12] mina-sshd git commit: [SSHD-862] Provide session context argument when KeyPairProvider keys access methods are invoked

Posted by lg...@apache.org.
[SSHD-862] Provide session context argument when KeyPairProvider keys access methods are invoked


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

Branch: refs/heads/master
Commit: e05898c8095ee082dd4378d910ac0febf9c7dcac
Parents: c649ac5
Author: Lyor Goldstein <lg...@apache.org>
Authored: Tue Nov 13 16:51:25 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  5 ++++
 .../common/keyprovider/KeyPairProvider.java     | 17 ++++++++-----
 .../keyprovider/MappedKeyPairProvider.java      |  7 +++---
 .../common/keyprovider/KeyPairProviderTest.java |  8 +++---
 .../PEMGeneratorHostKeyProviderTest.java        |  4 +--
 .../SimpleGeneratorHostKeyProviderTest.java     |  4 +--
 .../server/global/OpenSshHostKeysHandler.java   |  6 +++--
 .../server/session/AbstractServerSession.java   |  4 +--
 .../java/org/apache/sshd/agent/AgentTest.java   |  7 ++++--
 .../java/org/apache/sshd/client/ClientTest.java |  6 ++---
 .../sshd/common/auth/AuthenticationTest.java    | 26 ++++++++++++--------
 .../common/auth/SinglePublicKeyAuthTest.java    |  5 ++--
 12 files changed, 61 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 8af2a71..6751736 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -40,6 +40,8 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 * `SftpFileSystemProvider` and its associated helper classes have been moved to
 `org.apache.sshd.client.subsystem.sftp.fs` package
 
+* `KeyPairProvider` accepts a `SessionContext` argument in its `getKeyTypes/loadKey` methods
+
 ## Behavioral changes and enhancements
 
 * [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all
@@ -65,3 +67,6 @@ identities both from agent and client session
 * [SSHD-861](https://issues.apache.org/jira/browse/SSHD-861) - Fixed username/password encoding for `SftpFileSystem` URI(s)
 
     * Also added `SftpFileSystemClientSessionInitializer` support in `SftpFileSystemProvider`
+
+* [SSHD-862](https://issues.apache.org/jira/browse/SSHD-862) - Provide session context argument when
+key loading methods are invoked

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
index 3ef9b0f..68c4daf 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
 
 import org.apache.sshd.common.cipher.ECCurves;
 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;
 
@@ -75,12 +76,12 @@ public interface KeyPairProvider extends KeyIdentityProvider {
     KeyPairProvider EMPTY_KEYPAIR_PROVIDER =
         new KeyPairProvider() {
             @Override
-            public KeyPair loadKey(String type) {
+            public KeyPair loadKey(SessionContext session, String type) {
                 return null;
             }
 
             @Override
-            public Iterable<String> getKeyTypes() {
+            public Iterable<String> getKeyTypes(SessionContext session) {
                 return Collections.emptyList();
             }
 
@@ -100,10 +101,12 @@ public interface KeyPairProvider extends KeyIdentityProvider {
      * or &quot;ecdsa-sha2-nistp{256,384,521}&quot;. If there is no key of this type, return
      * {@code null}
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @param type the type of key to load
      * @return a valid key pair or {@code null} if this type of key is not available
      */
-    default KeyPair loadKey(String type) {
+    default KeyPair loadKey(SessionContext session, String type) {
         ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
         return GenericUtils.stream(loadKeys())
                 .filter(key -> type.equals(KeyUtils.getKeyType(key)))
@@ -112,9 +115,11 @@ public interface KeyPairProvider extends KeyIdentityProvider {
     }
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @return The available {@link Iterable} key types in preferred order - never {@code null}
      */
-    default Iterable<String> getKeyTypes() {
+    default Iterable<String> getKeyTypes(SessionContext session) {
         return GenericUtils.stream(loadKeys())
                 .map(KeyUtils::getKeyType)
                 .filter(GenericUtils::isNotEmpty)
@@ -148,7 +153,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
             }
 
             @Override
-            public KeyPair loadKey(String type) {
+            public KeyPair loadKey(SessionContext session, String type) {
                 for (KeyPair kp : pairs) {
                     String t = KeyUtils.getKeyType(kp);
                     if (Objects.equals(type, t)) {
@@ -160,7 +165,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
             }
 
             @Override
-            public Iterable<String> getKeyTypes() {
+            public Iterable<String> getKeyTypes(SessionContext session) {
                 // use a LinkedHashSet so as to preserve the order but avoid duplicates
                 Collection<String> types = new LinkedHashSet<>();
                 for (KeyPair kp : pairs) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
index bf3ec8b..d8f2350 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
@@ -28,6 +28,7 @@ import java.util.TreeMap;
 import java.util.function.Function;
 
 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;
 
@@ -66,18 +67,18 @@ public class MappedKeyPairProvider implements KeyPairProvider {
     }
 
     @Override
-    public KeyPair loadKey(String type) {
+    public KeyPair loadKey(SessionContext session, String type) {
         return pairsMap.get(type);
     }
 
     @Override
-    public Iterable<String> getKeyTypes() {
+    public Iterable<String> getKeyTypes(SessionContext session) {
         return pairsMap.keySet();
     }
 
     @Override
     public String toString() {
-        return String.valueOf(getKeyTypes());
+        return String.valueOf(pairsMap.keySet());
     }
 
     public static Map<String, KeyPair> mapUniquePairs(Collection<? extends KeyPair> pairs) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
index 021796b..afa55cd 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
@@ -49,10 +49,10 @@ public class KeyPairProviderTest extends JUnitTestSupport {
     public void testEmptyKeyProvider() {
         KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
         assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys()));
-        assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes()));
+        assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes(null)));
 
         for (String keyType : new String[]{null, "", getCurrentTestName()}) {
-            assertNull("Unexpected key-pair loaded for type='" + keyType + "'", provider.loadKey(keyType));
+            assertNull("Unexpected key-pair loaded for type='" + keyType + "'", provider.loadKey(null, keyType));
         }
     }
 
@@ -68,11 +68,11 @@ public class KeyPairProviderTest extends JUnitTestSupport {
             String.CASE_INSENSITIVE_ORDER);
 
         KeyPairProvider provider = MappedKeyPairProvider.MAP_TO_KEY_PAIR_PROVIDER.apply(pairsMap);
-        assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes());
+        assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes(null));
         assertEquals("Key pairs", pairsMap.values(), provider.loadKeys());
 
         pairsMap.forEach((keyType, expected) -> {
-            KeyPair actual = provider.loadKey(keyType);
+            KeyPair actual = provider.loadKey(null, keyType);
             assertSame(keyType, expected, actual);
         });
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
index 3be5b6d..af4a6b7 100644
--- a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
@@ -118,11 +118,11 @@ public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
-        Iterable<String> types = provider.getKeyTypes();
+        Iterable<String> types = provider.getKeyTypes(null);
         KeyPair kp = null;
         for (String type : types) {
             if (keyType.equals(type)) {
-                kp = provider.loadKey(keyType);
+                kp = provider.loadKey(null, keyType);
                 assertNotNull("Failed to load key for " + keyType, kp);
                 break;
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
index dc5e9fd..ab0ff2c 100644
--- a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
@@ -110,11 +110,11 @@ public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
-        Iterable<String> types = provider.getKeyTypes();
+        Iterable<String> types = provider.getKeyTypes(null);
         KeyPair kp = null;
         for (String type : types) {
             if (keyType.equals(type)) {
-                kp = provider.loadKey(keyType);
+                kp = provider.loadKey(null, keyType);
                 assertNotNull("Failed to load key for " + keyType, kp);
                 break;
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-core/src/main/java/org/apache/sshd/server/global/OpenSshHostKeysHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/OpenSshHostKeysHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/OpenSshHostKeysHandler.java
index 0a806aa..35a7d68 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/OpenSshHostKeysHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/OpenSshHostKeysHandler.java
@@ -84,7 +84,9 @@ public class OpenSshHostKeysHandler extends AbstractOpenSshHostKeysHandler imple
     }
 
     @Override
-    protected Result handleHostKeys(Session session, Collection<? extends PublicKey> keys, boolean wantReply, Buffer buffer) throws Exception {
+    protected Result handleHostKeys(
+            Session session, Collection<? extends PublicKey> keys, boolean wantReply, Buffer buffer)
+                throws Exception {
         // according to the specification there MUST be reply required by the server
         ValidateUtils.checkTrue(wantReply, "No reply required for host keys of %s", session);
         Collection<? extends NamedFactory<Signature>> factories =
@@ -112,7 +114,7 @@ public class OpenSshHostKeysHandler extends AbstractOpenSshHostKeysHandler imple
 
             KeyPair kp;
             try {
-                kp = ValidateUtils.checkNotNull(kpp.loadKey(keyType), "No key of type=%s available", keyType);
+                kp = ValidateUtils.checkNotNull(kpp.loadKey(session, keyType), "No key of type=%s available", keyType);
             } catch (Error e) {
                 log.warn("handleHostKeys({}) failed ({}) to load key of type={}: {}",
                          session, e.getClass().getSimpleName(), keyType, e.getMessage());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 96053f1..69f450a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -258,7 +258,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
         boolean debugEnabled = log.isDebugEnabled();
         Iterable<String> provided;
         try {
-            provided = (kpp == null) ? null : kpp.getKeyTypes();
+            provided = (kpp == null) ? null : kpp.getKeyTypes(this);
         } catch (Error e) {
             log.warn("resolveAvailableSignaturesProposal({}) failed ({}) to get key types: {}",
                  this, e.getClass().getSimpleName(), e.getMessage());
@@ -394,7 +394,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
         String keyType = getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
         KeyPairProvider provider = Objects.requireNonNull(getKeyPairProvider(), "No host keys provider");
         try {
-            return provider.loadKey(keyType);
+            return provider.loadKey(this, keyType);
         } catch (Error e) {
             log.warn("getHostKey({}) failed ({}) to load key of type={}: {}",
                  this, e.getClass().getSimpleName(), keyType, e.getMessage());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
index 8f3e4eb..3ce0f5b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
@@ -36,6 +36,7 @@ import org.apache.sshd.agent.unix.AprLibrary;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.Environment;
@@ -87,7 +88,8 @@ public class AgentTest extends BaseTestSupport {
                 assertObjectInstanceOf("Non collection initial identities", Collection.class, keys);
                 assertEquals("Unexpected initial identities size", 0, ((Collection<?>) keys).size());
 
-                KeyPair k = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+                KeyPairProvider provider = createTestHostKeyProvider();
+                KeyPair k = provider.loadKey(null, KeyPairProvider.SSH_RSA);
                 client.addIdentity(k, "");
                 keys = client.getIdentities();
                 assertNotNull("No registered identities after add", keys);
@@ -112,7 +114,8 @@ public class AgentTest extends BaseTestSupport {
         ProxyAgentFactory agentFactory = new ProxyAgentFactory();
         LocalAgentFactory localAgentFactory = new LocalAgentFactory();
         String username = getCurrentTestName();
-        KeyPair pair = CommonTestSupportUtils.createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
+        FileKeyPairProvider provider = CommonTestSupportUtils.createTestKeyPairProvider("dsaprivkey.pem");
+        KeyPair pair = provider.loadKey(null, KeyPairProvider.SSH_DSS);
         localAgentFactory.getAgent().addIdentity(pair, username);
 
         try (SshServer sshd1 = setupTestServer()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
index 1fa090a..11ef70e 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
@@ -1041,7 +1041,7 @@ public class ClientTest extends BaseTestSupport {
                 .getSession()) {
             assertNotNull("Client session creation not signalled", clientSessionHolder.get());
             KeyPairProvider keys = createTestHostKeyProvider();
-            session.addPublicKeyIdentity(keys.loadKey(KeyPairProvider.SSH_RSA));
+            session.addPublicKeyIdentity(keys.loadKey(session, KeyPairProvider.SSH_RSA));
             session.auth().verify(5L, TimeUnit.SECONDS);
         } finally {
             client.stop();
@@ -1055,7 +1055,7 @@ public class ClientTest extends BaseTestSupport {
         provider.setAlgorithm(KeyUtils.RSA_ALGORITHM);
 
         KeyPairProvider keys = createTestHostKeyProvider();
-        KeyPair pair = keys.loadKey(KeyPairProvider.SSH_RSA);
+        KeyPair pair = keys.loadKey(null, KeyPairProvider.SSH_RSA);
         sshd.setPublickeyAuthenticator((username, key, session) -> key.equals(pair.getPublic()));
         client.setUserAuthFactories(Collections.singletonList(UserAuthPublicKeyFactory.INSTANCE));
         client.start();
@@ -1064,7 +1064,7 @@ public class ClientTest extends BaseTestSupport {
                 .verify(7L, TimeUnit.SECONDS)
                 .getSession()) {
             assertNotNull("Client session creation not signalled", clientSessionHolder.get());
-            session.addPublicKeyIdentity(provider.loadKey(KeyPairProvider.SSH_RSA));
+            session.addPublicKeyIdentity(provider.loadKey(session, KeyPairProvider.SSH_RSA));
             session.addPublicKeyIdentity(pair);
             session.auth().verify(5L, TimeUnit.SECONDS);
         } finally {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/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 d7df493..5cb409c 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
@@ -276,13 +276,16 @@ public class AuthenticationTest extends BaseTestSupport {
 
             client.start();
 
-            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port)
+                        .verify(7L, TimeUnit.SECONDS)
+                        .getSession()) {
                 Collection<ClientSession.ClientSessionEvent> result =
                         s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
                         TimeUnit.SECONDS.toMillis(11L));
                 assertFalse("Timeout while waiting for session", result.contains(ClientSession.ClientSessionEvent.TIMEOUT));
 
-                KeyPair pair = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+                KeyPairProvider provider = createTestHostKeyProvider();
+                KeyPair pair = provider.loadKey(s, KeyPairProvider.SSH_RSA);
                 try {
                     assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
                 } finally {
@@ -344,13 +347,16 @@ public class AuthenticationTest extends BaseTestSupport {
             });
             client.start();
 
-            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+            try (ClientSession s = client.connect(null, TEST_LOCALHOST, port)
+                    .verify(7L, TimeUnit.SECONDS)
+                    .getSession()) {
                 Collection<ClientSession.ClientSessionEvent> result =
-                        s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
-                        TimeUnit.SECONDS.toMillis(11L));
+                    s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH),
+                    TimeUnit.SECONDS.toMillis(11L));
                 assertFalse("Timeout while waiting for session", result.contains(ClientSession.ClientSessionEvent.TIMEOUT));
 
-                KeyPair pair = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+                KeyPairProvider provider = createTestHostKeyProvider();
+                KeyPair pair = provider.loadKey(s, KeyPairProvider.SSH_RSA);
                 try {
                     assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
                 } finally {
@@ -587,9 +593,9 @@ public class AuthenticationTest extends BaseTestSupport {
     @Test   // see SSHD-618
     public void testPublicKeyAuthDifferentThanKex() throws Exception {
         KeyPairProvider serverKeys = KeyPairProvider.wrap(
-                    CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024),
-                    CommonTestSupportUtils.generateKeyPair(KeyUtils.DSS_ALGORITHM, 512),
-                    CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256));
+            CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024),
+            CommonTestSupportUtils.generateKeyPair(KeyUtils.DSS_ALGORITHM, 512),
+            CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256));
         sshd.setKeyPairProvider(serverKeys);
         sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
         sshd.setPasswordAuthenticator(RejectAllPasswordAuthenticator.INSTANCE);
@@ -612,7 +618,7 @@ public class AuthenticationTest extends BaseTestSupport {
                 String expType = kexSignature.getName();
                 assertEquals("Mismatched server key type", expType, keyType);
 
-                KeyPair kp = ValidateUtils.checkNotNull(serverKeys.loadKey(keyType), "No server key for type=%s", keyType);
+                KeyPair kp = ValidateUtils.checkNotNull(serverKeys.loadKey(null, keyType), "No server key for type=%s", keyType);
                 assertKeyEquals("Mismatched server public keys", kp.getPublic(), serverKey);
                 return true;
             });

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e05898c8/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
index 4e97267..3685715 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/SinglePublicKeyAuthTest.java
@@ -60,8 +60,9 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
     public SinglePublicKeyAuthTest() {
         SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
         provider.setAlgorithm(KeyUtils.RSA_ALGORITHM);
-        pairRsaBad = provider.loadKey(KeyPairProvider.SSH_RSA);
-        pairRsaGood = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+        pairRsaBad = provider.loadKey(null, KeyPairProvider.SSH_RSA);
+        KeyPairProvider badKeys = createTestHostKeyProvider();
+        pairRsaGood = badKeys.loadKey(null, KeyPairProvider.SSH_RSA);
     }
 
     @Before


[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

Posted by lg...@apache.org.
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();
+    }
+}


[08/12] mina-sshd git commit: [SSHD-862] Provide SessionContext argument when loading client keys identities

Posted by lg...@apache.org.
[SSHD-862] Provide SessionContext argument when loading client keys identities


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

Branch: refs/heads/master
Commit: 210f638e34ced6ce6322dc788dd01d8818b1da3d
Parents: e05898c
Author: Lyor Goldstein <lg...@apache.org>
Authored: Tue Nov 13 17:21:42 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  4 ++
 .../sshd/cli/server/SshServerCliSupport.java    |  4 +-
 .../auth/AuthenticationIdentitiesProvider.java  |  3 +-
 .../keys/BuiltinClientIdentitiesWatcher.java    | 11 +++--
 .../config/keys/ClientIdentitiesWatcher.java    | 13 +++---
 .../config/keys/ClientIdentityFileWatcher.java  |  7 ++--
 .../config/keys/ClientIdentityProvider.java     |  8 +++-
 .../sshd/common/config/keys/KeyRandomArt.java   | 22 +++++++---
 .../AbstractResourceKeyPairProvider.java        | 31 ++++++++------
 .../ClassLoadableResourceKeyPairProvider.java   |  5 ++-
 .../common/keyprovider/FileKeyPairProvider.java |  9 ++--
 .../common/keyprovider/KeyIdentityProvider.java | 44 +++++++++++---------
 .../common/keyprovider/KeyPairProvider.java     |  8 ++--
 .../keyprovider/MappedKeyPairProvider.java      |  2 +-
 .../keyprovider/MultiKeyIdentityIterator.java   | 13 +++++-
 .../keyprovider/MultiKeyIdentityProvider.java   |  6 ++-
 .../AbstractGeneratorHostKeyProvider.java       | 30 +++++++------
 .../SimpleGeneratorHostKeyProvider.java         |  8 +++-
 .../BuiltinClientIdentitiesWatcherTest.java     |  2 +-
 .../keys/ClientIdentityFileWatcherTest.java     |  7 ++--
 .../client/config/keys/ClientIdentityTest.java  |  2 +-
 .../common/config/keys/KeyRandomArtTest.java    |  2 +-
 .../common/keyprovider/KeyPairProviderTest.java |  4 +-
 .../MultiKeyIdentityProviderTest.java           |  5 ++-
 .../common/util/security/SecurityUtilsTest.java |  2 +-
 .../AbstractGeneratorHostKeyProviderTest.java   | 14 ++++---
 .../sshd/util/test/CommonTestSupportUtils.java  |  4 +-
 .../keys/LazyClientIdentityIteratorTest.java    |  7 ++--
 .../auth/pubkey/UserAuthPublicKeyIterator.java  |  2 +-
 .../sshd/client/session/ClientSession.java      |  4 +-
 .../server/session/ServerUserAuthService.java   |  2 +-
 .../hosts/HostConfigEntryResolverTest.java      |  3 +-
 .../signature/SignatureFactoriesTest.java       | 11 ++---
 .../sshd/server/auth/WelcomeBannerTest.java     |  5 ++-
 .../server/config/keys/ServerIdentityTest.java  |  2 +-
 35 files changed, 186 insertions(+), 120 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 6751736..a07e953 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -42,6 +42,10 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 
 * `KeyPairProvider` accepts a `SessionContext` argument in its `getKeyTypes/loadKey` methods
 
+* `KeyIdentityProvider` accepts a `SessionContext` argument in its `loadKeys` method
+
+* `ClientIdentityProvider` accepts a `SessionContext` argument in its `getClientIdentity` method
+
 ## Behavioral changes and enhancements
 
 * [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 6cdffc8..eac5b7e 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
@@ -92,8 +92,8 @@ public abstract class SshServerCliSupport extends CliSupport {
                 hostKeyProvider.setKeySize(hostKeySize);
             }
 
-            List<KeyPair> keys = ValidateUtils.checkNotNullAndNotEmpty(hostKeyProvider.loadKeys(),
-                    "Failed to load keys from %s", hostKeyFile);
+            List<KeyPair> keys = ValidateUtils.checkNotNullAndNotEmpty(
+                hostKeyProvider.loadKeys(null), "Failed to load keys from %s", hostKeyFile);
             KeyPair kp = keys.get(0);
             PublicKey pubKey = kp.getPublic();
             String keyAlgorithm = pubKey.getAlgorithm();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
index ee7c856..6ac1a93 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -26,6 +26,7 @@ import java.util.List;
 import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.helper.LazyMatchingTypeIterable;
 
 /**
@@ -82,7 +83,7 @@ public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, P
     static AuthenticationIdentitiesProvider wrapIdentities(Iterable<?> identities) {
         return new AuthenticationIdentitiesProvider() {
             @Override
-            public Iterable<KeyPair> loadKeys() {
+            public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return LazyMatchingTypeIterable.lazySelectMatchingTypes(identities, KeyPair.class);
             }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
index ab9929b..bb3f738 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
@@ -32,6 +32,7 @@ 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;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 
 /**
@@ -69,18 +70,20 @@ public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
     }
 
     @Override
-    public Iterable<KeyPair> loadKeys() {
-        return isSupportedOnly() ? loadKeys(this::isSupported) : super.loadKeys();
+    public Iterable<KeyPair> loadKeys(SessionContext session) {
+        return isSupportedOnly()
+            ? loadKeys(session, p -> isSupported(session, p))
+            : super.loadKeys(session);
     }
 
-    private boolean isSupported(KeyPair kp) {
+    protected boolean isSupported(SessionContext session, KeyPair kp) {
         BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
         if ((id != null) && id.isSupported()) {
             return true;
         }
         if (log.isDebugEnabled()) {
             log.debug("loadKeys - remove unsupported identity={}, key-type={}, key={}",
-                    id, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+                id, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
         }
         return false;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
index 05660f7..eed14e9 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
@@ -31,6 +31,7 @@ import java.util.function.Supplier;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 
 /**
@@ -69,17 +70,17 @@ public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements
     }
 
     @Override
-    public Iterable<KeyPair> loadKeys() {
-        return loadKeys(null);
+    public Iterable<KeyPair> loadKeys(SessionContext session) {
+        return loadKeys(session, null);
     }
 
-    protected Iterable<KeyPair> loadKeys(Predicate<? super KeyPair> filter) {
-        return ClientIdentityProvider.lazyKeysLoader(providers, this::doGetKeyPair, filter);
+    protected Iterable<KeyPair> loadKeys(SessionContext session, Predicate<? super KeyPair> filter) {
+        return ClientIdentityProvider.lazyKeysLoader(providers, p -> doGetKeyPair(session, p), filter);
     }
 
-    protected KeyPair doGetKeyPair(ClientIdentityProvider p) {
+    protected KeyPair doGetKeyPair(SessionContext session, ClientIdentityProvider p) {
         try {
-            KeyPair kp = p.getClientIdentity();
+            KeyPair kp = p.getClientIdentity(session);
             if (kp == null) {
                 if (log.isDebugEnabled()) {
                     log.debug("loadKeys({}) no key loaded", p);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 dc2128f..411ae1b 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
@@ -31,6 +31,7 @@ import java.util.function.Supplier;
 
 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.io.IoUtils;
 import org.apache.sshd.common.util.io.ModifiableFileWatcher;
@@ -84,7 +85,7 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
     }
 
     @Override
-    public KeyPair getClientIdentity() throws IOException, GeneralSecurityException {
+    public KeyPair getClientIdentity(SessionContext session) throws IOException, GeneralSecurityException {
         if (!checkReloadRequired()) {
             return identityHolder.get();
         }
@@ -95,7 +96,7 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
             return identityHolder.get();
         }
 
-        KeyPair id = reloadClientIdentity(path);
+        KeyPair id = reloadClientIdentity(session, path);
         if (!KeyUtils.compareKeyPairs(kp, id)) {
             if (log.isDebugEnabled()) {
                 log.debug("getClientIdentity({}) identity {}", path, (kp == null) ? "loaded" : "re-loaded");
@@ -107,7 +108,7 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
         return identityHolder.get();
     }
 
-    protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
+    protected KeyPair reloadClientIdentity(SessionContext session, Path path) throws IOException, GeneralSecurityException {
         if (isStrict()) {
             Map.Entry<String, Object> violation =
                 KeyUtils.validateStrictKeyFilePermissions(path, IoUtils.EMPTY_LINK_OPTIONS);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
index 6bd4c07..8c52d4f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
@@ -28,6 +28,8 @@ import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
+import org.apache.sshd.common.session.SessionContext;
+
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
@@ -36,6 +38,8 @@ public interface ClientIdentityProvider {
     /**
      * Provides a {@link KeyPair} representing the client identity
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @return The client identity - may be {@code null} if no currently
      * available identity from this provider. <B>Note:</B> the provider
      * may return a <U>different</U> value every time this method is called
@@ -43,7 +47,7 @@ public interface ClientIdentityProvider {
      * @throws IOException If failed to load the identity
      * @throws GeneralSecurityException If failed to parse the identity
      */
-    KeyPair getClientIdentity() throws IOException, GeneralSecurityException;
+    KeyPair getClientIdentity(SessionContext session) throws IOException, GeneralSecurityException;
 
     /**
      * Wraps a {@link KeyPair} into a {@link ClientIdentityProvider} that
@@ -53,7 +57,7 @@ public interface ClientIdentityProvider {
      * @return The wrapping provider
      */
     static ClientIdentityProvider of(KeyPair kp) {
-        return () -> kp;
+        return session -> kp;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
index b59dbd0..cf0b1c9 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
@@ -34,6 +34,7 @@ import java.util.Objects;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -197,6 +198,8 @@ public class KeyRandomArt {
     /**
      * Creates the combined representation of the random art entries for the provided keys
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param separator The separator to use between the arts - if empty char
      * ('\0') then no separation is done
      * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
@@ -205,14 +208,18 @@ public class KeyRandomArt {
      * @throws Exception If failed to extract or combine the entries
      * @see #combine(Appendable, char, KeyIdentityProvider)
      */
-    public static String combine(char separator, KeyIdentityProvider provider) throws Exception {
-        return combine(new StringBuilder(4 * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, provider).toString();
+    public static String combine(
+            SessionContext session, char separator, KeyIdentityProvider provider)
+                throws Exception {
+        return combine(session, new StringBuilder(4 * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, provider).toString();
     }
 
     /**
      * Appends the combined random art entries for the provided keys
      *
      * @param <A> The {@link Appendable} output writer
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param sb The writer
      * @param separator The separator to use between the arts - if empty char
      * ('\0') then no separation is done
@@ -223,21 +230,24 @@ public class KeyRandomArt {
      * @see #generate(KeyIdentityProvider)
      * @see #combine(Appendable, char, Collection)
      */
-    public static <A extends Appendable> A combine(A sb, char separator, KeyIdentityProvider provider) throws Exception {
-        return combine(sb, separator, generate(provider));
+    public static <A extends Appendable> A combine(
+            SessionContext session, A sb, char separator, KeyIdentityProvider provider) throws Exception {
+        return combine(sb, separator, generate(session, provider));
     }
 
     /**
      * Extracts and generates random art entries for all key in the provider
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
      * or has no keys to provide
      * @return The extracted {@link KeyRandomArt}s
      * @throws Exception If failed to extract the entries
      * @see KeyIdentityProvider#loadKeys()
      */
-    public static Collection<KeyRandomArt> generate(KeyIdentityProvider provider) throws Exception {
-        Iterable<KeyPair> keys = (provider == null) ? null : provider.loadKeys();
+    public static Collection<KeyRandomArt> generate(SessionContext session, KeyIdentityProvider provider) throws Exception {
+        Iterable<KeyPair> keys = (provider == null) ? null : provider.loadKeys(session);
         Iterator<KeyPair> iter = (keys == null) ? null : keys.iterator();
         if ((iter == null) || (!iter.hasNext())) {
             return Collections.emptyList();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 e4e941d..194daf5 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
@@ -35,6 +35,7 @@ import java.util.TreeSet;
 
 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.security.SecurityUtils;
@@ -109,15 +110,16 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
         }
     }
 
-    protected Iterable<KeyPair> loadKeys(final Collection<? extends R> resources) {
+    protected Iterable<KeyPair> loadKeys(SessionContext session, Collection<? extends R> resources) {
         if (GenericUtils.isEmpty(resources)) {
             return Collections.emptyList();
         } else {
-            return () -> new KeyPairIterator(resources);
+            return () -> new KeyPairIterator(session, resources);
         }
     }
 
-    protected KeyPair doLoadKey(R resource) throws IOException, GeneralSecurityException {
+    protected KeyPair doLoadKey(SessionContext session, R resource)
+            throws IOException, GeneralSecurityException {
         String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(resource, null), "No resource string value");
         KeyPair kp;
         synchronized (cacheMap) {
@@ -129,12 +131,12 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
             if (log.isTraceEnabled()) {
                 PublicKey key = kp.getPublic();
                 log.trace("doLoadKey({}) use cached key {}-{}",
-                          resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                      resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
             }
             return kp;
         }
 
-        kp = doLoadKey(resourceKey, resource, getPasswordFinder());
+        kp = doLoadKey(session, resourceKey, resource, getPasswordFinder());
         if (kp != null) {
             boolean reusedKey;
             synchronized (cacheMap) {
@@ -150,8 +152,8 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
             if (log.isDebugEnabled()) {
                 PublicKey key = kp.getPublic();
                 log.debug("doLoadKey({}) {} {}-{}",
-                          resourceKey, reusedKey ? "re-loaded" : "loaded",
-                          KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                      resourceKey, reusedKey ? "re-loaded" : "loaded",
+                      KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
             }
         } else {
             if (log.isDebugEnabled()) {
@@ -162,26 +164,29 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
         return kp;
     }
 
-    protected KeyPair doLoadKey(String resourceKey, R resource, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+    protected KeyPair doLoadKey(SessionContext session, String resourceKey, R resource, FilePasswordProvider provider)
+            throws IOException, GeneralSecurityException {
         try (InputStream inputStream = openKeyPairResource(resourceKey, resource)) {
-            return doLoadKey(resourceKey, inputStream, provider);
+            return doLoadKey(session, resourceKey, inputStream, provider);
         }
     }
 
     protected abstract InputStream openKeyPairResource(String resourceKey, R resource) throws IOException;
 
-    protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+    protected KeyPair doLoadKey(SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
             throws IOException, GeneralSecurityException {
         return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, provider);
     }
 
     protected class KeyPairIterator implements Iterator<KeyPair> {
+        protected final SessionContext session;
         private final Iterator<? extends R> iterator;
         private KeyPair nextKeyPair;
         private boolean nextKeyPairSet;
 
-        protected KeyPairIterator(Collection<? extends R> resources) {
-            iterator = resources.iterator();
+        protected KeyPairIterator(SessionContext session, Collection<? extends R> resources) {
+            this.session = session;
+            this.iterator = resources.iterator();
         }
 
         @Override
@@ -211,7 +216,7 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
             while (iterator.hasNext()) {
                 R r = iterator.next();
                 try {
-                    nextKeyPair = doLoadKey(r);
+                    nextKeyPair = doLoadKey(session, r);
                 } catch (Throwable e) {
                     log.warn("Failed (" + e.getClass().getSimpleName() + ")"
                            + " to load key resource=" + r + ": " + e.getMessage());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 6e8da54..3132abd 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
@@ -26,6 +26,7 @@ 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.threads.ThreadUtils;
 
@@ -84,8 +85,8 @@ public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPai
     }
 
     @Override
-    public Iterable<KeyPair> loadKeys() {
-        return loadKeys(getResources());
+    public Iterable<KeyPair> loadKeys(SessionContext session) {
+        return loadKeys(session, getResources());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 fa52eb2..a416871 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
@@ -29,6 +29,7 @@ import java.util.Collection;
 import java.util.Collections;
 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;
 
@@ -71,13 +72,13 @@ public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
     }
 
     @Override
-    public Iterable<KeyPair> loadKeys() {
-        return loadKeys(getPaths());
+    public Iterable<KeyPair> loadKeys(SessionContext session) {
+        return loadKeys(session, getPaths());
     }
 
     @Override
-    protected KeyPair doLoadKey(Path resource) throws IOException, GeneralSecurityException {
-        return super.doLoadKey((resource == null) ? null : resource.toAbsolutePath());
+    protected KeyPair doLoadKey(SessionContext session, Path resource) throws IOException, GeneralSecurityException {
+        return super.doLoadKey(session, (resource == null) ? null : resource.toAbsolutePath());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
index f03b68b..46b6f52 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
@@ -23,8 +23,8 @@ import java.security.KeyPair;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.function.Function;
 
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 
 /**
@@ -38,7 +38,7 @@ public interface KeyIdentityProvider {
      */
     KeyIdentityProvider EMPTY_KEYS_PROVIDER = new KeyIdentityProvider() {
         @Override
-        public Iterable<KeyPair> loadKeys() {
+        public Iterable<KeyPair> loadKeys(SessionContext session) {
             return Collections.emptyList();
         }
 
@@ -49,40 +49,40 @@ public interface KeyIdentityProvider {
     };
 
     /**
-     * Invokes {@link KeyIdentityProvider#loadKeys()} and returns the result - ignores
-     * {@code null} providers (i.e., returns an empty iterable instance)
-     */
-    Function<KeyIdentityProvider, Iterable<KeyPair>> LOADER = p ->
-        (p == null) ? Collections.<KeyPair>emptyList() : p.loadKeys();
-
-    /**
      * Load available keys.
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @return an {@link Iterable} instance of available keys - ignored if {@code null}
      */
-    Iterable<KeyPair> loadKeys();
+    Iterable<KeyPair> loadKeys(SessionContext session);
 
     /**
      * Creates a &quot;unified&quot; {@link Iterator} of {@link KeyPair}s out of 2 possible
      * {@link KeyIdentityProvider}
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param identities The registered keys identities
      * @param keys Extra available key pairs
      * @return The wrapping iterator
      * @see #resolveKeyIdentityProvider(KeyIdentityProvider, KeyIdentityProvider)
      */
-    static Iterator<KeyPair> iteratorOf(KeyIdentityProvider identities, KeyIdentityProvider keys) {
-        return iteratorOf(resolveKeyIdentityProvider(identities, keys));
+    static Iterator<KeyPair> iteratorOf(
+            SessionContext session, KeyIdentityProvider identities, KeyIdentityProvider keys) {
+        return iteratorOf(session, resolveKeyIdentityProvider(identities, keys));
     }
 
     /**
      * Resolves a non-{@code null} iterator of the available keys
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
      * @return A non-{@code null} iterator - which may be empty if no provider or no keys
      */
-    static Iterator<KeyPair> iteratorOf(KeyIdentityProvider provider) {
-        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys());
+    static Iterator<KeyPair> iteratorOf(SessionContext session, KeyIdentityProvider provider) {
+        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys(session));
     }
 
     /**
@@ -94,12 +94,14 @@ public interface KeyIdentityProvider {
      *      <LI>If both are the same instance then use it.</U>
      *      <LI>Otherwise, returns a wrapper that groups both providers.</LI>
      * </UL>
+     *
      * @param identities The registered key pair identities
      * @param keys The extra available key pairs
      * @return The resolved provider
      * @see #multiProvider(KeyIdentityProvider...)
      */
-    static KeyIdentityProvider resolveKeyIdentityProvider(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+    static KeyIdentityProvider resolveKeyIdentityProvider(
+            KeyIdentityProvider identities, KeyIdentityProvider keys) {
         if ((keys == null) || (identities == keys)) {
             return identities;
         } else if (identities == null) {
@@ -124,6 +126,8 @@ public interface KeyIdentityProvider {
     /**
      * Wraps a group of {@link KeyIdentityProvider} into a single one
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param providers The providers - ignored if {@code null}/empty (i.e., returns
      * {@link #EMPTY_KEYS_PROVIDER})
      * @return The wrapping provider
@@ -143,22 +147,24 @@ public interface KeyIdentityProvider {
     /**
      * Wraps a group of {@link KeyIdentityProvider} into an {@link Iterable} of {@link KeyPair}s
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param providers The group of providers - ignored if {@code null}/empty (i.e., returns an
      * empty iterable instance)
      * @return The wrapping iterable
      */
-    static Iterable<KeyPair> iterableOf(Collection<? extends KeyIdentityProvider> providers) {
+    static Iterable<KeyPair> iterableOf(SessionContext session, Collection<? extends KeyIdentityProvider> providers) {
         int numProviders = GenericUtils.size(providers);
         if (numProviders <= 0) {
             return Collections.emptyList();
         } else if (numProviders == 1) {
             KeyIdentityProvider p = GenericUtils.head(providers);
-            return p.loadKeys();
+            return p.loadKeys(session);
         } else {
             return new Iterable<KeyPair>() {
                 @Override
                 public Iterator<KeyPair> iterator() {
-                    return new MultiKeyIdentityIterator(providers);
+                    return new MultiKeyIdentityIterator(session, providers);
                 }
 
                 @Override
@@ -188,6 +194,6 @@ public interface KeyIdentityProvider {
      * @return The provider wrapper
      */
     static KeyIdentityProvider wrapKeyPairs(Iterable<KeyPair> pairs) {
-        return (pairs == null) ? EMPTY_KEYS_PROVIDER : () -> pairs;
+        return (pairs == null) ? EMPTY_KEYS_PROVIDER : session -> pairs;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
index 68c4daf..6b2224f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -86,7 +86,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
             }
 
             @Override
-            public Iterable<KeyPair> loadKeys() {
+            public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return Collections.emptyList();
             }
 
@@ -108,7 +108,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
      */
     default KeyPair loadKey(SessionContext session, String type) {
         ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
-        return GenericUtils.stream(loadKeys())
+        return GenericUtils.stream(loadKeys(session))
                 .filter(key -> type.equals(KeyUtils.getKeyType(key)))
                 .findFirst()
                 .orElse(null);
@@ -120,7 +120,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
      * @return The available {@link Iterable} key types in preferred order - never {@code null}
      */
     default Iterable<String> getKeyTypes(SessionContext session) {
-        return GenericUtils.stream(loadKeys())
+        return GenericUtils.stream(loadKeys(session))
                 .map(KeyUtils::getKeyType)
                 .filter(GenericUtils::isNotEmpty)
                 .collect(Collectors.toSet());
@@ -148,7 +148,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
     static KeyPairProvider wrap(Iterable<KeyPair> pairs) {
         return (pairs == null) ? EMPTY_KEYPAIR_PROVIDER : new KeyPairProvider() {
             @Override
-            public Iterable<KeyPair> loadKeys() {
+            public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return pairs;
             }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
index d8f2350..ca4fd5e 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
@@ -62,7 +62,7 @@ public class MappedKeyPairProvider implements KeyPairProvider {
     }
 
     @Override
-    public Iterable<KeyPair> loadKeys() {
+    public Iterable<KeyPair> loadKeys(SessionContext session) {
         return pairsMap.values();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
index 5ba5386..9de7c17 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
@@ -23,6 +23,8 @@ import java.security.KeyPair;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
+import org.apache.sshd.common.session.SessionContext;
+
 /**
  * Iterates over several {@link KeyIdentityProvider}-s exhausting their
  * keys one by one (lazily).
@@ -32,16 +34,22 @@ import java.util.NoSuchElementException;
 public class MultiKeyIdentityIterator implements Iterator<KeyPair> {
     protected Iterator<KeyPair> currentProvider;
     protected boolean finished;
+    private final SessionContext sessionContext;
     private final Iterator<? extends KeyIdentityProvider> providers;
 
-    public MultiKeyIdentityIterator(Iterable<? extends KeyIdentityProvider> providers) {
+    public MultiKeyIdentityIterator(SessionContext session, Iterable<? extends KeyIdentityProvider> providers) {
         this.providers = (providers == null) ? null : providers.iterator();
+        this.sessionContext = session;
     }
 
     public Iterator<? extends KeyIdentityProvider> getProviders() {
         return providers;
     }
 
+    public SessionContext getSessionContext() {
+        return sessionContext;
+    }
+
     @Override
     public boolean hasNext() {
         if (finished) {
@@ -58,9 +66,10 @@ public class MultiKeyIdentityIterator implements Iterator<KeyPair> {
             return true;
         }
 
+        SessionContext session = getSessionContext();
         while (provs.hasNext()) {
             KeyIdentityProvider p = provs.next();
-            Iterable<KeyPair> keys = (p == null) ? null : p.loadKeys();
+            Iterable<KeyPair> keys = (p == null) ? null : p.loadKeys(session);
             currentProvider = (keys == null) ? null : keys.iterator();
 
             if ((currentProvider != null) && currentProvider.hasNext()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProvider.java
index 18304ff..97b94a5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProvider.java
@@ -23,6 +23,8 @@ import java.security.KeyPair;
 import java.util.Collections;
 import java.util.Iterator;
 
+import org.apache.sshd.common.session.SessionContext;
+
 /**
  * Aggregates several {@link KeyIdentityProvider}-s into a single logical
  * one that (lazily) exposes the keys from each aggregated provider
@@ -37,11 +39,11 @@ public class MultiKeyIdentityProvider implements KeyIdentityProvider {
     }
 
     @Override
-    public Iterable<KeyPair> loadKeys() {
+    public Iterable<KeyPair> loadKeys(SessionContext session) {
         return new Iterable<KeyPair>() {
             @Override
             public Iterator<KeyPair> iterator() {
-                return (providers == null) ? Collections.emptyIterator() : new MultiKeyIdentityIterator(providers);
+                return (providers == null) ? Collections.emptyIterator() : new MultiKeyIdentityIterator(session, providers);
             }
 
             @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 f43f5c0..d30b4b8 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
@@ -39,6 +39,7 @@ 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.security.SecurityUtils;
 
@@ -120,20 +121,20 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
     }
 
     @Override   // co-variant return
-    public synchronized List<KeyPair> loadKeys() {
+    public synchronized List<KeyPair> loadKeys(SessionContext session) {
         Path keyPath = getPath();
         KeyPair kp;
         synchronized (keyPairHolder) {
             kp = keyPairHolder.get();
             if (kp == null) {
                 try {
-                    kp = resolveKeyPair(keyPath);
+                    kp = resolveKeyPair(session, keyPath);
                     if (kp != null) {
                         keyPairHolder.set(kp);
                     }
                 } catch (Throwable t) {
                     log.warn("loadKeys({}) Failed ({}) to resolve: {}",
-                            keyPath, t.getClass().getSimpleName(), t.getMessage());
+                        keyPath, t.getClass().getSimpleName(), t.getMessage());
                     if (log.isDebugEnabled()) {
                         log.debug("loadKeys(" + keyPath + ") resolution failure details", t);
                     }
@@ -148,12 +149,12 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
         }
     }
 
-    protected KeyPair resolveKeyPair(Path keyPath) throws IOException, GeneralSecurityException {
+    protected KeyPair resolveKeyPair(SessionContext session, Path keyPath) throws IOException, GeneralSecurityException {
         String alg = getAlgorithm();
         KeyPair kp;
         if (keyPath != null) {
             try {
-                kp = loadFromFile(alg, keyPath);
+                kp = loadFromFile(session, alg, keyPath);
                 if (kp != null) {
                     return kp;
                 }
@@ -203,13 +204,13 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
         return kp;
     }
 
-    protected KeyPair loadFromFile(String alg, Path keyPath) throws IOException, GeneralSecurityException {
+    protected KeyPair loadFromFile(SessionContext session, String alg, Path keyPath) throws IOException, GeneralSecurityException {
         LinkOption[] options = IoUtils.getLinkOptions(true);
         if ((!Files.exists(keyPath, options)) || (!Files.isRegularFile(keyPath, options))) {
             return null;
         }
 
-        KeyPair kp = readKeyPair(keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
+        KeyPair kp = readKeyPair(session, keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
         if (kp == null) {
             return null;
         }
@@ -225,7 +226,7 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
         if (Objects.equals(alg, keyAlgorithm)) {
             if (log.isDebugEnabled()) {
                 log.debug("resolveKeyPair({}) loaded key={}-{}",
-                          keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                      keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
             }
             return kp;
         }
@@ -233,23 +234,26 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
         // Not same algorithm - start again
         if (log.isDebugEnabled()) {
             log.debug("resolveKeyPair({}) mismatched loaded key algorithm: expected={}, loaded={}",
-                      keyPath, alg, keyAlgorithm);
+                  keyPath, alg, keyAlgorithm);
         }
         Files.deleteIfExists(keyPath);
         return null;
     }
 
-    protected KeyPair readKeyPair(Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
+    protected KeyPair readKeyPair(SessionContext session, Path keyPath, OpenOption... options)
+            throws IOException, GeneralSecurityException {
         try (InputStream inputStream = Files.newInputStream(keyPath, options)) {
-            return doReadKeyPair(keyPath.toString(), inputStream);
+            return doReadKeyPair(session, keyPath.toString(), inputStream);
         }
     }
 
-    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+    protected KeyPair doReadKeyPair(SessionContext session, String resourceKey, InputStream inputStream)
+            throws IOException, GeneralSecurityException {
         return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, null);
     }
 
-    protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
+    protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options)
+            throws IOException, GeneralSecurityException {
         if ((!Files.exists(keyPath)) || isOverwriteAllowed()) {
             try (OutputStream os = Files.newOutputStream(keyPath, options)) {
                 doWriteKeyPair(keyPath.toString(), kp, os);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 7f2888e..b40edcb 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,8 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.spec.InvalidKeySpecException;
 
+import org.apache.sshd.common.session.SessionContext;
+
 /**
  * TODO Add javadoc
  *
@@ -43,7 +45,8 @@ public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProv
     }
 
     @Override
-    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+    protected KeyPair doReadKeyPair(SessionContext session, String resourceKey, InputStream inputStream)
+            throws IOException, GeneralSecurityException {
         try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
             try {
                 return (KeyPair) r.readObject();
@@ -54,7 +57,8 @@ public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProv
     }
 
     @Override
-    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+    protected void doWriteKeyPair(String 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/210f638e/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 19375ec..4f4b285 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
@@ -145,7 +145,7 @@ public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
     }
 
     private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) {
-        Iterable<KeyPair> keys = watcher.loadKeys();
+        Iterable<KeyPair> keys = watcher.loadKeys(null);
         Collection<KeyPair> actual = new ArrayList<>();
         for (KeyPair kp : keys) {
             actual.add(kp);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 144a3b0..690a381 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
@@ -33,6 +33,7 @@ import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 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.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JUnitTestSupport;
@@ -78,10 +79,10 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
         AtomicInteger reloadCount = new AtomicInteger(0);
         ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) {
             @Override
-            protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
+            protected KeyPair reloadClientIdentity(SessionContext session, Path path) throws IOException, GeneralSecurityException {
                 assertEquals("Mismatched client identity path", idFile, path);
                 reloadCount.incrementAndGet();
-                return super.reloadClientIdentity(path);
+                return super.reloadClientIdentity(session, path);
             }
         };
         Files.deleteIfExists(idFile);
@@ -114,7 +115,7 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
     private static void testIdentityReload(
             String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount)
                 throws Exception {
-        KeyPair actualIdentity = provider.getClientIdentity();
+        KeyPair actualIdentity = provider.getClientIdentity(null);
         assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity);
         assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue());
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
index bc2f02c..c8acfa5 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
@@ -90,7 +90,7 @@ public class ClientIdentityTest extends JUnitTestSupport {
         KeyIdentityProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */);
         assertNotNull("No provider generated", provider);
 
-        Iterable<KeyPair> keys = provider.loadKeys();
+        Iterable<KeyPair> keys = provider.loadKeys(null);
         for (KeyPair kp : keys) {
             assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp));
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
index eedbfe7..42b39e1 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
@@ -90,7 +90,7 @@ public class KeyRandomArtTest extends JUnitTestSupport {
 
     @AfterClass
     public static void dumpAllArts() throws Exception {
-        KeyRandomArt.combine(System.out, ' ', () -> KEYS);
+        KeyRandomArt.combine(null, System.out, ' ', session -> KEYS);
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
index afa55cd..6f00621 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
@@ -48,7 +48,7 @@ public class KeyPairProviderTest extends JUnitTestSupport {
     @Test
     public void testEmptyKeyProvider() {
         KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
-        assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys()));
+        assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys(null)));
         assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes(null)));
 
         for (String keyType : new String[]{null, "", getCurrentTestName()}) {
@@ -69,7 +69,7 @@ public class KeyPairProviderTest extends JUnitTestSupport {
 
         KeyPairProvider provider = MappedKeyPairProvider.MAP_TO_KEY_PAIR_PROVIDER.apply(pairsMap);
         assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes(null));
-        assertEquals("Key pairs", pairsMap.values(), provider.loadKeys());
+        assertEquals("Key pairs", pairsMap.values(), provider.loadKeys(null));
 
         pairsMap.forEach((keyType, expected) -> {
             KeyPair actual = provider.loadKey(null, keyType);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
index 5a24da2..e5a4329 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.util.test.JUnitTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -66,7 +67,7 @@ public class MultiKeyIdentityProviderTest extends JUnitTestSupport {
         KeyIdentityProvider multiProvider = KeyIdentityProvider.multiProvider(providers);
         assertObjectInstanceOf(MultiKeyIdentityProvider.class.getSimpleName(), MultiKeyIdentityProvider.class, multiProvider);
 
-        Iterable<KeyPair> keys = multiProvider.loadKeys();
+        Iterable<KeyPair> keys = multiProvider.loadKeys(null);
         Iterator<KeyPair> iter = keys.iterator();
         for (int index = 0, count = expected.size(); index < count; index++) {
             KeyPair kpExpected = expected.get(index);
@@ -82,7 +83,7 @@ public class MultiKeyIdentityProviderTest extends JUnitTestSupport {
     private static KeyIdentityProvider wrapKeyPairs(AtomicInteger position, Iterable<KeyPair> keys) {
         return new KeyIdentityProvider() {
             @Override
-            public Iterable<KeyPair> loadKeys() {
+            public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return new Iterable<KeyPair>() {
                     @Override
                     public Iterator<KeyPair> iterator() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 cdb4967..488d2ce 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
@@ -183,7 +183,7 @@ public class SecurityUtilsTest extends JUnitTestSupport {
             String resourceKey, AbstractResourceKeyPairProvider<?> provider,
             Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
         provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
-        Iterable<KeyPair> iterator = provider.loadKeys();
+        Iterable<KeyPair> iterator = provider.loadKeys(null);
         List<KeyPair> pairs = new ArrayList<>();
         for (KeyPair kp : iterator) {
             pairs.add(kp);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 2978a18..66ecb05 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.session.SessionContext;
 import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
@@ -41,20 +42,20 @@ public class AbstractGeneratorHostKeyProviderTest extends JUnitTestSupport {
         super();
     }
 
-    @SuppressWarnings("synthetic-access")
     @Test
+    @SuppressWarnings("synthetic-access")
     public void testOverwriteKey() throws Exception {
         Path tempDir = assertHierarchyTargetFolderExists(getTempTargetFolder());
         Path keyPairFile = tempDir.resolve(getCurrentTestName() + ".key");
         Files.deleteIfExists(keyPairFile);
 
         TestProvider provider = new TestProvider(keyPairFile);
-        provider.loadKeys();
+        provider.loadKeys(null);
         assertEquals("Mismatched generate write count", 1, provider.getWriteCount());
 
         provider = new TestProvider(keyPairFile);
         provider.setOverwriteAllowed(false);
-        provider.loadKeys();
+        provider.loadKeys(null);
         assertEquals("Mismatched load write count", 0, provider.getWriteCount());
     }
 
@@ -67,12 +68,15 @@ public class AbstractGeneratorHostKeyProviderTest extends JUnitTestSupport {
         }
 
         @Override
-        protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+        protected KeyPair doReadKeyPair(
+                SessionContext session, String resourceKey, InputStream inputStream)
+                    throws IOException, GeneralSecurityException {
             return null;
         }
 
         @Override
-        protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+        protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream)
+                throws IOException, GeneralSecurityException {
             writes.incrementAndGet();
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
index 6558c07..a1d77ef 100644
--- a/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
@@ -453,7 +453,7 @@ public final class CommonTestSupportUtils {
 
     public static KeyPair getFirstKeyPair(KeyIdentityProvider provider) {
         Objects.requireNonNull(provider, "No key pair provider");
-        Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(), "No loaded keys");
+        Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(null), "No loaded keys");
         Iterator<? extends KeyPair> iter = Objects.requireNonNull(pairs.iterator(), "No keys iterator");
         ValidateUtils.checkTrue(iter.hasNext(), "Empty loaded kyes iterator");
         return Objects.requireNonNull(iter.next(), "No key pair in iterator");
@@ -537,7 +537,7 @@ public final class CommonTestSupportUtils {
         Objects.requireNonNull(provider, "No provider");
 
         // get the I/O out of the way
-        Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(), "No keys loaded");
+        Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(null), "No keys loaded");
         if (keys instanceof Collection<?>) {
             ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) keys, "Empty keys loaded");
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java b/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
index d6137a8..32f2712 100644
--- a/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
+++ b/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
@@ -27,6 +27,7 @@ import java.util.Iterator;
 import java.util.List;
 
 import org.apache.sshd.client.config.keys.ClientIdentityProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.util.test.JUnitTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -56,7 +57,7 @@ public class LazyClientIdentityIteratorTest extends JUnitTestSupport {
         Iterable<KeyPair> ids = ClientIdentityProvider.lazyKeysLoader(
             providers, p -> {
                 try {
-                    return p.getClientIdentity();
+                    return p.getClientIdentity(null);
                 } catch (Exception e) {
                     throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
                 }
@@ -93,7 +94,7 @@ public class LazyClientIdentityIteratorTest extends JUnitTestSupport {
         }
 
         @Override
-        public KeyPair getClientIdentity() {
+        public KeyPair getClientIdentity(SessionContext session) {
             loadCount++;
             return getKeyPair();
         }
@@ -101,7 +102,7 @@ public class LazyClientIdentityIteratorTest extends JUnitTestSupport {
         @Override
         public String toString() {
             return getClass().getSimpleName()
-                + "[" + getClientIdentity() + "]"
+                + "[" + getKeyPair() + "]"
                 + ": loadCount=" + getLoadCount();
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
index 73e34ee..d476464 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
@@ -95,7 +95,7 @@ public class UserAuthPublicKeyIterator extends AbstractKeyPairIterator<PublicKey
                 // Lazy load the keys the 1st time the iterator is called
                 if (keysHolder.get() == null) {
                     KeyIdentityProvider sessionKeysProvider = ClientSession.providerOf(session);
-                    keysHolder.set(sessionKeysProvider.loadKeys());
+                    keysHolder.set(sessionKeysProvider.loadKeys(session));
                 }
 
                 return new Iterator<KeyPairIdentity>() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index 63343cb..b6d6235 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -381,7 +381,7 @@ public interface ClientSession
         return (session == null)
             ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER
             : KeyIdentityProvider.resolveKeyIdentityProvider(
-                    session.getRegisteredIdentities(), session.getKeyPairProvider());
+                session.getRegisteredIdentities(), session.getKeyPairProvider());
     }
 
     /**
@@ -396,7 +396,7 @@ public interface ClientSession
      * @see ClientSession#getKeyPairProvider()
      */
     static Iterator<KeyPair> keyPairIteratorOf(ClientSession session) {
-        return KeyIdentityProvider.iteratorOf(providerOf(session));
+        return KeyIdentityProvider.iteratorOf(session, providerOf(session));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
index ae038ae..d6f6d51 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
@@ -437,7 +437,7 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
 
             if (ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(message)) {
                 try {
-                    return KeyRandomArt.combine(' ', session.getKeyPairProvider());
+                    return KeyRandomArt.combine(session, ' ', session.getKeyPairProvider());
                 } catch (Exception e) {
                     if (e instanceof IOException) {
                         throw (IOException) e;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/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 0679fa3..30cb1ff 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
@@ -43,6 +43,7 @@ import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.server.SshServer;
@@ -231,7 +232,7 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
         Collection<KeyPair> clientIdentities = Collections.singletonList(defaultIdentity);
         KeyPairProvider provider = new AbstractKeyPairProvider() {
             @Override
-            public Iterable<KeyPair> loadKeys() {
+            public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return clientIdentities;
             }
         };

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
index 164c2f1..7ae6c96 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
@@ -39,6 +39,7 @@ import org.apache.sshd.common.config.keys.impl.DSSPublicKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.SshServer;
@@ -164,8 +165,8 @@ public class SignatureFactoriesTest extends BaseTestSupport implements OptionalF
     }
 
     protected void testKeyPairProvider(
-            final String keyName, final int keySize, final PublicKeyEntryDecoder<?, ?> decoder, List<NamedFactory<Signature>> signatures)
-                    throws Exception {
+            String keyName, int keySize, PublicKeyEntryDecoder<?, ?> decoder, List<NamedFactory<Signature>> signatures)
+                throws Exception {
         testKeyPairProvider(keyName, () -> {
             try {
                 KeyPair kp = decoder.generateKeyPair(keySize);
@@ -178,12 +179,12 @@ public class SignatureFactoriesTest extends BaseTestSupport implements OptionalF
     }
 
     protected void testKeyPairProvider(
-            final String keyName, final Factory<Iterable<KeyPair>> keyPairFactory, List<NamedFactory<Signature>> signatures)
+            String keyName, Factory<Iterable<KeyPair>> keyPairFactory, List<NamedFactory<Signature>> signatures)
                     throws Exception {
-        final Iterable<KeyPair> iter = keyPairFactory.create();
+        Iterable<KeyPair> iter = keyPairFactory.create();
         testKeyPairProvider(new KeyPairProvider() {
             @Override
-            public Iterable<KeyPair> loadKeys() {
+            public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return iter;
             }
         }, signatures);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
index e417aa9..4ae73b6 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
@@ -98,8 +98,9 @@ public class WelcomeBannerTest extends BaseTestSupport {
     @Test   // see SSHD-686
     public void testAutoGeneratedBanner() throws Exception {
         KeyPairProvider keys = sshd.getKeyPairProvider();
-        PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.WELCOME_BANNER, ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE);
-        testBanner(KeyRandomArt.combine(' ', keys));
+        PropertyResolverUtils.updateProperty(sshd,
+            ServerAuthenticationManager.WELCOME_BANNER, ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE);
+        testBanner(KeyRandomArt.combine(null, ' ', keys));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/210f638e/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
index 5e3bb7b..1665dc9 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
@@ -92,7 +92,7 @@ public class ServerIdentityTest extends BaseTestSupport {
         KeyPairProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */);
         assertNotNull("No provider generated", provider);
 
-        Iterable<KeyPair> keys = provider.loadKeys();
+        Iterable<KeyPair> keys = provider.loadKeys(null);
         for (KeyPair kp : keys) {
             assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp));
         }


[12/12] mina-sshd git commit: [SSHD-864] Replaced BC deprecated PEMWriter with JCAPemWriter in BouncyCastleGeneratorHostKeyProvider

Posted by lg...@apache.org.
[SSHD-864] Replaced BC deprecated PEMWriter with JCAPemWriter in BouncyCastleGeneratorHostKeyProvider


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

Branch: refs/heads/master
Commit: 025df0e85655c83d99a585a620479b2a44287cb2
Parents: 895f30f
Author: Lyor Goldstein <lg...@apache.org>
Authored: Thu Nov 15 16:18:51 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:48 2018 +0200

----------------------------------------------------------------------
 .../BouncyCastleGeneratorHostKeyProvider.java   |  28 +++-
 .../AbstractGeneratorHostKeyProvider.java       |   4 +-
 ...ouncyCastleGeneratorHostKeyProviderTest.java | 140 +++++++++++++++++++
 3 files changed, 166 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/025df0e8/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 534b033..cee48bd 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
@@ -22,12 +22,16 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -37,11 +41,27 @@ public class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostK
         setPath(path);
     }
 
-    @SuppressWarnings("deprecation")
     @Override
-    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))) {
+    protected void doWriteKeyPair(NamedResource resourceKey, KeyPair kp, OutputStream outputStream)
+            throws IOException, GeneralSecurityException {
+        writePEMKeyPair(kp, outputStream);
+    }
+
+    public static void writePEMKeyPair(KeyPair kp, Path targetPath) throws IOException {
+        writePEMKeyPair(kp, targetPath, IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+
+    public static void writePEMKeyPair(
+            KeyPair kp, Path targetPath, OpenOption... options)
+                throws IOException {
+        try (OutputStream os = Files.newOutputStream(targetPath, options)) {
+            writePEMKeyPair(kp, os);
+        }
+    }
+
+    public static void writePEMKeyPair(KeyPair kp, OutputStream outputStream) throws IOException {
+        try (JcaPEMWriter w = new JcaPEMWriter(
+                new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
             w.writeObject(kp);
             w.flush();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/025df0e8/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 be4ceb5..287abec 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
@@ -118,7 +118,7 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
         if ((kp != null) & log.isDebugEnabled()) {
             PublicKey key = kp.getPublic();
             log.debug("clearLoadedKeys({}) removed key={}-{}",
-                      getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                  getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
         }
     }
 
@@ -162,7 +162,7 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
                 }
             } catch (Throwable e) {
                 log.warn("resolveKeyPair({}) Failed ({}) to load: {}",
-                        keyPath, e.getClass().getSimpleName(), e.getMessage());
+                    keyPath, e.getClass().getSimpleName(), e.getMessage());
                 if (log.isDebugEnabled()) {
                     log.debug("resolveKeyPair(" + keyPath + ") load failure details", e);
                 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/025df0e8/sshd-common/src/test/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..3bca2e2
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProviderTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.security.bouncycastle;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class BouncyCastleGeneratorHostKeyProviderTest extends JUnitTestSupport {
+    private final String keyType;
+    private final int keySize;
+
+    public BouncyCastleGeneratorHostKeyProviderTest(String keyType, int keySize) {
+        this.keyType = keyType;
+        this.keySize = keySize;
+    }
+
+    @Parameters(name = "{0} / {1}")
+    public static List<Object[]> parameters() {
+        if (!SecurityUtils.isBouncyCastleRegistered()) {
+            return Collections.emptyList();
+        }
+
+        List<Object[]> params = new ArrayList<>();
+        for (Integer ks : RSA_SIZES) {
+            params.add(new Object[]{BuiltinIdentities.Constants.RSA, ks});
+        }
+        for (Integer ks : DSS_SIZES) {
+            params.add(new Object[]{BuiltinIdentities.Constants.DSA, ks});
+        }
+
+/* TODO - causes an issue where BC cannot parse its own file
+        if (SecurityUtils.isECCSupported()) {
+            for (ECCurves curve : ECCurves.VALUES) {
+                params.add(new Object[]{BuiltinIdentities.Constants.ECDSA, curve.getKeySize()});
+            }
+        }
+*/
+        return params;
+    }
+
+    @Test
+    public void testKeyReadWrite() throws IOException, GeneralSecurityException {
+        KeyPair expected;
+        if (BuiltinIdentities.Constants.RSA.equalsIgnoreCase(keyType)) {
+            expected = KeyUtils.generateKeyPair(KeyPairProvider.SSH_RSA, keySize);
+        } else if (BuiltinIdentities.Constants.DSA.equalsIgnoreCase(keyType)) {
+            expected = KeyUtils.generateKeyPair(KeyPairProvider.SSH_DSS, keySize);
+        } else if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyType)) {
+            ECCurves curve = ECCurves.fromCurveSize(keySize);
+            assertNotNull("No curve for key size=" + keySize, curve);
+            expected = KeyUtils.generateKeyPair(curve.getKeyType(), curve.getKeySize());
+        } else if (BuiltinIdentities.Constants.ED25519.equalsIgnoreCase(keyType)) {
+            KeyPairGenerator g = SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+            expected = g.generateKeyPair();
+        } else {
+            throw new InvalidKeyException("Unsupported key type: " + keyType);
+        }
+
+        PublicKey key = expected.getPublic();
+        String keyAlgorithm = key.getAlgorithm();
+        if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyAlgorithm)) {
+            keyAlgorithm = KeyUtils.EC_ALGORITHM;
+        } else if (BuiltinIdentities.Constants.ED25519.equalsIgnoreCase(keyAlgorithm)) {
+            keyAlgorithm = SecurityUtils.EDDSA;
+        }
+
+        Path dir = getTempTargetFolder();
+        dir = Files.createDirectories(dir.resolve(getClass().getSimpleName()));
+        Path file = dir.resolve(keyType + "-" + keySize + ".pem");
+        BouncyCastleGeneratorHostKeyProvider.writePEMKeyPair(expected, file);
+
+        AbstractGeneratorHostKeyProvider provider = new BouncyCastleGeneratorHostKeyProvider(file);
+        provider.setAlgorithm(keyAlgorithm);
+
+        Iterable<KeyPair> keys = provider.loadKeys(null);
+        KeyPair actual = null;
+        for (KeyPair k : keys) {
+            assertNull("Unexpected multiple keys loaded", actual);
+            actual = k;
+        }
+
+        assertKeyPairEquals(keyType + "/" + keySize, expected, actual);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + keyType + "/" + keySize + "]";
+    }
+}


[02/12] mina-sshd git commit: [SSHD-862] Propagate available session context to code that deals with loading private keys

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 a416871..f7de669 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
@@ -77,12 +77,15 @@ public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
     }
 
     @Override
-    protected KeyPair doLoadKey(SessionContext session, Path resource) throws IOException, GeneralSecurityException {
+    protected KeyPair doLoadKey(SessionContext session, Path resource)
+            throws IOException, GeneralSecurityException {
         return super.doLoadKey(session, (resource == null) ? null : resource.toAbsolutePath());
     }
 
     @Override
-    protected InputStream openKeyPairResource(String resourceKey, Path resource) throws IOException {
+    protected InputStream openKeyPairResource(
+            SessionContext session, String resourceKey, Path resource)
+                throws IOException {
         return Files.newInputStream(resource, IoUtils.EMPTY_OPEN_OPTIONS);
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
index 46b6f52..3298f20 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
@@ -19,6 +19,8 @@
 
 package org.apache.sshd.common.keyprovider;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.Collection;
 import java.util.Collections;
@@ -53,37 +55,11 @@ public interface KeyIdentityProvider {
      *
      * @param session The {@link SessionContext} for invoking this load command - may
      * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
+     * @throws IOException If failed to read/parse the keys data
+     * @throws GeneralSecurityException If failed to generate the keys
      * @return an {@link Iterable} instance of available keys - ignored if {@code null}
      */
-    Iterable<KeyPair> loadKeys(SessionContext session);
-
-    /**
-     * Creates a &quot;unified&quot; {@link Iterator} of {@link KeyPair}s out of 2 possible
-     * {@link KeyIdentityProvider}
-     *
-     * @param session The {@link SessionContext} for invoking this load command - may
-     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
-     * @param identities The registered keys identities
-     * @param keys Extra available key pairs
-     * @return The wrapping iterator
-     * @see #resolveKeyIdentityProvider(KeyIdentityProvider, KeyIdentityProvider)
-     */
-    static Iterator<KeyPair> iteratorOf(
-            SessionContext session, KeyIdentityProvider identities, KeyIdentityProvider keys) {
-        return iteratorOf(session, resolveKeyIdentityProvider(identities, keys));
-    }
-
-    /**
-     * Resolves a non-{@code null} iterator of the available keys
-     *
-     * @param session The {@link SessionContext} for invoking this load command - may
-     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
-     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
-     * @return A non-{@code null} iterator - which may be empty if no provider or no keys
-     */
-    static Iterator<KeyPair> iteratorOf(SessionContext session, KeyIdentityProvider provider) {
-        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys(session));
-    }
+    Iterable<KeyPair> loadKeys(SessionContext session) throws IOException, GeneralSecurityException;
 
     /**
      * <P>Creates a &quot;unified&quot; {@link KeyIdentityProvider} out of 2 possible ones
@@ -159,7 +135,12 @@ public interface KeyIdentityProvider {
             return Collections.emptyList();
         } else if (numProviders == 1) {
             KeyIdentityProvider p = GenericUtils.head(providers);
-            return p.loadKeys(session);
+            try {
+                return p.loadKeys(session);
+            } catch (IOException | GeneralSecurityException e) {
+                throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ")"
+                    + " keys loading exception: " + e.getMessage(), e);
+            }
         } else {
             return new Iterable<KeyPair>() {
                 @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
index 6b2224f..237b61f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sshd.common.keyprovider;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.Collection;
@@ -105,8 +107,11 @@ public interface KeyPairProvider extends KeyIdentityProvider {
      * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @param type the type of key to load
      * @return a valid key pair or {@code null} if this type of key is not available
+     * @throws IOException If failed to read/parse the keys data
+     * @throws GeneralSecurityException If failed to generate the keys
      */
-    default KeyPair loadKey(SessionContext session, String type) {
+    default KeyPair loadKey(SessionContext session, String type)
+            throws IOException, GeneralSecurityException {
         ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
         return GenericUtils.stream(loadKeys(session))
                 .filter(key -> type.equals(KeyUtils.getKeyType(key)))
@@ -118,8 +123,11 @@ public interface KeyPairProvider extends KeyIdentityProvider {
      * @param session The {@link SessionContext} for invoking this load command - may
      * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @return The available {@link Iterable} key types in preferred order - never {@code null}
+     * @throws IOException If failed to read/parse the keys data
+     * @throws GeneralSecurityException If failed to generate the keys
      */
-    default Iterable<String> getKeyTypes(SessionContext session) {
+    default Iterable<String> getKeyTypes(SessionContext session)
+            throws IOException, GeneralSecurityException {
         return GenericUtils.stream(loadKeys(session))
                 .map(KeyUtils::getKeyType)
                 .filter(GenericUtils::isNotEmpty)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
index 9de7c17..09d5f29 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java
@@ -19,6 +19,8 @@
 
 package org.apache.sshd.common.keyprovider;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
@@ -69,7 +71,13 @@ public class MultiKeyIdentityIterator implements Iterator<KeyPair> {
         SessionContext session = getSessionContext();
         while (provs.hasNext()) {
             KeyIdentityProvider p = provs.next();
-            Iterable<KeyPair> keys = (p == null) ? null : p.loadKeys(session);
+            Iterable<KeyPair> keys;
+            try {
+                keys = (p == null) ? null : p.loadKeys(session);
+            } catch (IOException | GeneralSecurityException e) {
+                throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ")"
+                    + " keys loading exception: " + e.getMessage(), e);
+            }
             currentProvider = (keys == null) ? null : keys.iterator();
 
             if ((currentProvider != null) && currentProvider.hasNext()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
index 87827bd..c937079 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
@@ -19,7 +19,7 @@
 
 package org.apache.sshd.common.session;
 
-import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.auth.UsernameHolder;
 import org.apache.sshd.common.util.GenericUtils;
@@ -34,7 +34,7 @@ public interface SessionContext
         extends ConnectionEndpointsIndicator,
                 UsernameHolder,
                 PropertyResolver,
-                AttributeRepository {
+                AttributeStore {
     /**
      * Default prefix expected for the client / server identification string
      * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-4.2">RFC 4253 - section 4.2</A>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
index a55e5d6..35f46d9 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
@@ -213,4 +213,16 @@ public final class ValidateUtils {
         return constructor.apply(message);
     }
 
+    public static <T extends Throwable> T initializeExceptionCause(T err, Throwable cause) {
+        if (cause == null) {
+            return err;
+        }
+
+        if (err.getCause() != null) {
+            return err; // already initialized - avoid IllegalStateException
+        }
+
+        err.initCause(cause);
+        return err;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 1534f95..2949875 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
@@ -67,6 +67,7 @@ import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.random.JceRandomFactory;
 import org.apache.sshd.common.random.RandomFactory;
+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.buffer.Buffer;
@@ -468,9 +469,10 @@ public final class SecurityUtils {
     /* -------------------------------------------------------------------- */
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @param resourceKey An identifier of the key being loaded - used as
-     *                    argument to the {@link FilePasswordProvider#getPassword(String, int)}
-     *                    invocation
+     * argument to the {@code FilePasswordProvider#getPassword} invocation
      * @param inputStream The {@link InputStream} for the <U>private</U> key
      * @param provider    A {@link FilePasswordProvider} - may be {@code null}
      *                    if the loaded key is <U>guaranteed</U> not to be encrypted
@@ -478,23 +480,25 @@ public final class SecurityUtils {
      * @throws IOException              If failed to read/parse the input stream
      * @throws GeneralSecurityException If failed to generate the keys
      */
-    public static KeyPair loadKeyPairIdentity(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
+    public static KeyPair loadKeyPairIdentity(
+            SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+                throws IOException, GeneralSecurityException {
         KeyPairResourceParser parser = getKeyPairResourceParser();
         if (parser == null) {
             throw new NoSuchProviderException("No registered key-pair resource parser");
         }
 
-        Collection<KeyPair> ids = parser.loadKeyPairs(resourceKey, provider, inputStream);
+        Collection<KeyPair> ids = parser.loadKeyPairs(session, resourceKey, provider, inputStream);
         int numLoaded = GenericUtils.size(ids);
         if (numLoaded <= 0) {
-            throw new InvalidKeyException("Unsupported private key file format: " + resourceKey);
+            return null;
         }
+
         if (numLoaded != 1) {
             throw new InvalidKeySpecException("Multiple private key pairs N/A: " + resourceKey);
         }
 
-        return ids.iterator().next();
+        return GenericUtils.head(ids);
     }
 
     /* -------------------------------------------------------------------- */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 5c8befb..881adff 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
@@ -39,6 +39,7 @@ import javax.security.auth.login.FailedLoginException;
 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;
+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.security.SecurityProviderRegistrar;
@@ -76,7 +77,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+            SessionContext session, String 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);
@@ -86,20 +87,24 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
         String data = writer.toString();
         byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
         try (InputStream bais = new ByteArrayInputStream(dataBytes)) {
-            return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+            return extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, bais);
         }
     }
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
-        KeyPair kp = loadKeyPair(resourceKey, stream, passwordProvider);
+        KeyPair kp = loadKeyPair(session, resourceKey, stream, passwordProvider);
         return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
     }
 
-    public static KeyPair loadKeyPair(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
+    public static KeyPair loadKeyPair(
+            SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+                throws IOException, GeneralSecurityException {
         try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
             Object o = r.readObject();
 
@@ -122,7 +127,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
                 }
 
                 for (int retryIndex = 0;; retryIndex++) {
-                    String password = provider.getPassword(resourceKey, retryIndex);
+                    String password = provider.getPassword(session, resourceKey, retryIndex);
                     PEMKeyPair decoded;
                     try {
                         if (GenericUtils.isEmpty(password)) {
@@ -134,7 +139,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
                         decoded = ((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor);
                     } catch (IOException | GeneralSecurityException | RuntimeException e) {
                         ResourceDecodeResult result =
-                            provider.handleDecodeAttemptResult(resourceKey, retryIndex, password, e);
+                            provider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, e);
                         if (result == null) {
                             result = ResourceDecodeResult.TERMINATE;
                         }
@@ -151,7 +156,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
                     }
 
                     o = decoded;
-                    provider.handleDecodeAttemptResult(resourceKey, retryIndex, password, null);
+                    provider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, null);
                     break;
                 }
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
index 2f1d055..f9402eb 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
@@ -36,6 +36,7 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyEntryResolver;
 import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 import net.i2p.crypto.eddsa.EdDSAPrivateKey;
@@ -62,8 +63,9 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntr
     }
 
     @Override
-    public EdDSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
+    public EdDSAPrivateKey decodePrivateKey(
+            SessionContext session, String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+                throws IOException, GeneralSecurityException {
         if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
             throw new InvalidKeyException("Unsupported key type: " + keyType);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 d30b4b8..71e3a4f 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
@@ -249,7 +249,7 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
 
     protected KeyPair doReadKeyPair(SessionContext session, String resourceKey, InputStream inputStream)
             throws IOException, GeneralSecurityException {
-        return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, null);
+        return SecurityUtils.loadKeyPairIdentity(session, resourceKey, inputStream, null);
     }
 
     protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 4f4b285..81c458f 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
@@ -40,6 +40,7 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+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.IoUtils;
@@ -78,7 +79,9 @@ public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
 
         ClientIdentityLoader loader = new ClientIdentityLoader() {
             @Override
-            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+            public KeyPair loadClientIdentity(
+                    SessionContext session, String location, FilePasswordProvider provider)
+                        throws IOException, GeneralSecurityException {
                 BuiltinIdentities id = findIdentity(location);
                 assertNotNull("Invalid location: " + location, id);
                 return idsMap.get(id);
@@ -144,7 +147,9 @@ public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
         }
     }
 
-    private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) {
+    private static void testMultipleFilesWatch(
+            String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected)
+                throws IOException, GeneralSecurityException {
         Iterable<KeyPair> keys = watcher.loadKeys(null);
         Collection<KeyPair> actual = new ArrayList<>();
         for (KeyPair kp : keys) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 690a381..bd43f44 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
@@ -60,7 +60,9 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
         KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
         ClientIdentityLoader loader = new ClientIdentityLoader() {
             @Override
-            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+            public KeyPair loadClientIdentity(
+                    SessionContext session, String location, FilePasswordProvider provider)
+                        throws IOException, GeneralSecurityException {
                 assertTrue("Invalid location: " + location, isValidLocation(location));
                 return identity;
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
index d5c6aba..680a53f 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
@@ -73,7 +73,7 @@ public class OpenSSHKeyPairResourceParserTest extends JUnitTestSupport {
         URL urlKeyPair = getClass().getResource(resourceKey);
         assertNotNull("Missing key-pair resource: " + resourceKey, urlKeyPair);
 
-        Collection<KeyPair> pairs = PARSER.loadKeyPairs(urlKeyPair, FilePasswordProvider.EMPTY);
+        Collection<KeyPair> pairs = PARSER.loadKeyPairs(null, urlKeyPair, FilePasswordProvider.EMPTY);
         assertEquals("Mismatched pairs count", 1, GenericUtils.size(pairs));
 
         URL urlPubKey = getClass().getResource(resourceKey + ".pub");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 406a273..9775e2c 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
@@ -92,7 +92,7 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
             os.close();
 
             try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
-                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(getCurrentTestName(), bais, null);
+                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(null, 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/2b013cca/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
index 6f00621..fb9ed8c 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
@@ -19,6 +19,8 @@
 
 package org.apache.sshd.common.keyprovider;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.PublicKey;
@@ -46,7 +48,7 @@ public class KeyPairProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testEmptyKeyProvider() {
+    public void testEmptyKeyProvider() throws IOException, GeneralSecurityException {
         KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
         assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys(null)));
         assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes(null)));
@@ -57,7 +59,7 @@ public class KeyPairProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testMapToKeyPairProvider() {
+    public void testMapToKeyPairProvider() throws IOException, GeneralSecurityException {
         PublicKey pubKey = Mockito.mock(PublicKey.class);
         PrivateKey prvKey = Mockito.mock(PrivateKey.class);
         String[] testKeys = {getCurrentTestName(), getClass().getSimpleName()};
@@ -71,9 +73,11 @@ public class KeyPairProviderTest extends JUnitTestSupport {
         assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes(null));
         assertEquals("Key pairs", pairsMap.values(), provider.loadKeys(null));
 
-        pairsMap.forEach((keyType, expected) -> {
+        for (Map.Entry<String, KeyPair> pe : pairsMap.entrySet()) {
+            String keyType = pe.getKey();
+            KeyPair expected = pe.getValue();
             KeyPair actual = provider.loadKey(null, keyType);
             assertSame(keyType, expected, actual);
-        });
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
index e5a4329..a1a2dca 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityProviderTest.java
@@ -19,6 +19,8 @@
 
 package org.apache.sshd.common.keyprovider;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.PublicKey;
@@ -47,7 +49,7 @@ public class MultiKeyIdentityProviderTest extends JUnitTestSupport {
     }
 
     @Test   // see SSHD-860
-    public void testLazyKeyIdentityMultiProvider() {
+    public void testLazyKeyIdentityMultiProvider() throws IOException, GeneralSecurityException {
         List<KeyPair> expected = new ArrayList<>();
         for (int index = 1; index <= Short.SIZE; index++) {
             PublicKey pub = Mockito.mock(PublicKey.class);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 488d2ce..e6019fe 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
@@ -67,7 +67,7 @@ public class SecurityUtilsTest extends JUnitTestSupport {
           + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
 
     private static final String DEFAULT_PASSWORD = "super secret passphrase";
-    private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = (file, index) -> DEFAULT_PASSWORD;
+    private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = (session, file, index) -> DEFAULT_PASSWORD;
 
     public SecurityUtilsTest() {
         super();
@@ -155,7 +155,7 @@ public class SecurityUtilsTest extends JUnitTestSupport {
         KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
         if (SecurityUtils.isBouncyCastleRegistered()) {
             KeyPairResourceLoader bcLoader = SecurityUtils.getBouncycastleKeyPairResourceParser();
-            Collection<KeyPair> kpList = bcLoader.loadKeyPairs(file, TEST_PASSWORD_PROVIDER);
+            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();
@@ -170,18 +170,21 @@ public class SecurityUtilsTest extends JUnitTestSupport {
     }
 
     private static KeyPair testLoadPrivateKeyResource(
-            String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+            String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
+                throws IOException, GeneralSecurityException {
         return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
     }
 
     private static KeyPair testLoadPrivateKeyFile(
-            Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+            Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
+                throws IOException, GeneralSecurityException {
         return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
     }
 
     private static KeyPair testLoadPrivateKey(
             String resourceKey, AbstractResourceKeyPairProvider<?> provider,
-            Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+            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<>();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
index af4a6b7..a7a4659 100644
--- a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
@@ -21,6 +21,7 @@ package org.apache.sshd.server.keyprovider;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.security.interfaces.ECPublicKey;
@@ -51,19 +52,19 @@ public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testDSA() throws IOException {
+    public void testDSA() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         testPEMGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
     }
 
     @Test
-    public void testRSA() throws IOException {
+    public void testRSA() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         testPEMGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
     }
 
     @Test
-    public void testECnistp256() throws IOException {
+    public void testECnistp256() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
         Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
@@ -71,7 +72,7 @@ public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testECnistp384() throws IOException {
+    public void testECnistp384() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
         Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
@@ -79,14 +80,16 @@ public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testECnistp521() throws IOException {
+    public void testECnistp521() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
         Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
         testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
     }
 
-    private Path testPEMGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
+    private Path testPEMGeneratorHostKeyProvider(
+            String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec)
+                throws IOException, GeneralSecurityException {
         Path path = initKeyFileLocation(algorithm);
         KeyPair kpWrite = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
         assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
@@ -103,7 +106,9 @@ public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
         return path;
     }
 
-    private static KeyPair invokePEMGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
+    private static KeyPair invokePEMGeneratorHostKeyProvider(
+            Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec)
+                throws IOException, GeneralSecurityException {
         AbstractGeneratorHostKeyProvider provider = SecurityUtils.createGeneratorHostKeyProvider(path.toAbsolutePath().normalize());
         provider.setAlgorithm(algorithm);
         provider.setOverwriteAllowed(true);
@@ -117,7 +122,8 @@ public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
         return validateKeyPairProvider(provider, keyType);
     }
 
-    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
+    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType)
+            throws IOException, GeneralSecurityException {
         Iterable<String> types = provider.getKeyTypes(null);
         KeyPair kp = null;
         for (String type : types) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
index ab0ff2c..fe4a7d0 100644
--- a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
@@ -21,6 +21,7 @@ package org.apache.sshd.server.keyprovider;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
@@ -51,17 +52,17 @@ public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testDSA() throws IOException {
+    public void testDSA() throws IOException, GeneralSecurityException {
         testSimpleGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
     }
 
     @Test
-    public void testRSA() throws IOException {
+    public void testRSA() throws IOException, GeneralSecurityException {
         testSimpleGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
     }
 
     @Test
-    public void testECnistp256() throws IOException {
+    public void testECnistp256() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
         Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
@@ -69,7 +70,7 @@ public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testECnistp384() throws IOException {
+    public void testECnistp384() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
         Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
@@ -77,14 +78,16 @@ public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testECnistp521() throws IOException {
+    public void testECnistp521() throws IOException, GeneralSecurityException {
         Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
         Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
         Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
         testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
     }
 
-    private Path testSimpleGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
+    private Path testSimpleGeneratorHostKeyProvider(
+            String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec)
+                throws IOException, GeneralSecurityException {
         Path path = initKeyFileLocation(algorithm);
         KeyPair kpWrite = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
         assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
@@ -94,7 +97,9 @@ public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
         return path;
     }
 
-    private static KeyPair invokeSimpleGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
+    private static KeyPair invokeSimpleGeneratorHostKeyProvider(
+            Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec)
+                throws IOException, GeneralSecurityException {
         SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
         provider.setAlgorithm(algorithm);
         provider.setOverwriteAllowed(true);
@@ -109,7 +114,9 @@ public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
         return validateKeyPairProvider(provider, keyType);
     }
 
-    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
+    private static KeyPair validateKeyPairProvider(
+            KeyPairProvider provider, String keyType)
+                throws IOException, GeneralSecurityException {
         Iterable<String> types = provider.getKeyTypes(null);
         KeyPair kp = null;
         for (String type : types) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
index a1d77ef..d920344 100644
--- a/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
@@ -453,7 +453,14 @@ public final class CommonTestSupportUtils {
 
     public static KeyPair getFirstKeyPair(KeyIdentityProvider provider) {
         Objects.requireNonNull(provider, "No key pair provider");
-        Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(null), "No loaded keys");
+        Iterable<? extends KeyPair> pairs;
+        try {
+            pairs = Objects.requireNonNull(provider.loadKeys(null), "No loaded keys");
+        } catch (IOException | GeneralSecurityException e) {
+            throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ")"
+                + " keys loading exception: " + e.getMessage(), e);
+        }
+
         Iterator<? extends KeyPair> iter = Objects.requireNonNull(pairs.iterator(), "No keys iterator");
         ValidateUtils.checkTrue(iter.hasNext(), "Empty loaded kyes iterator");
         return Objects.requireNonNull(iter.next(), "No key pair in iterator");
@@ -537,7 +544,14 @@ public final class CommonTestSupportUtils {
         Objects.requireNonNull(provider, "No provider");
 
         // get the I/O out of the way
-        Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(null), "No keys loaded");
+        Iterable<KeyPair> keys;
+        try {
+            keys = Objects.requireNonNull(provider.loadKeys(null), "No keys loaded");
+        } catch (IOException | GeneralSecurityException e) {
+            throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ")"
+                + " keys loading exception: " + e.getMessage(), e);
+        }
+
         if (keys instanceof Collection<?>) {
             ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) keys, "Empty keys loaded");
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 6c3e9c8..6ece3d9 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
@@ -524,7 +524,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         List<KeyPair> ids = new ArrayList<>(locations.size());
         boolean ignoreNonExisting = this.getBooleanProperty(IGNORE_INVALID_IDENTITIES, DEFAULT_IGNORE_INVALID_IDENTITIES);
         ClientIdentityLoader loader = Objects.requireNonNull(getClientIdentityLoader(), "No ClientIdentityLoader");
-        FilePasswordProvider provider = Objects.requireNonNull(getFilePasswordProvider(), "No FilePasswordProvider");
+        FilePasswordProvider provider = getFilePasswordProvider();
         boolean debugEnabled = log.isDebugEnabled();
         for (String l : locations) {
             if (!loader.isValidLocation(l)) {
@@ -539,14 +539,14 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
             }
 
             try {
-                KeyPair kp = loader.loadClientIdentity(l, provider);
+                KeyPair kp = loader.loadClientIdentity(null /* TODO use lazy-load here as well */, l, provider);
                 if (kp == null) {
                     throw new IOException("No identity loaded from " + l);
                 }
 
                 if (debugEnabled) {
                     log.debug("loadClientIdentities({}) type={}, fingerprint={}",
-                              l, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+                          l, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
                 }
 
                 ids.add(kp);
@@ -561,7 +561,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
     protected ConnectFuture doConnect(
             String username, SocketAddress targetAddress,
             AttributeRepository context, SocketAddress localAddress,
-            Collection<? extends KeyPair> identities, boolean useDefaultIdentities)
+            Iterable<? extends KeyPair> identities, boolean useDefaultIdentities)
                 throws IOException {
         if (connector == null) {
             throw new IllegalStateException("SshClient not started. Please call start() method before connecting to a server");
@@ -578,7 +578,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
     protected SshFutureListener<IoConnectFuture> createConnectCompletionListener(
             ConnectFuture connectFuture, String username, SocketAddress address,
-            Collection<? extends KeyPair> identities, boolean useDefaultIdentities) {
+            Iterable<? extends KeyPair> identities, boolean useDefaultIdentities) {
         return new SshFutureListener<IoConnectFuture>() {
             @Override
             @SuppressWarnings("synthetic-access")
@@ -621,7 +621,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
     protected void onConnectOperationComplete(
             IoSession ioSession, ConnectFuture connectFuture,  String username,
-            SocketAddress address, Collection<? extends KeyPair> identities, boolean useDefaultIdentities) {
+            SocketAddress address, Iterable<? extends KeyPair> identities, boolean useDefaultIdentities) {
         AbstractClientSession session = (AbstractClientSession) AbstractSession.getSession(ioSession);
         session.setUsername(username);
         session.setConnectAddress(address);
@@ -630,12 +630,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
             setupDefaultSessionIdentities(session);
         }
 
-        int numIds = GenericUtils.size(identities);
-        if (numIds > 0) {
-            if (log.isDebugEnabled()) {
-                log.debug("onConnectOperationComplete({}) adding {} identities", session, numIds);
-            }
-
+        if (identities != null) {
             boolean traceEnabled = log.isTraceEnabled();
             for (KeyPair kp : identities) {
                 if (traceEnabled) {
@@ -792,7 +787,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                      to {@code FilePasswordProvider#getPassword} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
@@ -818,7 +813,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                      to {@code FilePasswordProvider#getPassword} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
index d476464..a6f135c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
@@ -21,6 +21,7 @@ package org.apache.sshd.client.auth.pubkey;
 
 import java.io.IOException;
 import java.nio.channels.Channel;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.util.ArrayList;
@@ -84,6 +85,7 @@ public class UserAuthPublicKeyIterator extends AbstractKeyPairIterator<PublicKey
         }
     }
 
+    @SuppressWarnings("checkstyle:anoninnerlength")
     protected Iterable<KeyPairIdentity> initializeSessionIdentities(
             ClientSession session, SignatureFactoriesManager signatureFactories) {
         return new Iterable<KeyPairIdentity>() {
@@ -94,8 +96,13 @@ public class UserAuthPublicKeyIterator extends AbstractKeyPairIterator<PublicKey
             public Iterator<KeyPairIdentity> iterator() {
                 // Lazy load the keys the 1st time the iterator is called
                 if (keysHolder.get() == null) {
-                    KeyIdentityProvider sessionKeysProvider = ClientSession.providerOf(session);
-                    keysHolder.set(sessionKeysProvider.loadKeys(session));
+                    try {
+                        KeyIdentityProvider sessionKeysProvider = ClientSession.providerOf(session);
+                        keysHolder.set(sessionKeysProvider.loadKeys(session));
+                    } catch (IOException | GeneralSecurityException e) {
+                        throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ")"
+                            + " keys loading exception: " + e.getMessage(), e);
+                    }
                 }
 
                 return new Iterator<KeyPairIdentity>() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index b6d6235..b7b5377 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -385,21 +385,6 @@ public interface ClientSession
     }
 
     /**
-     * Creates a &quot;unified&quot; {@link Iterator} of key pairs out of the registered
-     * {@link KeyPair} identities and the extra available ones as a single iterator
-     * of key pairs
-     *
-     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
-     * iterator returned)
-     * @return The wrapping iterator
-     * @see ClientSession#getRegisteredIdentities()
-     * @see ClientSession#getKeyPairProvider()
-     */
-    static Iterator<KeyPair> keyPairIteratorOf(ClientSession session) {
-        return KeyIdentityProvider.iteratorOf(session, providerOf(session));
-    }
-
-    /**
      * Creates a &quot;unified&quot; {@link Iterator} of passwords out of the registered
      * passwords and the extra available ones as a single iterator of passwords
      *

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 0ecab07..8c094cd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -24,7 +24,6 @@ import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.AttributeRepository;
-import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.FactoryManagerHolder;
@@ -54,7 +53,6 @@ import org.apache.sshd.common.util.buffer.Buffer;
  */
 public interface Session
         extends SessionContext,
-                AttributeStore,
                 MutableUserHolder,
                 KexFactoryManager,
                 SessionListenerManager,

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index db3b17a..fc9958f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -20,8 +20,10 @@ package org.apache.sshd.common.session.helpers;
 
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.net.ProtocolException;
 import java.net.SocketTimeoutException;
 import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
 import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -711,7 +713,16 @@ public abstract class AbstractSession extends SessionHelper {
             return doWritePacket(buffer);
         } finally {
             resetIdleTimeout();
-            checkRekey();
+            try {
+                checkRekey();
+            } catch (GeneralSecurityException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("writePacket(" + this + ") rekey security exception details", e);
+                }
+                throw ValidateUtils.initializeExceptionCause(
+                    new ProtocolException("Failed (" + e.getClass().getSimpleName() + ")"
+                        + " to check re-key necessity: " + e.getMessage()), e);
+            }
         }
     }
 
@@ -1101,8 +1112,9 @@ public abstract class AbstractSession extends SessionHelper {
      * @return <code>true</code> if the identification has been fully read or
      * <code>false</code> if more data is needed
      * @throws IOException if an error occurs such as a bad protocol version
+     * @throws GeneralSecurityException If unsuccessful KEX was involved
      */
-    protected abstract boolean readIdentification(Buffer buffer) throws IOException;
+    protected abstract boolean readIdentification(Buffer buffer) throws IOException, GeneralSecurityException;
 
     /**
      * Send the key exchange initialization packet.
@@ -1607,7 +1619,17 @@ public abstract class AbstractSession extends SessionHelper {
 
     @Override
     public KeyExchangeFuture reExchangeKeys() throws IOException {
-        requestNewKeysExchange();
+        try {
+            requestNewKeysExchange();
+        } catch (GeneralSecurityException e) {
+            if (log.isDebugEnabled()) {
+                log.debug("reExchangeKeys(" + this + ") security exception details", e);
+            }
+            throw ValidateUtils.initializeExceptionCause(
+                new ProtocolException("Failed (" + e.getClass().getSimpleName() + ")"
+                    + " to generate keys for exchange: " + e.getMessage()), e);
+        }
+
         return ValidateUtils.checkNotNull(kexFutureHolder.get(), "No current KEX future on state=%s", kexState.get());
     }
 
@@ -1616,11 +1638,12 @@ public abstract class AbstractSession extends SessionHelper {
      *
      * @return A {@link KeyExchangeFuture} to wait for the initiated exchange
      * or {@code null} if no need to re-key or an exchange is already in progress
-     * @throws IOException If failed to send the request
+     * @throws IOException If failed load the keys or send the request
+     * @throws GeneralSecurityException If failed to generate the necessary keys
      * @see #isRekeyRequired()
      * @see #requestNewKeysExchange()
      */
-    protected KeyExchangeFuture checkRekey() throws IOException {
+    protected KeyExchangeFuture checkRekey() throws IOException, GeneralSecurityException {
         return isRekeyRequired() ? requestNewKeysExchange() : null;
     }
 
@@ -1629,9 +1652,10 @@ public abstract class AbstractSession extends SessionHelper {
      *
      * @return A {@link KeyExchangeFuture} to wait for the initiated exchange
      * or {@code null} if an exchange is already in progress
-     * @throws IOException If failed to send the request
+     * @throws IOException If failed to load the keys or send the request
+     * @throws GeneralSecurityException If failed to generate the keys
      */
-    protected KeyExchangeFuture requestNewKeysExchange() throws IOException {
+    protected KeyExchangeFuture requestNewKeysExchange() throws IOException, GeneralSecurityException {
         if (!kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
             if (log.isDebugEnabled()) {
                 log.debug("requestNewKeysExchange({}) KEX state not DONE: {}", this, kexState.get());
@@ -1740,7 +1764,7 @@ public abstract class AbstractSession extends SessionHelper {
         return rekey;
     }
 
-    protected byte[] sendKexInit() throws IOException {
+    protected byte[] sendKexInit() throws IOException, GeneralSecurityException {
         String resolvedAlgorithms = resolveAvailableSignaturesProposal();
         if (GenericUtils.isEmpty(resolvedAlgorithms)) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
@@ -1794,10 +1818,12 @@ public abstract class AbstractSession extends SessionHelper {
     /**
      * @return A comma-separated list of all the signature protocols to be
      * included in the proposal - {@code null}/empty if no proposal
+     * @throws IOException If failed to read/parse the keys data
+     * @throws GeneralSecurityException If failed to generate the keys
      * @see #getFactoryManager()
      * @see #resolveAvailableSignaturesProposal(FactoryManager)
      */
-    protected String resolveAvailableSignaturesProposal() {
+    protected String resolveAvailableSignaturesProposal() throws IOException, GeneralSecurityException {
         return resolveAvailableSignaturesProposal(getFactoryManager());
     }
 
@@ -1805,8 +1831,11 @@ public abstract class AbstractSession extends SessionHelper {
      * @param manager The {@link FactoryManager}
      * @return A comma-separated list of all the signature protocols to be
      * included in the proposal - {@code null}/empty if no proposal
+     * @throws IOException If failed to read/parse the keys data
+     * @throws GeneralSecurityException If failed to generate the keys
      */
-    protected abstract String resolveAvailableSignaturesProposal(FactoryManager manager);
+    protected abstract String resolveAvailableSignaturesProposal(FactoryManager manager)
+            throws IOException, GeneralSecurityException;
 
     /**
      * Indicates the the key exchange is completed and the exchanged keys

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
index 0823a8f..4757838 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
@@ -117,9 +117,11 @@ public final class ServerIdentity {
      * @see #findIdentities(Properties, LinkOption...)
      * @see IdentityUtils#loadIdentities(Map, org.apache.sshd.common.config.keys.FilePasswordProvider, java.nio.file.OpenOption...)
      */
-    public static Map<String, KeyPair> loadIdentities(Properties props, LinkOption... options) throws IOException, GeneralSecurityException {
+    public static Map<String, KeyPair> loadIdentities(Properties props, LinkOption... options)
+            throws IOException, GeneralSecurityException {
         Map<String, Path> ids = findIdentities(props, options);
-        return IdentityUtils.loadIdentities(ids, null /* server key files are never encrypted */, IoUtils.EMPTY_OPEN_OPTIONS);
+        return IdentityUtils.loadIdentities(
+                null /* server keys are not loaded in a session context */, ids, null /* server key files are never encrypted */, IoUtils.EMPTY_OPEN_OPTIONS);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 69f450a..6af869e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -22,6 +22,7 @@ package org.apache.sshd.server.session;
 import java.io.IOException;
 import java.net.SocketAddress;
 import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.Collection;
 import java.util.List;
@@ -247,7 +248,8 @@ public abstract class AbstractServerSession extends AbstractSession implements S
     }
 
     @Override
-    protected String resolveAvailableSignaturesProposal(FactoryManager proposedManager) {
+    protected String resolveAvailableSignaturesProposal(FactoryManager proposedManager)
+            throws IOException, GeneralSecurityException {
         /*
          * Make sure we can provide key(s) for the available signatures
          */
@@ -319,7 +321,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
     }
 
     @Override
-    protected boolean readIdentification(Buffer buffer) throws IOException {
+    protected boolean readIdentification(Buffer buffer) throws IOException, GeneralSecurityException {
         ServerProxyAcceptor acceptor = getServerProxyAcceptor();
         int rpos = buffer.rpos();
         boolean debugEnabled = log.isDebugEnabled();
@@ -395,7 +397,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
         KeyPairProvider provider = Objects.requireNonNull(getKeyPairProvider(), "No host keys provider");
         try {
             return provider.loadKey(this, keyType);
-        } catch (Error e) {
+        } catch (IOException | GeneralSecurityException | Error e) {
             log.warn("getHostKey({}) failed ({}) to load key of type={}: {}",
                  this, e.getClass().getSimpleName(), keyType, e.getMessage());
             if (log.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 30cb1ff..3881890 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
@@ -153,8 +153,9 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
             }
 
             @Override
-            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider)
-                    throws IOException, GeneralSecurityException {
+            public KeyPair loadClientIdentity(
+                    SessionContext session, String location, FilePasswordProvider provider)
+                        throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
                     return identity;
                 }
@@ -218,8 +219,9 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
             }
 
             @Override
-            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider)
-                    throws IOException, GeneralSecurityException {
+            public KeyPair loadClientIdentity(
+                    SessionContext session, String location, FilePasswordProvider provider)
+                        throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
                     return specificIdentity;
                 }


[03/12] mina-sshd git commit: [SSHD-862] Propagate available session context to code that deals with loading private keys

Posted by lg...@apache.org.
[SSHD-862] Propagate available session context to code that deals with loading private keys


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

Branch: refs/heads/master
Commit: 2b013cca86bede945a6da0fd20e6afaab6aca22a
Parents: 20be0e9
Author: Lyor Goldstein <lg...@apache.org>
Authored: Wed Nov 14 08:37:11 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  43 +--
 .../sshd/cli/client/SshClientCliSupport.java    |   2 +-
 .../sshd/cli/server/SshServerCliSupport.java    |   2 +-
 .../sshd/client/config/keys/ClientIdentity.java |  39 +--
 .../config/keys/ClientIdentityFileWatcher.java  |   3 +-
 .../config/keys/ClientIdentityLoader.java       |  13 +-
 .../config/keys/FilePasswordProvider.java       |  14 +-
 .../sshd/common/config/keys/IdentityUtils.java  |  12 +-
 .../config/keys/PrivateKeyEntryDecoder.java     |  33 ++-
 .../config/keys/PrivateKeyEntryResolver.java    |  13 +-
 .../loader/AbstractKeyPairResourceParser.java   |  38 ++-
 .../keys/loader/KeyPairResourceLoader.java      |  67 +++--
 .../keys/loader/KeyPairResourceParser.java      |   7 +-
 .../OpenSSHDSSPrivateKeyEntryDecoder.java       |   6 +-
 .../OpenSSHECDSAPrivateKeyEntryDecoder.java     |   6 +-
 .../openssh/OpenSSHKeyPairResourceParser.java   |  32 ++-
 .../openssh/OpenSSHRSAPrivateKeyDecoder.java    |   6 +-
 .../pem/AbstractPEMResourceKeyPairParser.java   |  16 +-
 .../loader/pem/DSSPEMResourceKeyPairParser.java |   6 +-
 .../pem/ECDSAPEMResourceKeyPairParser.java      |   6 +-
 .../keys/loader/pem/PEMResourceParserUtils.java |   7 +-
 .../pem/PKCS8PEMResourceKeyPairParser.java      |  18 +-
 .../loader/pem/RSAPEMResourceKeyPairParser.java |   6 +-
 .../AbstractResourceKeyPairProvider.java        |  11 +-
 .../ClassLoadableResourceKeyPairProvider.java   |   4 +-
 .../common/keyprovider/FileKeyPairProvider.java |   7 +-
 .../common/keyprovider/KeyIdentityProvider.java |  41 +--
 .../common/keyprovider/KeyPairProvider.java     |  12 +-
 .../keyprovider/MultiKeyIdentityIterator.java   |  10 +-
 .../sshd/common/session/SessionContext.java     |   4 +-
 .../apache/sshd/common/util/ValidateUtils.java  |  12 +
 .../common/util/security/SecurityUtils.java     |  18 +-
 .../BouncyCastleKeyPairResourceParser.java      |  23 +-
 .../OpenSSHEd25519PrivateKeyEntryDecoder.java   |   6 +-
 .../AbstractGeneratorHostKeyProvider.java       |   2 +-
 .../BuiltinClientIdentitiesWatcherTest.java     |   9 +-
 .../keys/ClientIdentityFileWatcherTest.java     |   4 +-
 .../OpenSSHKeyPairResourceParserTest.java       |   2 +-
 .../pem/PKCS8PEMResourceKeyPairParserTest.java  |   2 +-
 .../common/keyprovider/KeyPairProviderTest.java |  12 +-
 .../MultiKeyIdentityProviderTest.java           |   4 +-
 .../common/util/security/SecurityUtilsTest.java |  13 +-
 .../PEMGeneratorHostKeyProviderTest.java        |  22 +-
 .../SimpleGeneratorHostKeyProviderTest.java     |  23 +-
 .../sshd/util/test/CommonTestSupportUtils.java  |  18 +-
 .../java/org/apache/sshd/client/SshClient.java  |  23 +-
 .../auth/pubkey/UserAuthPublicKeyIterator.java  |  11 +-
 .../sshd/client/session/ClientSession.java      |  15 -
 .../org/apache/sshd/common/session/Session.java |   2 -
 .../common/session/helpers/AbstractSession.java |  49 +++-
 .../sshd/server/config/keys/ServerIdentity.java |   6 +-
 .../server/session/AbstractServerSession.java   |   8 +-
 .../hosts/HostConfigEntryResolverTest.java      |  10 +-
 .../sshd/common/auth/AuthenticationTest.java    | 278 ++++++++++++++-----
 .../common/auth/SinglePublicKeyAuthTest.java    |   4 +-
 .../super-secret-passphrase-RSA-AES-128-key     |  30 ++
 .../super-secret-passphrase-RSA-AES-128-key.pub |   1 +
 .../openpgp/PGPKeyPairResourceParser.java       |  19 +-
 .../openpgp/PGPKeyPairResourceParserTest.java   |   7 +-
 .../loader/putty/AbstractPuttyKeyDecoder.java   |  23 +-
 .../keys/loader/putty/PuttyKeyUtilsTest.java    |  18 +-
 .../sftp/ApacheSshdSftpSessionFactory.java      |   2 +-
 62 files changed, 770 insertions(+), 390 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 57d53f6..9cc1cd4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,19 +2,19 @@
 
 ## Major code re-factoring
 
-* `AttributeStore` "read" methods moved to (new class) `AttributeRepository`
+* `AttributeStore` "read" methods moved to (new class) `AttributeRepository`.
 
-    * `AttributeKey` moved to `AttributeRepository`
+    * `AttributeKey` moved to `AttributeRepository`.
 
-    * `getAttribute` and `resolveAttribute` moved to `AttributeRepository`
+    * `getAttribute` and `resolveAttribute` moved to `AttributeRepository`.
 
     * Added `attributeKeys` enumeration method to `AttributeRepository`.
 
-* `DEFAULT_PORT` moved from `SshConfigFileReader` to `SshConstants`
+* `DEFAULT_PORT` moved from `SshConfigFileReader` to `SshConstants`.
 
-* Moved some session "summary" related definitions from `Session` to `SessionContext` (which `Session` extends)
+* Moved some session "summary" related definitions from `Session` to `SessionContext` (which `Session` extends).
 
-* Added new `sessionDisconnect` method to `SessionListener`
+* Added new `sessionDisconnect` method to `SessionListener`.
 
 * `ReservedSessionMessagesHandler#handleUnimplementedMessage` has an extra `cmd` argument
 and is called both for `SSH_MSG_UNIMPLEMENTED` as well as for any other unexpected/unrecognized
@@ -35,44 +35,45 @@ accept also an `AttributeRepository` connection context argument (propagated fro
     * The interface methods are also provided with a retry index that indicates the number of
     times they have been re-invoked for the same resource (including on success).
 
+    * The available session context (if any) is also provided as an argument to the interface methods.
+
 * `SshAgent#getIdentities` returns an `Iterable` rather than a `List`
 
 * `SftpFileSystemProvider` and its associated helper classes have been moved to
-`org.apache.sshd.client.subsystem.sftp.fs` package
+`org.apache.sshd.client.subsystem.sftp.fs` package.
+
+* `KeyPairProvider` accepts a `SessionContext` argument in its `getKeyTypes/loadKey` methods.
 
-* `KeyPairProvider` accepts a `SessionContext` argument in its `getKeyTypes/loadKey` methods
+* `KeyIdentityProvider` accepts a `SessionContext` argument in its `loadKeys` method.
 
-* `KeyIdentityProvider` accepts a `SessionContext` argument in its `loadKeys` method
+* `ClientIdentityProvider` accepts a `SessionContext` argument in its `getClientIdentity` method.
 
-* `ClientIdentityProvider` accepts a `SessionContext` argument in its `getClientIdentity` method
+* `ClientIdentityLoader` accepts a `SessionContext` argument in its `loadClientIdentity` method.
 
-* `ApacheSshdSftpSessionFactory#get/setPrivateKey` has been renamed to `get/setPrivateKeyLocation`
+* `ApacheSshdSftpSessionFactory#get/setPrivateKey` has been renamed to `get/setPrivateKeyLocation`.
 
 ## Behavioral changes and enhancements
 
 * [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all
 pending packets have been sent to the peer channel when closing the tunnel gracefully.
 
-* [SSHD-850](https://issues.apache.org/jira/browse/SSHD-850) - Add capability to retry a failed private key decryption
+* [SSHD-850](https://issues.apache.org/jira/browse/SSHD-850) - Add capability to retry a failed private key decryption.
 
-* [SSHD-857](https://issues.apache.org/jira/browse/SSHD-857) - Add session disconnect event signalling to SessionListener
+* [SSHD-857](https://issues.apache.org/jira/browse/SSHD-857) - Add session disconnect event signalling to SessionListener.
 
     * Also calling `ReservedSessionMessagesHandler#handleUnimplementedMessage` not only for `SSH_MSG_UNIMPLEMENTED` but
     also for any unexpected/unrecognized command encountered during the session message processing loop.
 
-* [SSHD-859](https://issues.apache.org/jira/browse/SSHD-859) - Provide client session connection context that is propagated to the SSH session
+* [SSHD-859](https://issues.apache.org/jira/browse/SSHD-859) - Provide client session connection context that is propagated to the SSH session.
 
     * Also added connection context argument (propagated from the `ClientSessionCreator#connect` invocation)
     to`connectionEstablished` and `abortEstablishedConnection` methods of `IoServiceEventListener`.
 
-* [SSHD-860](https://issues.apache.org/jira/browse/SSHD-860) - `UserAuthPublicKeyIterator` uses lazy loading of public key
-identities both from agent and client session
-
-    * Also using lazy identity `KeyPair`(s) loading in `ClientIdentitiesWatcher`
+* [SSHD-860](https://issues.apache.org/jira/browse/SSHD-860) - Use lazy loading of public key identities.
 
-* [SSHD-861](https://issues.apache.org/jira/browse/SSHD-861) - Fixed username/password encoding for `SftpFileSystem` URI(s)
+* [SSHD-861](https://issues.apache.org/jira/browse/SSHD-861) - Fixed username/password encoding for `SftpFileSystem` URI(s).
 
     * Also added `SftpFileSystemClientSessionInitializer` support in `SftpFileSystemProvider`
 
-* [SSHD-862](https://issues.apache.org/jira/browse/SSHD-862) - Provide session context argument when
-key loading methods are invoked
+* [SSHD-862](https://issues.apache.org/jira/browse/SSHD-862) - Provide session context argument (if available) when
+key loading methods are invoked.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 2978524..9286324 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -346,7 +346,7 @@ public abstract class SshClientCliSupport extends CliSupport {
     public static FileKeyPairProvider setupSessionIdentities(ClientFactoryManager client, Collection<? extends Path> identities,
             BufferedReader stdin, PrintStream stdout, PrintStream stderr)
                 throws Throwable {
-        client.setFilePasswordProvider((file, index) -> {
+        client.setFilePasswordProvider((session, file, index) -> {
             stdout.print("Enter password for private key file=" + file + ": ");
             return stdin.readLine();
         });

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 eac5b7e..f716553 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
@@ -116,7 +116,7 @@ public abstract class SshServerCliSupport extends CliSupport {
             for (String keyFilePath : keyFiles) {
                 Path path = Paths.get(keyFilePath);
                 try (InputStream inputStream = Files.newInputStream(path)) {
-                    KeyPair kp = SecurityUtils.loadKeyPairIdentity(keyFilePath, inputStream, null);
+                    KeyPair kp = SecurityUtils.loadKeyPairIdentity(null, keyFilePath, 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/2b013cca/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
index 13f5c34..c92cfc0 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
@@ -38,6 +38,7 @@ import org.apache.sshd.common.config.keys.IdentityUtils;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+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.FileInfoExtractor;
@@ -101,7 +102,7 @@ public final class ClientIdentity {
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                      to {@code FilePasswordProvider#getPassword} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
@@ -114,7 +115,7 @@ public final class ClientIdentity {
      */
     public static KeyPairProvider loadDefaultKeyPairProvider(
             boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
+                throws IOException, GeneralSecurityException {
         return loadDefaultKeyPairProvider(PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
     }
 
@@ -126,7 +127,7 @@ public final class ClientIdentity {
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                      to {@code FilePasswordProvider#getPassword} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
@@ -139,7 +140,7 @@ public final class ClientIdentity {
      */
     public static KeyPairProvider loadDefaultKeyPairProvider(
             Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
+                throws IOException, GeneralSecurityException {
         Map<String, KeyPair> ids = loadDefaultIdentities(dir, strict, provider, options);
         return IdentityUtils.createKeyPairProvider(ids, supportedOnly);
     }
@@ -149,7 +150,7 @@ public final class ClientIdentity {
      *                 access rights are excluded from consideration
      * @param provider A {@link FilePasswordProvider} - may be {@code null}
      *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                 to {@code FilePasswordProvider#getPassword} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link LinkOption}s to apply when checking
      *                 for existence
@@ -160,8 +161,9 @@ public final class ClientIdentity {
      * @see PublicKeyEntry#getDefaultKeysFolderPath()
      * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
      */
-    public static Map<String, KeyPair> loadDefaultIdentities(boolean strict, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
+    public static Map<String, KeyPair> loadDefaultIdentities(
+            boolean strict, FilePasswordProvider provider, LinkOption... options)
+                throws IOException, GeneralSecurityException {
         return loadDefaultIdentities(PublicKeyEntry.getDefaultKeysFolderPath(), strict, provider, options);
     }
 
@@ -171,7 +173,7 @@ public final class ClientIdentity {
      *                 access rights are excluded from consideration
      * @param provider A {@link FilePasswordProvider} - may be {@code null}
      *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                 to {@code FilePasswordProvider#getPassword} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link LinkOption}s to apply when checking
      *                 for existence
@@ -182,14 +184,17 @@ public final class ClientIdentity {
      * @see #loadIdentities(Path, boolean, Collection, Function, FilePasswordProvider, LinkOption...)
      * @see BuiltinIdentities
      */
-    public static Map<String, KeyPair> loadDefaultIdentities(Path dir, boolean strict, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        return loadIdentities(dir, strict, BuiltinIdentities.NAMES, ID_GENERATOR, provider, options);
+    public static Map<String, KeyPair> loadDefaultIdentities(
+            Path dir, boolean strict, FilePasswordProvider provider, LinkOption... options)
+                throws IOException, GeneralSecurityException {
+        return loadIdentities(null, dir, strict, BuiltinIdentities.NAMES, ID_GENERATOR, provider, options);
     }
 
     /**
      * Scans a folder and loads all available identity files
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param dir         The {@link Path} of the folder to scan - ignored if not exists
      * @param strict      If {@code true} then files that do not have the required
      *                    access rights are excluded from consideration
@@ -198,7 +203,7 @@ public final class ClientIdentity {
      *                    holding the specified type
      * @param provider    A {@link FilePasswordProvider} - may be {@code null}
      *                    if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                    to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                    to {@code FilePasswordProvider#getPassword} is the path of the
      *                    file whose key is to be loaded
      * @param options     The {@link LinkOption}s to apply when checking
      *                    for existence
@@ -210,10 +215,12 @@ public final class ClientIdentity {
      * @see IdentityUtils#loadIdentities(Map, FilePasswordProvider, java.nio.file.OpenOption...)
      */
     public static Map<String, KeyPair> loadIdentities(
-            Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, FilePasswordProvider provider, LinkOption... options)
+            SessionContext session, Path dir, boolean strict,
+            Collection<String> types, Function<? super String, String> idGenerator,
+            FilePasswordProvider provider, LinkOption... options)
             throws IOException, GeneralSecurityException {
         Map<String, Path> paths = scanIdentitiesFolder(dir, strict, types, idGenerator, options);
-        return IdentityUtils.loadIdentities(paths, provider, IoUtils.EMPTY_OPEN_OPTIONS);
+        return IdentityUtils.loadIdentities(session, paths, provider, IoUtils.EMPTY_OPEN_OPTIONS);
     }
 
     /**
@@ -233,8 +240,8 @@ public final class ClientIdentity {
      * @see KeyUtils#validateStrictKeyFilePermissions(Path, LinkOption...)
      */
     public static Map<String, Path> scanIdentitiesFolder(
-            Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, LinkOption... options)
-            throws IOException {
+            Path dir, boolean strict, Collection<String> types, Function<? super String, String> idGenerator, LinkOption... options)
+                throws IOException {
         if (GenericUtils.isEmpty(types)) {
             return Collections.emptyMap();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 411ae1b..e2e1b82 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
@@ -123,8 +123,7 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
         String location = path.toString();
         ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
         if (idLoader.isValidLocation(location)) {
-            KeyPair kp = idLoader.loadClientIdentity(
-                location, Objects.requireNonNull(getFilePasswordProvider(), "No file password provider"));
+            KeyPair kp = idLoader.loadClientIdentity(session, location, getFilePasswordProvider());
             if (log.isTraceEnabled()) {
                 PublicKey key = (kp == null) ? null : kp.getPublic();
                 if (key != null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 8b3a295..542697a 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
@@ -28,6 +28,7 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 
 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.security.SecurityUtils;
@@ -51,10 +52,12 @@ public interface ClientIdentityLoader {
         }
 
         @Override
-        public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+        public KeyPair loadClientIdentity(
+                SessionContext session, String location, FilePasswordProvider provider)
+                    throws IOException, GeneralSecurityException {
             Path path = toPath(location);
             try (InputStream inputStream = Files.newInputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
-                return SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
+                return SecurityUtils.loadKeyPairIdentity(session, path.toString(), inputStream, provider);
             }
         }
 
@@ -81,6 +84,8 @@ public interface ClientIdentityLoader {
     boolean isValidLocation(String location) throws IOException;
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool).
      * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
      * depends on the implementation.
      * @param provider The {@link FilePasswordProvider} to consult if the location contains
@@ -91,5 +96,7 @@ public interface ClientIdentityLoader {
      * @throws GeneralSecurityException If failed to convert the contents into
      * a valid identity
      */
-    KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException;
+    KeyPair loadClientIdentity(
+        SessionContext session, String location, FilePasswordProvider provider)
+            throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 54e8465..365de50 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,8 @@ package org.apache.sshd.common.config.keys;
 import java.io.IOException;
 import java.security.GeneralSecurityException;
 
+import org.apache.sshd.common.session.SessionContext;
+
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
@@ -39,9 +41,11 @@ public interface FilePasswordProvider {
     /**
      * An &quot;empty&quot; provider that returns {@code null} - i.e., unprotected key file
      */
-    FilePasswordProvider EMPTY = (resourceKey, retryIndex) -> null;
+    FilePasswordProvider EMPTY = (session, resourceKey, retryIndex) -> null;
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param resourceKey The resource key representing the <U>private</U> file
      * @param retryIndex The zero-based index of the invocation for the specific
      * resource (in case invoked several times for the same resource)
@@ -49,13 +53,15 @@ public interface FilePasswordProvider {
      * @throws IOException if cannot resolve password
      * @see #handleDecodeAttemptResult(String, int, String, Exception)
      */
-    String getPassword(String resourceKey, int retryIndex) throws IOException;
+    String getPassword(SessionContext session, String resourceKey, int retryIndex) throws IOException;
 
     /**
      * Invoked to inform the password provide about the decoding result. <b>Note:</b>
      * any exception thrown from this method (including if called to inform about
      * success) will be propagated instead of the original (if any was reported)
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param resourceKey The resource key representing the <U>private</U> file
      * @param retryIndex The zero-based index of the invocation for the specific
      * resource (in case invoked several times for the same resource). If success
@@ -68,12 +74,12 @@ public interface FilePasswordProvider {
      * @throws GeneralSecurityException If not attempting to resolve a new password
      */
     default ResourceDecodeResult handleDecodeAttemptResult(
-            String resourceKey, int retryIndex, String password, Exception err)
+            SessionContext session, String resourceKey, int retryIndex, String password, Exception err)
                 throws IOException, GeneralSecurityException {
         return ResourceDecodeResult.TERMINATE;
     }
 
     static FilePasswordProvider of(String password) {
-        return (r, index) -> password;
+        return (session, resource, index) -> password;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 9cf3859..6d9ab44 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
@@ -33,6 +33,7 @@ import java.util.TreeMap;
 
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 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.security.SecurityUtils;
@@ -123,11 +124,13 @@ public final class IdentityUtils {
     }
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param paths    A {@link Map} of the identities where key=identity type (case
      *                 <U>insensitive</U>), value=the {@link Path} of file with the identity key
      * @param provider A {@link FilePasswordProvider} - may be {@code null}
      *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
+     *                 to {@code FilePasswordProvider#getPassword} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link OpenOption}s to use when reading the key data
      * @return A {@link Map} of the identities where key=identity type (case
@@ -136,8 +139,9 @@ public final class IdentityUtils {
      * @throws GeneralSecurityException If failed to load the keys
      * @see SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)
      */
-    public static Map<String, KeyPair> loadIdentities(Map<String, ? extends Path> paths, FilePasswordProvider provider, OpenOption... options)
-            throws IOException, GeneralSecurityException {
+    public static Map<String, KeyPair> loadIdentities(
+            SessionContext session, Map<String, ? extends Path> paths, FilePasswordProvider provider, OpenOption... options)
+                throws IOException, GeneralSecurityException {
         if (GenericUtils.isEmpty(paths)) {
             return Collections.emptyMap();
         }
@@ -148,7 +152,7 @@ public final class IdentityUtils {
             String type = pe.getKey();
             Path path = pe.getValue();
             try (InputStream inputStream = Files.newInputStream(path, options)) {
-                KeyPair kp = SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
+                KeyPair kp = SecurityUtils.loadKeyPairIdentity(session, path.toString(), 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/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
index 8dc6113..12f6800 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
@@ -32,6 +32,7 @@ import java.util.Collection;
 import java.util.Objects;
 
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -45,17 +46,19 @@ public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends Priva
             extends KeyEntryResolver<PUB, PRV>, PrivateKeyEntryResolver {
 
     @Override
-    default PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+    default PrivateKey resolve(SessionContext session, String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
         ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
         Collection<String> supported = getSupportedTypeNames();
         if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
-            return decodePrivateKey(FilePasswordProvider.EMPTY, keyData);
+            return decodePrivateKey(session, FilePasswordProvider.EMPTY, keyData);
         }
 
         throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
     }
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param passwordProvider The {@link FilePasswordProvider} to use
      * in case the data is encrypted - may be {@code null} if no encrypted
      * data is expected
@@ -65,24 +68,27 @@ public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends Priva
      * @throws IOException              If failed to decode the key
      * @throws GeneralSecurityException If failed to generate the key
      */
-    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte... keyData)
-            throws IOException, GeneralSecurityException {
-        return decodePrivateKey(passwordProvider, keyData, 0, NumberUtils.length(keyData));
+    default PRV decodePrivateKey(
+            SessionContext session, FilePasswordProvider passwordProvider, byte... keyData)
+                throws IOException, GeneralSecurityException {
+        return decodePrivateKey(session, passwordProvider, keyData, 0, NumberUtils.length(keyData));
     }
 
-    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte[] keyData, int offset, int length)
-            throws IOException, GeneralSecurityException {
+    default PRV decodePrivateKey(
+            SessionContext session, FilePasswordProvider passwordProvider, byte[] keyData, int offset, int length)
+                throws IOException, GeneralSecurityException {
         if (length <= 0) {
             return null;
         }
 
         try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
-            return decodePrivateKey(passwordProvider, stream);
+            return decodePrivateKey(session, passwordProvider, stream);
         }
     }
 
-    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
+    default PRV decodePrivateKey(
+            SessionContext session, FilePasswordProvider passwordProvider, InputStream keyData)
+                throws IOException, GeneralSecurityException {
         // the actual data is preceded by a string that repeats the key type
         String type = KeyEntryResolver.decodeString(keyData, KeyPairResourceLoader.MAX_KEY_TYPE_NAME_LENGTH);
         if (GenericUtils.isEmpty(type)) {
@@ -94,10 +100,12 @@ public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends Priva
             throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
         }
 
-        return decodePrivateKey(type, passwordProvider, keyData);
+        return decodePrivateKey(session, type, passwordProvider, keyData);
     }
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param keyType The reported / encode key type
      * @param passwordProvider The {@link FilePasswordProvider} to use
      * in case the data is encrypted - may be {@code null} if no encrypted
@@ -108,7 +116,8 @@ public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends Priva
      * @throws IOException              If failed to read from the data stream
      * @throws GeneralSecurityException If failed to generate the key
      */
-    PRV decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+    PRV decodePrivateKey(
+        SessionContext session, String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
             throws IOException, GeneralSecurityException;
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
index 1e4c91e..581bef7 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
@@ -24,6 +24,8 @@ import java.security.GeneralSecurityException;
 import java.security.PrivateKey;
 import java.security.spec.InvalidKeySpecException;
 
+import org.apache.sshd.common.session.SessionContext;
+
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
@@ -34,7 +36,8 @@ public interface PrivateKeyEntryResolver {
      */
     PrivateKeyEntryResolver IGNORING = new PrivateKeyEntryResolver() {
         @Override
-        public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+        public PrivateKey resolve(SessionContext session, String keyType, byte[] keyData)
+                throws IOException, GeneralSecurityException {
             return null;
         }
 
@@ -49,7 +52,8 @@ public interface PrivateKeyEntryResolver {
      */
     PrivateKeyEntryResolver FAILING = new PrivateKeyEntryResolver() {
         @Override
-        public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+        public PrivateKey resolve(SessionContext session, String keyType, byte[] keyData)
+                throws IOException, GeneralSecurityException {
             throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
         }
 
@@ -60,11 +64,14 @@ public interface PrivateKeyEntryResolver {
     };
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param keyType The {@code OpenSSH} reported key type
      * @param keyData The {@code OpenSSH} encoded key data
      * @return The extracted {@link PrivateKey} - ignored if {@code null}
      * @throws IOException If failed to parse the key data
      * @throws GeneralSecurityException If failed to generate the key
      */
-    PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
+    PrivateKey resolve(SessionContext session, String keyType, byte[] keyData)
+        throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 e9ad24e..0b69906 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
@@ -34,6 +34,7 @@ import java.util.Map;
 import java.util.logging.Level;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+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.buffer.BufferUtils;
@@ -84,8 +85,9 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
     }
 
     @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-            throws IOException, GeneralSecurityException {
+    public Collection<KeyPair> loadKeyPairs(
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                throws IOException, GeneralSecurityException {
         Collection<KeyPair> keyPairs = Collections.emptyList();
         List<String> beginMarkers = getBeginners();
         List<List<String>> endMarkers = getEndingMarkers();
@@ -103,8 +105,8 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
 
             int endIndex = markerPos.getKey();
             String endLine = lines.get(endIndex);
-            Collection<KeyPair> kps =
-                extractKeyPairs(resourceKey, startLine, endLine, passwordProvider, lines.subList(startIndex, endIndex));
+            Collection<KeyPair> kps = extractKeyPairs(session, resourceKey,
+                startLine, endLine, passwordProvider, lines.subList(startIndex, endIndex));
             if (GenericUtils.isNotEmpty(kps)) {
                 if (GenericUtils.isEmpty(keyPairs)) {
                     keyPairs = new LinkedList<>(kps);
@@ -124,6 +126,8 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * Extracts the key pairs within a <U>single</U> delimited by markers block of lines. By
      * default cleans up the empty lines, joins them and converts them from BASE64
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param resourceKey A hint as to the origin of the text lines
      * @param beginMarker The line containing the begin marker
      * @param endMarker The line containing the end marker
@@ -136,12 +140,17 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * @see #extractKeyPairs(String, String, String, FilePasswordProvider, byte[])
      */
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            List<String> lines)
                 throws IOException, GeneralSecurityException {
-        return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
+        return extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
     }
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param resourceKey A hint as to the origin of the text lines
      * @param beginMarker The line containing the begin marker
      * @param endMarker The line containing the end marker
@@ -154,18 +163,23 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * @see #extractKeyPairs(String, String, String, FilePasswordProvider, InputStream)
      */
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, byte[] bytes)
-                    throws IOException, GeneralSecurityException {
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            byte[] bytes)
+                throws IOException, GeneralSecurityException {
         if (log.isTraceEnabled()) {
             BufferUtils.dumpHex(getSimplifiedLogger(), Level.FINER, beginMarker, ':', 16, bytes);
         }
 
         try (InputStream bais = new ByteArrayInputStream(bytes)) {
-            return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+            return extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, bais);
         }
     }
 
     /**
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param resourceKey A hint as to the origin of the text lines
      * @param beginMarker The line containing the begin marker
      * @param endMarker The line containing the end marker
@@ -177,6 +191,8 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * @throws GeneralSecurityException If failed to generate the keys
      */
     public abstract Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException;
+        SessionContext session, String resourceKey,
+        String beginMarker, String endMarker,
+        FilePasswordProvider passwordProvider, InputStream stream)
+            throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 00c53cf..5bf2597 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
@@ -39,6 +39,7 @@ import java.util.List;
 import java.util.Objects;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.io.IoUtils;
 
 /**
@@ -57,74 +58,95 @@ public interface KeyPairResourceLoader {
     /**
      * An empty loader that never fails but always returns an empty list
      */
-    KeyPairResourceLoader EMPTY = (resourceKey, passwordProvider, lines) -> Collections.emptyList();
+    KeyPairResourceLoader EMPTY = (session, resourceKey, passwordProvider, lines) -> Collections.emptyList();
 
+    /**
+     * Loads private key data - <B>Note:</B> any non-ASCII characters are assumed to be UTF-8 encoded
+     *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
+     * @param path The private key file {@link Path}
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * data is expected
+     * @param options The {@link OpenOption}-s to use to access the file data
+     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+     * <B>Note:</B> the resource loader may decide to skip unknown lines if
+     * more than one key pair type is encoded in it
+     * @throws IOException If failed to process the lines
+     * @throws GeneralSecurityException If failed to generate the keys from the
+     * parsed data
+     */
     default Collection<KeyPair> loadKeyPairs(
-            Path path, FilePasswordProvider passwordProvider, OpenOption... options)
+            SessionContext session, Path path, FilePasswordProvider passwordProvider, OpenOption... options)
                 throws IOException, GeneralSecurityException {
-        return loadKeyPairs(path, passwordProvider, StandardCharsets.UTF_8, options);
+        return loadKeyPairs(session, path, passwordProvider, StandardCharsets.UTF_8, options);
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
+            SessionContext session, Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
                 throws IOException, GeneralSecurityException {
         try (InputStream stream = Files.newInputStream(path, options)) {
-            return loadKeyPairs(path.toString(), passwordProvider, stream, cs);
+            return loadKeyPairs(session, path.toString(), passwordProvider, stream, cs);
         }
     }
 
-    default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider)
-            throws IOException, GeneralSecurityException {
-        return loadKeyPairs(url, passwordProvider, StandardCharsets.UTF_8);
+    default Collection<KeyPair> loadKeyPairs(
+            SessionContext session, URL url, FilePasswordProvider passwordProvider)
+                throws IOException, GeneralSecurityException {
+        return loadKeyPairs(session, url, passwordProvider, StandardCharsets.UTF_8);
     }
 
-    default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider, Charset cs)
-            throws IOException, GeneralSecurityException {
+    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(url.toExternalForm(), passwordProvider, stream, cs);
+            return loadKeyPairs(session, url.toExternalForm(), passwordProvider, stream, cs);
         }
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, String data)
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, String data)
                 throws IOException, GeneralSecurityException {
         try (Reader reader = new StringReader((data == null) ? "" : data)) {
-            return loadKeyPairs(resourceKey, passwordProvider, reader);
+            return loadKeyPairs(session, resourceKey, passwordProvider, reader);
         }
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
                 throws IOException, GeneralSecurityException {
-        return loadKeyPairs(resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
+        return loadKeyPairs(session, resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
                 throws IOException, GeneralSecurityException {
         try (Reader reader = new InputStreamReader(
                 Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
-            return loadKeyPairs(resourceKey, passwordProvider, reader);
+            return loadKeyPairs(session, resourceKey, passwordProvider, reader);
         }
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, Reader r)
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, Reader r)
                 throws IOException, GeneralSecurityException {
         try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
-            return loadKeyPairs(resourceKey, passwordProvider, br);
+            return loadKeyPairs(session, resourceKey, passwordProvider, br);
         }
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
+            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
                 throws IOException, GeneralSecurityException {
-        return loadKeyPairs(resourceKey, passwordProvider, IoUtils.readAllLines(r));
+        return loadKeyPairs(session, resourceKey, passwordProvider, IoUtils.readAllLines(r));
     }
 
     /**
      * Loads key pairs from the given resource text lines
      *
+     * @param session The {@link SessionContext} for invoking this load command - may
+     * be {@code null} if not invoked within a session context (e.g., offline tool or session unknown).
      * @param resourceKey A hint as to the origin of the text lines
      * @param passwordProvider The {@link FilePasswordProvider} to use
      * in case the data is encrypted - may be {@code null} if no encrypted
@@ -137,6 +159,7 @@ public interface KeyPairResourceLoader {
      * @throws GeneralSecurityException If failed to generate the keys from the
      * parsed data
      */
-    Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+    Collection<KeyPair> loadKeyPairs(
+        SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
             throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 f7e336a..c434f0a 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
@@ -31,6 +31,7 @@ import java.util.LinkedList;
 import java.util.List;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -45,7 +46,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
     KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
         @Override
         public Collection<KeyPair> loadKeyPairs(
-                String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
                     throws IOException, GeneralSecurityException {
             return Collections.emptyList();
         }
@@ -153,7 +154,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
         return new KeyPairResourceParser() {
             @Override
             public Collection<KeyPair> loadKeyPairs(
-                    String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                    SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
                         throws IOException, GeneralSecurityException {
                 Collection<KeyPair> keyPairs = Collections.emptyList();
                 for (KeyPairResourceParser p : parsers) {
@@ -161,7 +162,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
                         continue;
                     }
 
-                    Collection<KeyPair> kps = p.loadKeyPairs(resourceKey, passwordProvider, lines);
+                    Collection<KeyPair> kps = p.loadKeyPairs(session, resourceKey, passwordProvider, lines);
                     if (GenericUtils.isEmpty(kps)) {
                         continue;
                     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
index 6188a04..fc87175 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
@@ -41,6 +41,7 @@ import org.apache.sshd.common.config.keys.KeyEntryResolver;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -54,8 +55,9 @@ public class OpenSSHDSSPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDec
     }
 
     @Override
-    public DSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
+    public DSAPrivateKey decodePrivateKey(
+            SessionContext session, String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+                throws IOException, GeneralSecurityException {
         if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
             throw new InvalidKeySpecException("Unexpected key type: " + keyType);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
index 83a0fb3..dfe01f6 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
@@ -43,6 +43,7 @@ import org.apache.sshd.common.config.keys.KeyEntryResolver;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -56,8 +57,9 @@ public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryD
     }
 
     @Override
-    public ECPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
+    public ECPrivateKey decodePrivateKey(
+            SessionContext session, String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+                throws IOException, GeneralSecurityException {
         ECCurves curve = ECCurves.fromKeyType(keyType);
         if (curve == null) {
             throw new InvalidKeySpecException("Not an EC curve name: " + keyType);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 d531021..c82957d 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
@@ -48,6 +48,7 @@ import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -98,9 +99,12 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
-        stream = validateStreamMagicMarker(resourceKey, stream);
+        stream = validateStreamMagicMarker(session, resourceKey, stream);
 
         String cipher = KeyEntryResolver.decodeString(stream, MAX_CIPHER_NAME_LENGTH);
         if (!OpenSSHParserContext.IS_NONE_CIPHER.test(cipher)) {
@@ -135,7 +139,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
         OpenSSHParserContext context = new OpenSSHParserContext(cipher, kdfName, kdfOptions);
         boolean traceEnabled = log.isTraceEnabled();
         for (int index = 1; index <= numKeys; index++) {
-            PublicKey pubKey = readPublicKey(resourceKey, context, stream);
+            PublicKey pubKey = readPublicKey(session, resourceKey, context, stream);
             ValidateUtils.checkNotNull(pubKey, "Empty public key #%d in %s", index, resourceKey);
             if (traceEnabled) {
                 log.trace("extractKeyPairs({}) read public key #{}: {} {}",
@@ -146,13 +150,13 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
 
         byte[] privateData = KeyEntryResolver.readRLEBytes(stream, MAX_PRIVATE_KEY_DATA_SIZE);
         try (InputStream bais = new ByteArrayInputStream(privateData)) {
-            return readPrivateKeys(resourceKey, context, publicKeys, passwordProvider, bais);
+            return readPrivateKeys(session, resourceKey, context, publicKeys, passwordProvider, bais);
         }
     }
 
     protected PublicKey readPublicKey(
-            String resourceKey, OpenSSHParserContext context, InputStream stream)
-                    throws IOException, GeneralSecurityException {
+            SessionContext session, String resourceKey, OpenSSHParserContext context, InputStream stream)
+                throws IOException, GeneralSecurityException {
         byte[] keyData = KeyEntryResolver.readRLEBytes(stream, MAX_PUBLIC_KEY_DATA_SIZE);
         try (InputStream bais = new ByteArrayInputStream(keyData)) {
             String keyType = KeyEntryResolver.decodeString(bais, MAX_KEY_TYPE_NAME_LENGTH);
@@ -166,7 +170,8 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
     }
 
     protected List<KeyPair> readPrivateKeys(
-            String resourceKey, OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
+            SessionContext session, String resourceKey,
+            OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
             FilePasswordProvider passwordProvider, InputStream stream)
                 throws IOException, GeneralSecurityException {
         if (GenericUtils.isEmpty(publicKeys)) {
@@ -190,7 +195,8 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
                     resourceKey, keyIndex, pubType);
             }
 
-            Map.Entry<PrivateKey, String> prvData = readPrivateKey(resourceKey, context, pubType, passwordProvider, stream);
+            Map.Entry<PrivateKey, String> prvData =
+                readPrivateKey(session, resourceKey, context, pubType, passwordProvider, stream);
             PrivateKey prvKey = (prvData == null) ? null : prvData.getKey();
             ValidateUtils.checkNotNull(prvKey, "Empty private key #%d in %s", keyIndex, resourceKey);
 
@@ -210,7 +216,9 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
     }
 
     protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
-            String resourceKey, OpenSSHParserContext context, String keyType, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            OpenSSHParserContext context, String keyType,
+            FilePasswordProvider passwordProvider, InputStream stream)
                 throws IOException, GeneralSecurityException {
         String prvType = KeyEntryResolver.decodeString(stream, MAX_KEY_TYPE_NAME_LENGTH);
         if (!Objects.equals(keyType, prvType)) {
@@ -224,7 +232,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
             throw new NoSuchAlgorithmException("Unsupported key type (" + prvType + ") in " + resourceKey);
         }
 
-        PrivateKey prvKey = decoder.decodePrivateKey(prvType, passwordProvider, stream);
+        PrivateKey prvKey = decoder.decodePrivateKey(session, prvType, passwordProvider, stream);
         if (prvKey == null) {
             throw new InvalidKeyException("Cannot parse key type (" + prvType + ") in " + resourceKey);
         }
@@ -233,7 +241,9 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
         return new SimpleImmutableEntry<>(prvKey, comment);
     }
 
-    protected <S extends InputStream> S validateStreamMagicMarker(String resourceKey, S stream) throws IOException {
+    protected <S extends InputStream> S validateStreamMagicMarker(
+            SessionContext session, String resourceKey, S stream)
+                throws IOException {
         byte[] actual = new byte[AUTH_MAGIC_BYTES.length];
         IoUtils.readFully(stream, actual);
         if (!Arrays.equals(AUTH_MAGIC_BYTES, actual)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
index 72e003f..b77257d 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
@@ -41,6 +41,7 @@ import org.apache.sshd.common.config.keys.KeyEntryResolver;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -55,8 +56,9 @@ public class OpenSSHRSAPrivateKeyDecoder extends AbstractPrivateKeyEntryDecoder<
     }
 
     @Override
-    public RSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
+    public RSAPrivateKey decodePrivateKey(
+            SessionContext session, String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+                throws IOException, GeneralSecurityException {
         if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
             throw new InvalidKeySpecException("Unexpected key type: " + keyType);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 6ac9e2f..a324339 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
@@ -39,6 +39,7 @@ import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
 import org.apache.sshd.common.config.keys.loader.PrivateKeyEncryptionContext;
 import org.apache.sshd.common.config.keys.loader.PrivateKeyObfuscator;
+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.buffer.BufferUtils;
@@ -73,7 +74,10 @@ public abstract class AbstractPEMResourceKeyPairParser
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            List<String> lines)
                 throws IOException, GeneralSecurityException {
         if (GenericUtils.isEmpty(lines)) {
             return Collections.emptyList();
@@ -133,7 +137,7 @@ public abstract class AbstractPEMResourceKeyPairParser
             }
 
             for (int retryIndex = 0;; retryIndex++) {
-                String password = passwordProvider.getPassword(resourceKey, retryIndex);
+                String password = passwordProvider.getPassword(session, resourceKey, retryIndex);
                 Collection<KeyPair> keys;
                 try {
                     if (GenericUtils.isEmpty(password)) {
@@ -146,11 +150,11 @@ public abstract class AbstractPEMResourceKeyPairParser
                     byte[] encryptedData = KeyPairResourceParser.extractDataBytes(dataLines);
                     byte[] decodedData = applyPrivateKeyCipher(encryptedData, encContext, false);
                     try (InputStream bais = new ByteArrayInputStream(decodedData)) {
-                        keys = extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+                        keys = extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, bais);
                     }
                 } catch (IOException | GeneralSecurityException | RuntimeException e) {
                     ResourceDecodeResult result =
-                        passwordProvider.handleDecodeAttemptResult(resourceKey, retryIndex, password, e);
+                        passwordProvider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, e);
                     if (result == null) {
                         result = ResourceDecodeResult.TERMINATE;
                     }
@@ -166,12 +170,12 @@ public abstract class AbstractPEMResourceKeyPairParser
                     }
                 }
 
-                passwordProvider.handleDecodeAttemptResult(resourceKey, retryIndex, password, null);
+                passwordProvider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, null);
                 return keys;
             }
         }
 
-        return super.extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
+        return super.extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
     }
 
     protected byte[] applyPrivateKeyCipher(

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 c68a815..39c84a6 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
@@ -36,6 +36,7 @@ import java.util.List;
 
 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.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.der.ASN1Object;
 import org.apache.sshd.common.util.io.der.ASN1Type;
@@ -68,7 +69,10 @@ public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParse
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
         KeyPair kp = decodeDSSKeyPair(SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM), stream, false);
         return Collections.singletonList(kp);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 768c30f..19c15e2 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
@@ -41,6 +41,7 @@ import java.util.Map;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.der.ASN1Object;
 import org.apache.sshd.common.util.io.der.ASN1Type;
@@ -72,7 +73,10 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
         Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
         if (!SecurityUtils.isECCSupported()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 b6749da..d3aba1e 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
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -43,11 +44,11 @@ public final class PEMResourceParserUtils {
     public static final KeyPairResourceParser PROXY = new KeyPairResourceParser() {
         @Override
         public Collection<KeyPair> loadKeyPairs(
-                String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-                        throws IOException, GeneralSecurityException {
+                SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                    throws IOException, GeneralSecurityException {
             @SuppressWarnings("synthetic-access")
             KeyPairResourceParser proxy = PROXY_HOLDER.get();
-            return (proxy == null) ? Collections.<KeyPair>emptyList() : proxy.loadKeyPairs(resourceKey, passwordProvider, lines);
+            return (proxy == null) ? Collections.emptyList() : proxy.loadKeyPairs(session, resourceKey, passwordProvider, lines);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 1967719..90f033b 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
@@ -35,6 +35,7 @@ import java.util.List;
 
 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.IoUtils;
@@ -65,27 +66,28 @@ public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
         // Save the data before getting the algorithm OID since we will need it
         byte[] encBytes = IoUtils.toByteArray(stream);
         List<Integer> oidAlgorithm = getPKCS8AlgorithmIdentifier(encBytes);
-        PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes, passwordProvider);
+        PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes);
         PublicKey pubKey = ValidateUtils.checkNotNull(KeyUtils.recoverPublicKey(prvKey),
                 "Failed to recover public key of OID=%s", oidAlgorithm);
         KeyPair kp = new KeyPair(pubKey, prvKey);
         return Collections.singletonList(kp);
     }
 
-    public static PrivateKey decodePEMPrivateKeyPKCS8(
-            List<Integer> oidAlgorithm, byte[] keyBytes, FilePasswordProvider passwordProvider)
-                throws GeneralSecurityException {
+    public static PrivateKey decodePEMPrivateKeyPKCS8(List<Integer> oidAlgorithm, byte[] keyBytes)
+            throws GeneralSecurityException {
         ValidateUtils.checkNotNullAndNotEmpty(oidAlgorithm, "No PKCS8 algorithm OID");
-        return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes, passwordProvider);
+        return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes);
     }
 
-    public static PrivateKey decodePEMPrivateKeyPKCS8(
-            String oid, byte[] keyBytes, FilePasswordProvider passwordProvider)
+    public static PrivateKey decodePEMPrivateKeyPKCS8(String oid, byte[] keyBytes)
                 throws GeneralSecurityException {
         KeyPairPEMResourceParser parser =
             PEMResourceParserUtils.getPEMResourceParserByOid(

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 988c210..8badd63 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
@@ -37,6 +37,7 @@ import java.util.List;
 
 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.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.der.ASN1Object;
 import org.apache.sshd.common.util.io.der.ASN1Type;
@@ -69,7 +70,10 @@ public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParse
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, String resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            InputStream stream)
                 throws IOException, GeneralSecurityException {
         KeyPair kp = decodeRSAKeyPair(SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM), stream, false);
         return Collections.singletonList(kp);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 194daf5..7fbee1b 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
@@ -166,16 +166,17 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
 
     protected KeyPair doLoadKey(SessionContext session, String resourceKey, R resource, FilePasswordProvider provider)
             throws IOException, GeneralSecurityException {
-        try (InputStream inputStream = openKeyPairResource(resourceKey, resource)) {
+        try (InputStream inputStream = openKeyPairResource(session, resourceKey, resource)) {
             return doLoadKey(session, resourceKey, inputStream, provider);
         }
     }
 
-    protected abstract InputStream openKeyPairResource(String resourceKey, R resource) throws IOException;
+    protected abstract InputStream openKeyPairResource(SessionContext session, String resourceKey, R resource) throws IOException;
 
-    protected KeyPair doLoadKey(SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
-        return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, provider);
+    protected KeyPair doLoadKey(
+            SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+                throws IOException, GeneralSecurityException {
+        return SecurityUtils.loadKeyPairIdentity(session, resourceKey, inputStream, provider);
     }
 
     protected class KeyPairIterator implements Iterator<KeyPair> {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2b013cca/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 3132abd..54b4e30 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
@@ -90,7 +90,9 @@ public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPai
     }
 
     @Override
-    protected InputStream openKeyPairResource(String resourceKey, String resource) throws IOException {
+    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);


[07/12] mina-sshd git commit: [SSHD-862] Re-factored ApacheSshdSftpSessionFactory to lazy-load the private key (if any) only after session established

Posted by lg...@apache.org.
[SSHD-862] Re-factored ApacheSshdSftpSessionFactory to lazy-load the private key (if any) only after session established


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

Branch: refs/heads/master
Commit: 20be0e9d05427ae65e3ea5e2f61205fe69ff9def
Parents: 210f638
Author: Lyor Goldstein <lg...@apache.org>
Authored: Wed Nov 14 07:42:17 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |   2 +
 .../keys/FilePasswordProviderManager.java       |  36 +++++
 .../sshd/client/ClientFactoryManager.java       |  12 +-
 .../sftp/ApacheSshdSftpSessionFactory.java      | 142 +++++++++++++------
 4 files changed, 142 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/20be0e9d/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index a07e953..57d53f6 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -46,6 +46,8 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 
 * `ClientIdentityProvider` accepts a `SessionContext` argument in its `getClientIdentity` method
 
+* `ApacheSshdSftpSessionFactory#get/setPrivateKey` has been renamed to `get/setPrivateKeyLocation`
+
 ## Behavioral changes and enhancements
 
 * [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/20be0e9d/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderManager.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderManager.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderManager.java
new file mode 100644
index 0000000..aae151c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderManager.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface FilePasswordProviderManager {
+    /**
+     * @return The {@link FilePasswordProvider} to use if need to load encrypted
+     * identities keys - never {@code null}
+     * @see FilePasswordProvider#EMPTY
+     */
+    FilePasswordProvider getFilePasswordProvider();
+
+    void setFilePasswordProvider(FilePasswordProvider provider);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/20be0e9d/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
index 26ca4bd..35b42b1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
@@ -22,7 +22,7 @@ import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
 import org.apache.sshd.client.config.keys.ClientIdentityLoader;
 import org.apache.sshd.client.session.ClientProxyConnectorHolder;
 import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProviderManager;
 
 /**
  * The <code>ClientFactoryManager</code> enable the retrieval of additional
@@ -33,6 +33,7 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider;
 public interface ClientFactoryManager
         extends FactoryManager,
                 ClientProxyConnectorHolder,
+                FilePasswordProviderManager,
                 ClientAuthenticationManager {
 
     /**
@@ -91,13 +92,4 @@ public interface ClientFactoryManager
     ClientIdentityLoader getClientIdentityLoader();
 
     void setClientIdentityLoader(ClientIdentityLoader loader);
-
-    /**
-     * @return The {@link FilePasswordProvider} to use if need to load encrypted
-     * identities keys - never {@code null}
-     * @see FilePasswordProvider#EMPTY
-     */
-    FilePasswordProvider getFilePasswordProvider();
-
-    void setFilePasswordProvider(FilePasswordProvider provider);
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/20be0e9d/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 3c0a65c..cbd6f22 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
@@ -19,7 +19,9 @@
 
 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;
 import java.util.Collection;
@@ -40,6 +42,7 @@ import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.auth.MutableBasicCredentials;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProviderManager;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
 import org.apache.sshd.common.future.CloseFuture;
@@ -63,12 +66,9 @@ import org.springframework.integration.file.remote.session.SharedSessionCapable;
 public class ApacheSshdSftpSessionFactory
         extends AbstractLoggingBean
         implements SessionFactory<DirEntry>, SharedSessionCapable,
-        MutableBasicCredentials, SimpleClientConfigurator,
+        MutableBasicCredentials, FilePasswordProviderManager, SimpleClientConfigurator,
         InitializingBean, DisposableBean {
 
-    // TODO add support for loading multiple private keys
-    protected volatile KeyPair privateKeyPair;
-
     private final boolean sharedSession;
     private final AtomicReference<ClientSession> sharedSessionHolder = new AtomicReference<>();
 
@@ -76,8 +76,11 @@ public class ApacheSshdSftpSessionFactory
     private volatile int portValue = SshConstants.DEFAULT_PORT;
     private volatile String userValue;
     private volatile String passwordValue;
+    // TODO add support for loading multiple private keys
+    private volatile KeyPair privateKeyPair;
     private volatile Resource privateKey;
     private volatile String privateKeyPassphrase;
+    private volatile FilePasswordProvider passwordProvider;
     private volatile Properties sessionConfig;
     private volatile long connTimeout = DEFAULT_CONNECT_TIMEOUT;
     private volatile long authTimeout = DEFAULT_AUTHENTICATION_TIMEOUT;
@@ -142,17 +145,17 @@ public class ApacheSshdSftpSessionFactory
 
     /**
      * The password to authenticate against the remote host. If a password is
-     * not provided, then a {@link #setPrivateKey(Resource)} call is mandatory.
+     * not provided, then a {@link #setPrivateKeyLocation(Resource)} call is mandatory.
      *
      * @param password The password to use - if {@code null} then no password
-     * is set - in which case the {@link #getPrivateKey()} resource is used
+     * is set - in which case the {@link #getPrivateKeyLocation()} resource is used
      */
     @Override
     public void setPassword(String password) {
         this.passwordValue = password;
     }
 
-    public Resource getPrivateKey() {
+    public Resource getPrivateKeyLocation() {
         return privateKey;
     }
 
@@ -163,7 +166,7 @@ public class ApacheSshdSftpSessionFactory
      *
      * @param privateKey The private key {@link Resource}
      */
-    public void setPrivateKey(Resource privateKey) {
+    public void setPrivateKeyLocation(Resource privateKey) {
         this.privateKey = privateKey;
     }
 
@@ -179,6 +182,16 @@ public class ApacheSshdSftpSessionFactory
         this.privateKeyPassphrase = privateKeyPassphrase;
     }
 
+    @Override
+    public FilePasswordProvider getFilePasswordProvider() {
+        return passwordProvider;
+    }
+
+    @Override
+    public void setFilePasswordProvider(FilePasswordProvider provider) {
+        this.passwordProvider = provider;
+    }
+
     public KeyPair getPrivateKeyPair() {
         return privateKeyPair;
     }
@@ -268,11 +281,12 @@ public class ApacheSshdSftpSessionFactory
         synchronized (sharedSessionHolder) {
             sharedSession = sharedSessionHolder.getAndSet(null);
         }
+
         if (sharedSession != null) {
             log.info("resetSharedSession - session={}", sharedSession);
             sharedSession.close(false).addListener(new SshFutureListener<CloseFuture>() {
-                @SuppressWarnings("synthetic-access")
                 @Override
+                @SuppressWarnings("synthetic-access")
                 public void operationComplete(CloseFuture future) {
                     log.info("resetSharedSession - session closed: {}", sharedSession);
                 }
@@ -283,15 +297,10 @@ public class ApacheSshdSftpSessionFactory
     @Override
     public void afterPropertiesSet() throws Exception {
         KeyPair kp = getPrivateKeyPair();
-        if (kp == null) {
-            Resource privateKeyLocation = getPrivateKey();
-            if (privateKeyLocation != null) {
-                kp = loadPrivateKey(privateKeyLocation, getPrivateKeyPassphrase());
-                log.info("afterPropertiesSet() - loaded private key={}", privateKeyLocation);
-                setPrivateKeyPair(kp);
-            }
-        }
-        ValidateUtils.checkState(GenericUtils.isNotEmpty(getPassword()) || (kp != null), "Either password or private key must be set");
+        Resource privateKeyLocation = getPrivateKeyLocation();
+        ValidateUtils.checkState(
+            GenericUtils.isNotEmpty(getPassword()) || (kp != null) || (privateKeyLocation != null),
+            "Either password or private key must be provided");
 
         SshClient client = getSshClient();
         if (client == null) {
@@ -320,19 +329,66 @@ public class ApacheSshdSftpSessionFactory
         }
     }
 
-    protected KeyPair loadPrivateKey(Resource keyResource, String keyPassword) throws Exception {
-        FilePasswordProvider passwordProvider = GenericUtils.isEmpty(keyPassword)
-                ? FilePasswordProvider.EMPTY
-                : FilePasswordProvider.of(keyPassword);
+    protected KeyPair resolveKeyIdentity(ClientSession session) throws IOException, GeneralSecurityException {
+        KeyPair kp = getPrivateKeyPair();
+        if (kp != null) {
+            return kp;
+        }
+
+        Resource location = getPrivateKeyLocation();
+        if (location == null) {
+            return null;
+        }
+
+        kp = loadPrivateKey(session, location, getPrivateKeyPassphrase());
+        if (kp != null) {
+            setPrivateKeyPair(kp);  // cache it for re-use
+        }
+
+        return kp;
+    }
+
+    protected FilePasswordProvider resolveFilePasswordProvider(ClientSession session, Resource keyResource, String keyPassword) {
+        FilePasswordProvider provider = getFilePasswordProvider();
+        if (provider != null) {
+            return provider;
+        }
+
+        return GenericUtils.isEmpty(keyPassword)
+             ? FilePasswordProvider.EMPTY
+             : FilePasswordProvider.of(keyPassword);
+    }
+
+    protected KeyPair loadPrivateKey(ClientSession session, Resource keyResource, String keyPassword)
+            throws IOException, GeneralSecurityException {
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("loadPrivateKey({}) loading from {}", session, keyResource);
+        }
+
+        FilePasswordProvider provider = resolveFilePasswordProvider(session, keyResource, keyPassword);
         Collection<KeyPair> keyPairs;
         try (InputStream inputStream = keyResource.getInputStream()) {
-            keyPairs = PEMResourceParserUtils.PROXY.loadKeyPairs(keyResource.toString(), passwordProvider, inputStream);
+            keyPairs = PEMResourceParserUtils.PROXY.loadKeyPairs(keyResource.toString(), provider, inputStream);
         }
 
         int numLoaded = GenericUtils.size(keyPairs);
-        ValidateUtils.checkState(numLoaded > 0, "No keys loaded from %s", keyResource);
+        if (numLoaded <= 0) {
+            if (debugEnabled) {
+                log.debug("loadPrivateKey({}) no keys loaded from {}", session, keyResource);
+            }
+        }
+
+        // TODO add support for multiple keys
         ValidateUtils.checkState(numLoaded == 1, "Multiple keys loaded from %s", keyResource);
-        return keyPairs.iterator().next();
+
+        KeyPair kp = GenericUtils.head(keyPairs);
+        if (debugEnabled) {
+            PublicKey pubKey = kp.getPublic();
+            log.debug("loadPrivateKey({}) loaded {} key={} from {}",
+                session, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey), keyResource);
+        }
+        return kp;
     }
 
     @Override
@@ -344,7 +400,8 @@ public class ApacheSshdSftpSessionFactory
                 session = resolveClientSession(sharedInstance);
 
                 SftpVersionSelector selector = getSftpVersionSelector();
-                SftpClient sftpClient = SftpClientFactory.instance().createSftpClient(session, selector);
+                SftpClientFactory sftpFactory = SftpClientFactory.instance();
+                SftpClient sftpClient = sftpFactory.createSftpClient(session, selector);
                 try {
                     ClientSession sessionInstance = session;
                     Session<DirEntry> result = sharedInstance
@@ -402,14 +459,10 @@ public class ApacheSshdSftpSessionFactory
     protected ClientSession createClientSession() throws Exception {
         String hostname = ValidateUtils.checkNotNullAndNotEmpty(getHost(), "Host must not be empty");
         String username = ValidateUtils.checkNotNullAndNotEmpty(getUsername(), "User must not be empty");
-        String passwordIdentity = getPassword();
-        KeyPair kp = getPrivateKeyPair();
-        ValidateUtils.checkState(GenericUtils.isNotEmpty(passwordIdentity) || (kp != null),
-                "Either password or private key must be set");
         ClientSession session = createClientSession(hostname, username, getPort(), getEffectiveTimeoutValue(getConnectTimeout()));
         try {
             session = configureClientSessionProperties(session, getSessionConfig());
-            session = authenticateClientSession(session, passwordIdentity, kp, getEffectiveTimeoutValue(getAuthenticationTimeout()));
+            session = authenticateClientSession(session, getEffectiveTimeoutValue(getAuthenticationTimeout()));
 
             ClientSession newSession = session;
             if (log.isDebugEnabled()) {
@@ -424,7 +477,7 @@ public class ApacheSshdSftpSessionFactory
         }
     }
 
-    protected ClientSession createClientSession(String hostname, String username, int port, long timeout) throws Exception {
+    protected ClientSession createClientSession(String hostname, String username, int port, long timeout) throws IOException {
         SshClient client = getSshClient();
         if (log.isDebugEnabled()) {
             log.debug("createClientSession({}@{}:{}) waitTimeout={}", username, hostname, port, timeout);
@@ -433,7 +486,7 @@ public class ApacheSshdSftpSessionFactory
         return connectFuture.verify(timeout).getSession();
     }
 
-    protected ClientSession configureClientSessionProperties(ClientSession session, Properties props) throws Exception {
+    protected ClientSession configureClientSessionProperties(ClientSession session, Properties props) throws IOException {
         if (GenericUtils.isEmpty(props)) {
             return session;
         }
@@ -450,22 +503,31 @@ public class ApacheSshdSftpSessionFactory
         return session;
     }
 
-    protected ClientSession authenticateClientSession(
-            ClientSession session, String passwordIdentity, KeyPair privateKeyIdentity, long timeout) throws Exception {
-        if (log.isDebugEnabled()) {
-            PublicKey key = (privateKeyIdentity == null) ? null : privateKeyIdentity.getPublic();
-            log.debug("authenticateClientSession({}) password?={}, key={}/{}",
-                    session, GenericUtils.isNotEmpty(passwordIdentity), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-        }
+    protected ClientSession authenticateClientSession(ClientSession session, long timeout)
+            throws IOException, GeneralSecurityException {
+        boolean debugEnabled = log.isDebugEnabled();
 
+        String passwordIdentity = getPassword();
         if (GenericUtils.isNotEmpty(passwordIdentity)) {
+            if (debugEnabled) {
+                log.debug("authenticateClientSession({}) using password identity", session);
+            }
             session.addPasswordIdentity(passwordIdentity);
         }
 
+        KeyPair privateKeyIdentity = resolveKeyIdentity(session);
         if (privateKeyIdentity != null) {
+            if (debugEnabled) {
+                PublicKey pubKey = privateKeyIdentity.getPublic();
+                log.debug("authenticateClientSession({}) using {} key={}",
+                    session, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
+            }
             session.addPublicKeyIdentity(privateKeyIdentity);
         }
 
+        if (debugEnabled) {
+            log.debug("authenticateClientSession({}) authenticate - timeout=", session, timeout);
+        }
         session.auth().verify(timeout);
         return session;
     }


[06/12] mina-sshd git commit: [SSHD-850] Updated CHANGES.md to reflect correctly latest changes to FilePasswordProvider

Posted by lg...@apache.org.
[SSHD-850] Updated CHANGES.md to reflect correctly latest changes to FilePasswordProvider


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

Branch: refs/heads/master
Commit: 71516ebb633e1feb153b915f6e6dc6725df9423e
Parents: 34ac8a7
Author: Lyor Goldstein <lg...@apache.org>
Authored: Tue Nov 13 08:12:44 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      | 41 ++++++++++++++------
 .../sshd/client/config/keys/ClientIdentity.java | 10 ++---
 .../sshd/common/config/keys/IdentityUtils.java  |  2 +-
 .../common/util/security/SecurityUtils.java     |  2 +-
 .../common/util/security/SecurityUtilsTest.java | 17 +++++---
 .../java/org/apache/sshd/client/SshClient.java  |  4 +-
 .../openpgp/PGPKeyPairResourceParserTest.java   |  5 ++-
 .../keys/loader/putty/PuttyKeyUtilsTest.java    |  4 +-
 8 files changed, 56 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index cc604ce..7ed1296 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,8 +12,8 @@
 * Added new `sessionDisconnect` method to `SessionListener`
 
 * `ReservedSessionMessagesHandler#handleUnimplementedMessage` has an extra `cmd` argument
-and is called both for `SSH_MSG_UNIMPLEMENTED` and also for any unexpected/unrecognized command
-encountered during the session message processing loop.
+and is called both for `SSH_MSG_UNIMPLEMENTED` as well as for any other unexpected/unrecognized
+command encountered during the session message processing loop.
 
 * `ClientSessionCreator` has extra `connect` methods with an `AttributeRepository`
 connection context argument
@@ -22,22 +22,41 @@ connection context argument
 accept also an `AttributeRepository` connection context argument (propagated from the
 `ClientSessionCreator#connect` invocation).
 
-* `FilePasswordProvider` has an extra method (`handleDecodeAttemptResult`) that enables
-user to try and repeat an encrypted private key decoding using a different password. Furthermore,
-the interface methods are also provided with a retry index that indicates the number of the time
-they have ben re-invoked for the same resource (including on success).
+* `FilePasswordProvider`
+
+    * Added an extra method (`handleDecodeAttemptResult`) that enables users to try and repeat an
+    encrypted private key decoding using a different password.
+
+    * The interface methods are also provided with a retry index that indicates the number of
+    times they have been re-invoked for the same resource (including on success).
 
 * `SshAgent#getIdentities` returns an `Iterable` rather than a `List`
 
 * `SftpFileSystemProvider` and its associated helper classes have been moved to
 `org.apache.sshd.client.subsystem.sftp.fs` package
 
-* Added `SftpFileSystemClientSessionInitializer` support in `SftpFileSystemProvider`
+## Behavioral changes and enhancements
+
+* [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all
+pending packets have been sent to the peer channel when closing the tunnel gracefully.
+
+* [SSHD-850](https://issues.apache.org/jira/browse/SSHD-850) - Add capability to retry a failed private key decryption
+
+* [SSHD-857](https://issues.apache.org/jira/browse/SSHD-857) - Add session disconnect event signalling to SessionListener
+
+    * Also calling `ReservedSessionMessagesHandler#handleUnimplementedMessage` not only for `SSH_MSG_UNIMPLEMENTED` but
+    also for any unexpected/unrecognized command encountered during the session message processing loop.
+
+* [SSHD-859](https://issues.apache.org/jira/browse/SSHD-859) - Provide client session connection context that is propagated to the SSH session
+
+    * Also added connection context argument (propagated from the `ClientSessionCreator#connect` invocation)
+    to`connectionEstablished` and `abortEstablishedConnection` methods of `IoServiceEventListener`.
 
-## Behavioral changes
+* [SSHD-860](https://issues.apache.org/jira/browse/SSHD-860) - `UserAuthPublicKeyIterator` uses lazy loading of public key
+identities both from agent and client session
 
-* [SSHD-860](https://issues.apache.org/jira/browse/SSHD-860)
+    * Also using lazy identity `KeyPair`(s) loading in `ClientIdentitiesWatcher`
 
-    * `UserAuthPublicKeyIterator` uses lazy loading of public key identities both from agent and client session
+* [SSHD-861](https://issues.apache.org/jira/browse/SSHD-861) - Fixed username/password encoding for `SftpFileSystem` URI(s)
 
-    * Using lazy identity `KeyPair`(s) loading in `ClientIdentitiesWatcher`
+    * Also added `SftpFileSystemClientSessionInitializer` support in `SftpFileSystemProvider`

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
index 931d96f..13f5c34 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
@@ -101,7 +101,7 @@ public final class ClientIdentity {
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
@@ -126,7 +126,7 @@ public final class ClientIdentity {
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
@@ -149,7 +149,7 @@ public final class ClientIdentity {
      *                 access rights are excluded from consideration
      * @param provider A {@link FilePasswordProvider} - may be {@code null}
      *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                 to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link LinkOption}s to apply when checking
      *                 for existence
@@ -171,7 +171,7 @@ public final class ClientIdentity {
      *                 access rights are excluded from consideration
      * @param provider A {@link FilePasswordProvider} - may be {@code null}
      *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                 to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link LinkOption}s to apply when checking
      *                 for existence
@@ -198,7 +198,7 @@ public final class ClientIdentity {
      *                    holding the specified type
      * @param provider    A {@link FilePasswordProvider} - may be {@code null}
      *                    if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                    to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                    to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                    file whose key is to be loaded
      * @param options     The {@link LinkOption}s to apply when checking
      *                    for existence

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/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 fbc3ce7..9cf3859 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
@@ -127,7 +127,7 @@ public final class IdentityUtils {
      *                 <U>insensitive</U>), value=the {@link Path} of file with the identity key
      * @param provider A {@link FilePasswordProvider} - may be {@code null}
      *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                 to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link OpenOption}s to use when reading the key data
      * @return A {@link Map} of the identities where key=identity type (case

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/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 e05f429..1534f95 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
@@ -469,7 +469,7 @@ public final class SecurityUtils {
 
     /**
      * @param resourceKey An identifier of the key being loaded - used as
-     *                    argument to the {@link FilePasswordProvider#getPassword(String)}
+     *                    argument to the {@link FilePasswordProvider#getPassword(String, int)}
      *                    invocation
      * @param inputStream The {@link InputStream} for the <U>private</U> key
      * @param provider    A {@link FilePasswordProvider} - may be {@code null}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/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 f244fd4..cdb4967 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
@@ -107,7 +107,8 @@ public class SecurityUtilsTest extends JUnitTestSupport {
         }
     }
 
-    private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) throws IOException, GeneralSecurityException {
+    private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm)
+            throws IOException, GeneralSecurityException {
         return testLoadRSAPrivateKey(DEFAULT_PASSWORD.replace(' ', '-') + "-RSA-" + algorithm.toUpperCase() + "-key");
     }
 
@@ -146,8 +147,9 @@ public class SecurityUtilsTest extends JUnitTestSupport {
         return testLoadPrivateKey(name, RSAPublicKey.class, RSAPrivateKey.class);
     }
 
-    private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
-            throws IOException, GeneralSecurityException {
+    private KeyPair testLoadPrivateKey(
+            String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
+                throws IOException, GeneralSecurityException {
         Path folder = getTestResourcesFolder();
         Path file = folder.resolve(name);
         KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
@@ -167,15 +169,18 @@ public class SecurityUtilsTest extends JUnitTestSupport {
         return kpResource;
     }
 
-    private static KeyPair testLoadPrivateKeyResource(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+    private static KeyPair testLoadPrivateKeyResource(
+            String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
         return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
     }
 
-    private static KeyPair testLoadPrivateKeyFile(Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+    private static KeyPair testLoadPrivateKeyFile(
+            Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
         return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
     }
 
-    private static KeyPair testLoadPrivateKey(String resourceKey, AbstractResourceKeyPairProvider<?> provider,
+    private static KeyPair testLoadPrivateKey(
+            String resourceKey, AbstractResourceKeyPairProvider<?> provider,
             Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
         provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
         Iterable<KeyPair> iterator = provider.loadKeys();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/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 227a4d5..6c3e9c8 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
@@ -792,7 +792,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
@@ -818,7 +818,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
      *                      supported internally
      * @param provider      A {@link FilePasswordProvider} - may be {@code null}
      *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      to {@link FilePasswordProvider#getPassword(String, int)} is the path of the
      *                      file whose key is to be loaded
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/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 bf2b8bb..6bb2f70 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
@@ -67,14 +67,15 @@ public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
         this.resourceName = resourceName;
         this.result = result;
         this.passwordProvider = new FilePasswordProvider() {
-
             @Override
             @SuppressWarnings("synthetic-access")
             public String getPassword(String resourceKey, int retryIndex) throws IOException {
                 switch (result) {
                     case IGNORE:
                     case TERMINATE:
-                        return "qweryuiop123456!@#$%^";
+                        assertEquals("Mismatched retries invocation count", 0, retryIndex);
+                        assertEquals("Mismatched retries tracking count", retryIndex, retriesCount.get());
+                        return "qwertyuiop123456!@#$%^";
                     case RETRY: {
                         int count = retriesCount.incrementAndGet();
                         assertEquals("Mismatched retries count", count, retryIndex + 1);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/71516ebb/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 3cf2b4f..8f1f2eb 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
@@ -147,7 +147,9 @@ public class PuttyKeyUtilsTest extends JUnitTestSupport {
                     switch (result) {
                         case IGNORE:
                         case TERMINATE:
-                            return "qweryuiop123456!@#$%^";
+                            assertEquals("Mismatched retries invocation count", 0, retryIndex);
+                            assertEquals("Mismatched retries tracking count", retryIndex, retriesCount.get());
+                            return "qwertyuiop123456!@#$%^";
                         case RETRY: {
                             int count = retriesCount.incrementAndGet();
                             assertEquals("Mismatched retries count", retryIndex + 1, count);


[05/12] mina-sshd git commit: [SSHD-862] Established SessionContext 'succinct' representation of a session

Posted by lg...@apache.org.
[SSHD-862] Established SessionContext 'succinct' representation of a session


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

Branch: refs/heads/master
Commit: c649ac5abc008215d95268ad0df5d80dbe12c065
Parents: 8be6ef9
Author: Lyor Goldstein <lg...@apache.org>
Authored: Tue Nov 13 08:22:40 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  2 +
 .../apache/sshd/common/PropertyResolver.java    |  5 +-
 .../sshd/common/session/SessionContext.java     | 96 ++++++++++++++++++++
 .../util/net/ConnectionEndpointsIndicator.java  | 37 ++++++++
 .../client/session/AbstractClientSession.java   |  4 +-
 .../forward/ExplicitPortForwardingTracker.java  |  4 +-
 .../org/apache/sshd/common/io/IoSession.java    | 15 +--
 .../org/apache/sshd/common/session/Session.java | 83 ++++-------------
 .../common/session/helpers/AbstractSession.java |  2 +-
 .../common/session/helpers/SessionHelper.java   |  7 +-
 .../server/channel/PuttyRequestHandler.java     |  7 +-
 .../server/session/AbstractServerSession.java   |  4 +-
 .../session/helpers/AbstractSessionTest.java    |  5 +-
 .../java/org/apache/sshd/server/ServerTest.java |  5 +-
 14 files changed, 181 insertions(+), 95 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 95c17cf..8af2a71 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,8 @@
 
 * `DEFAULT_PORT` moved from `SshConfigFileReader` to `SshConstants`
 
+* Moved some session "summary" related definitions from `Session` to `SessionContext` (which `Session` extends)
+
 * Added new `sessionDisconnect` method to `SessionListener`
 
 * `ReservedSessionMessagesHandler#handleUnimplementedMessage` has an extra `cmd` argument

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
index 91c3d41..d7f5f19 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
@@ -57,7 +57,7 @@ public interface PropertyResolver {
 
     /**
      * @return The parent resolver that can be used to query for missing
-     *         properties - {@code null} if no parent
+     * properties - {@code null} if no parent
      */
     PropertyResolver getParentPropertyResolver();
 
@@ -81,7 +81,8 @@ public interface PropertyResolver {
      * be converted into one.
      * </P>
      *
-     * @return a valid <code>Map</code> containing configuration values, never {@code null}
+     * @return a valid <code>Map</code> containing configuration values, never {@code null}.
+     * <b>Note:</b> may be immutable.
      */
     Map<String, Object> getProperties();
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
new file mode 100644
index 0000000..87827bd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
@@ -0,0 +1,96 @@
+/*
+ * 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.session;
+
+import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.auth.UsernameHolder;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.net.ConnectionEndpointsIndicator;
+
+/**
+ * A &quot;succinct&quot; summary of the most important attributes of an SSH session
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SessionContext
+        extends ConnectionEndpointsIndicator,
+                UsernameHolder,
+                PropertyResolver,
+                AttributeRepository {
+    /**
+     * Default prefix expected for the client / server identification string
+     * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-4.2">RFC 4253 - section 4.2</A>
+     */
+    String DEFAULT_SSH_VERSION_PREFIX = "SSH-2.0-";
+
+    /**
+     * Backward compatible special prefix
+     * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-5">RFC 4253 - section 5</A>
+     */
+    String FALLBACK_SSH_VERSION_PREFIX = "SSH-1.99-";
+
+    /**
+     * Maximum number of characters for any single line sent as part
+     * of the initial handshake - according to
+     * <A HREF="https://tools.ietf.org/html/rfc4253#section-4.2">RFC 4253 - section 4.2</A>:</BR>
+     *
+     * <P><CODE>
+     *      The maximum length of the string is 255 characters,
+     *      including the Carriage Return and Line Feed.
+     * </CODE></P>
+     */
+    int MAX_VERSION_LINE_LENGTH = 256;
+
+    /**
+     * @return A <U>clone</U> of the established session identifier - {@code null} if
+     * not yet established
+     */
+    byte[] getSessionId();
+
+    /**
+     * Retrieve the client version for this session.
+     *
+     * @return the client version - may be {@code null}/empty if versions not yet exchanged
+     */
+    String getClientVersion();
+
+    /**
+     * Retrieve the server version for this session.
+     *
+     * @return the server version - may be {@code null}/empty if versions not yet exchanged
+     */
+    String getServerVersion();
+
+    /**
+     * @return {@code true} if session has successfully completed the authentication phase
+     */
+    boolean isAuthenticated();
+
+    /**
+     * @param version The reported client/server version
+     * @return {@code true} if version not empty and starts with either
+     * {@value #DEFAULT_SSH_VERSION_PREFIX} or {@value #FALLBACK_SSH_VERSION_PREFIX}
+     */
+    static boolean isValidVersionPrefix(String version) {
+        return GenericUtils.isNotEmpty(version)
+            && (version.startsWith(DEFAULT_SSH_VERSION_PREFIX) || version.startsWith(FALLBACK_SSH_VERSION_PREFIX));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-common/src/main/java/org/apache/sshd/common/util/net/ConnectionEndpointsIndicator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/net/ConnectionEndpointsIndicator.java b/sshd-common/src/main/java/org/apache/sshd/common/util/net/ConnectionEndpointsIndicator.java
new file mode 100644
index 0000000..9d56e13
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/net/ConnectionEndpointsIndicator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.net;
+
+import java.net.SocketAddress;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ConnectionEndpointsIndicator {
+    /**
+     * @return the socket address of remote peer.
+     */
+    SocketAddress getRemoteAddress();
+
+    /**
+     * @return the socket address of local machine which is associated with this session.
+     */
+    SocketAddress getLocalAddress();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
index 3fac01c..1c329ef 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -59,7 +59,7 @@ import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.helpers.AbstractConnectionService;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
@@ -392,7 +392,7 @@ public abstract class AbstractClientSession extends AbstractSession implements C
             log.debug("readIdentification({}) Server version string: {}", this, serverVersion);
         }
 
-        if (!Session.isValidVersionPrefix(serverVersion)) {
+        if (!SessionContext.isValidVersionPrefix(serverVersion)) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
                     "Unsupported protocol version: " + serverVersion);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/client/session/forward/ExplicitPortForwardingTracker.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/forward/ExplicitPortForwardingTracker.java b/sshd-core/src/main/java/org/apache/sshd/client/session/forward/ExplicitPortForwardingTracker.java
index b8642ed..08cfefc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/forward/ExplicitPortForwardingTracker.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/forward/ExplicitPortForwardingTracker.java
@@ -24,12 +24,13 @@ import java.util.Objects;
 
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.forward.PortForwardingManager;
+import org.apache.sshd.common.util.net.ConnectionEndpointsIndicator;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class ExplicitPortForwardingTracker extends PortForwardingTracker {
+public class ExplicitPortForwardingTracker extends PortForwardingTracker implements ConnectionEndpointsIndicator {
     private final boolean localForwarding;
     private final SshdSocketAddress remoteAddress;
 
@@ -44,6 +45,7 @@ public class ExplicitPortForwardingTracker extends PortForwardingTracker {
         return localForwarding;
     }
 
+    @Override
     public SshdSocketAddress getRemoteAddress() {
         return remoteAddress;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/common/io/IoSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoSession.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoSession.java
index 3a3545c..c110eae 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoSession.java
@@ -18,11 +18,10 @@
  */
 package org.apache.sshd.common.io;
 
-import java.net.SocketAddress;
-
 import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.util.net.ConnectionEndpointsIndicator;
 
-public interface IoSession extends PacketWriter, Closeable {
+public interface IoSession extends ConnectionEndpointsIndicator, PacketWriter, Closeable {
 
     /**
      * @return a unique identifier for this session. Every session has its own
@@ -74,16 +73,6 @@ public interface IoSession extends PacketWriter, Closeable {
     Object removeAttribute(Object key);
 
     /**
-     * @return the socket address of remote peer.
-     */
-    SocketAddress getRemoteAddress();
-
-    /**
-     * @return the socket address of local machine which is associated with this session.
-     */
-    SocketAddress getLocalAddress();
-
-    /**
      * @return the {@link IoService} that created this session.
      */
     IoService getService();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 06516da..0ecab07 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.common.session;
 
 import java.io.IOException;
+import java.net.SocketAddress;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
@@ -27,7 +28,6 @@ import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.FactoryManagerHolder;
-import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.Service;
 import org.apache.sshd.common.auth.MutableUserHolder;
 import org.apache.sshd.common.channel.ChannelListenerManager;
@@ -44,7 +44,6 @@ import org.apache.sshd.common.kex.KexFactoryManager;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.mac.MacInformation;
-import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -54,7 +53,10 @@ import org.apache.sshd.common.util.buffer.Buffer;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public interface Session
-        extends KexFactoryManager,
+        extends SessionContext,
+                AttributeStore,
+                MutableUserHolder,
+                KexFactoryManager,
                 SessionListenerManager,
                 ReservedSessionMessagesManager,
                 ChannelListenerManager,
@@ -63,35 +65,8 @@ public interface Session
                 UnknownChannelReferenceHandlerManager,
                 FactoryManagerHolder,
                 PortForwardingInformationProvider,
-                PropertyResolver,
-                AttributeStore,
-                Closeable,
-                MutableUserHolder,
-                PacketWriter {
-
-    /**
-     * Default prefix expected for the client / server identification string
-     * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-4.2">RFC 4253 - section 4.2</A>
-     */
-    String DEFAULT_SSH_VERSION_PREFIX = "SSH-2.0-";
-
-    /**
-     * Backward compatible special prefix
-     * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-5">RFC 4253 - section 5</A>
-     */
-    String FALLBACK_SSH_VERSION_PREFIX = "SSH-1.99-";
-
-    /**
-     * Maximum number of characters for any single line sent as part
-     * of the initial handshake - according to
-     * <A HREF="https://tools.ietf.org/html/rfc4253#section-4.2">RFC 4253 - section 4.2</A>:</BR>
-     *
-     * <P><CODE>
-     *      The maximum length of the string is 255 characters,
-     *      including the Carriage Return and Line Feed.
-     * </CODE></P>
-     */
-    int MAX_VERSION_LINE_LENGTH = 256;
+                PacketWriter,
+                Closeable {
 
     /**
      * Timeout status.
@@ -103,20 +78,6 @@ public interface Session
     }
 
     /**
-     * Retrieve the client version for this session.
-     *
-     * @return the client version.
-     */
-    String getClientVersion();
-
-    /**
-     * Retrieve the server version for this session.
-     *
-     * @return the server version.
-     */
-    String getServerVersion();
-
-    /**
      * Retrieve one of the negotiated values during the KEX stage
      *
      * @param paramType The request {@link KexProposalOption} value
@@ -273,6 +234,18 @@ public interface Session
      */
     IoSession getIoSession();
 
+    @Override
+    default SocketAddress getLocalAddress() {
+        IoSession s = getIoSession();
+        return (s == null) ? null : s.getLocalAddress();
+    }
+
+    @Override
+    default SocketAddress getRemoteAddress() {
+        IoSession s = getIoSession();
+        return (s == null) ? null : s.getRemoteAddress();
+    }
+
     /**
      * Check if timeout has occurred.
      *
@@ -298,8 +271,6 @@ public interface Session
      */
     long resetIdleTimeout();
 
-    boolean isAuthenticated();
-
     /**
      * @return Timeout value in milliseconds for authentication stage
      */
@@ -320,12 +291,6 @@ public interface Session
 
     void setAuthenticated() throws IOException;
 
-    /**
-     * @return The established session identifier - {@code null} if
-     * not yet established
-     */
-    byte[] getSessionId();
-
     KeyExchange getKex();
 
     /**
@@ -351,16 +316,6 @@ public interface Session
     }
 
     /**
-     * @param version The reported client/server version
-     * @return {@code true} if version not empty and starts with either
-     * {@value #DEFAULT_SSH_VERSION_PREFIX} or {@value #FALLBACK_SSH_VERSION_PREFIX}
-     */
-    static boolean isValidVersionPrefix(String version) {
-        return GenericUtils.isNotEmpty(version)
-            && (version.startsWith(DEFAULT_SSH_VERSION_PREFIX) || version.startsWith(FALLBACK_SSH_VERSION_PREFIX));
-    }
-
-    /**
      * Attempts to use the session's attribute, if not found then tries the factory manager
      *
      * @param <T> The generic attribute type

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 1f5efbb..db3b17a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -282,7 +282,7 @@ public abstract class AbstractSession extends SessionHelper {
             decoderBuffer.putBuffer(buffer);
             // One of those property will be set by the constructor and the other
             // one should be set by the readIdentification method
-            if (clientVersion == null || serverVersion == null) {
+            if ((clientVersion == null) || (serverVersion == null)) {
                 if (readIdentification(decoderBuffer)) {
                     decoderBuffer.compact();
                 } else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
index 25cbf2f..6a4574d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
@@ -60,6 +60,7 @@ import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ReservedSessionMessagesHandler;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.session.UnknownChannelReferenceHandler;
 import org.apache.sshd.common.util.GenericUtils;
@@ -619,7 +620,7 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements
     /**
      * Resolves the identification to send to the peer session by consulting
      * the associated {@link FactoryManager}. If a value is set, then it is
-     * <U>appended</U> to the standard {@link #DEFAULT_SSH_VERSION_PREFIX}.
+     * <U>appended</U> to the standard {@link SessionContext#DEFAULT_SSH_VERSION_PREFIX}.
      * Otherwise a default value is returned consisting of the prefix and
      * the core artifact name + version in <U>uppercase</U> - e.g.,'
      * &quot;SSH-2.0-SSHD-CORE-1.2.3.4&quot;
@@ -630,7 +631,7 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements
     protected String resolveIdentificationString(String configPropName) {
         FactoryManager manager = getFactoryManager();
         String ident = manager.getString(configPropName);
-        return DEFAULT_SSH_VERSION_PREFIX + (GenericUtils.isEmpty(ident) ? manager.getVersion() : ident);
+        return SessionContext.DEFAULT_SSH_VERSION_PREFIX + (GenericUtils.isEmpty(ident) ? manager.getVersion() : ident);
     }
 
     /**
@@ -669,7 +670,7 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements
             FactoryManager.MAX_IDENTIFICATION_SIZE, FactoryManager.DEFAULT_MAX_IDENTIFICATION_SIZE);
         List<String> ident = null;
         int rpos = buffer.rpos();
-        for (byte[] data = new byte[MAX_VERSION_LINE_LENGTH];;) {
+        for (byte[] data = new byte[SessionContext.MAX_VERSION_LINE_LENGTH];;) {
             int pos = 0;    // start accumulating line from scratch
             for (boolean needLf = false;;) {
                 if (buffer.available() == 0) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
index a5eabe1..79a166c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
@@ -27,6 +27,7 @@ import org.apache.sshd.common.channel.AbstractChannelRequestHandler;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.PtyMode;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
@@ -98,12 +99,12 @@ public class PuttyRequestHandler extends AbstractChannelRequestHandler {
 
     /**
      * @param clientVersion The client identification string - ignored if {@code null}/empty
-     * @return {@code true} if the identification starts with the {@link Session#DEFAULT_SSH_VERSION_PREFIX}
+     * @return {@code true} if the identification starts with the {@link SessionContext#DEFAULT_SSH_VERSION_PREFIX}
      * and it contains the &quot;putty&quot; string (case insensitive)
      */
     public static boolean isPuttyClient(String clientVersion) {
-        return (GenericUtils.length(clientVersion) > Session.DEFAULT_SSH_VERSION_PREFIX.length())
-            && clientVersion.startsWith(Session.DEFAULT_SSH_VERSION_PREFIX)
+        return (GenericUtils.length(clientVersion) > SessionContext.DEFAULT_SSH_VERSION_PREFIX.length())
+            && clientVersion.startsWith(SessionContext.DEFAULT_SSH_VERSION_PREFIX)
             && clientVersion.toLowerCase().contains("putty");
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 04e7fd0..96053f1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -43,7 +43,7 @@ import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -358,7 +358,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
         }
 
         String errorMessage = null;
-        if (!Session.isValidVersionPrefix(clientVersion)) {
+        if (!SessionContext.isValidVersionPrefix(clientVersion)) {
             errorMessage = "Unsupported protocol version: " + clientVersion;
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
index cf6779f..388bc7a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
@@ -44,6 +44,7 @@ import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ReservedSessionMessagesHandler;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -120,11 +121,11 @@ public class AbstractSessionTest extends BaseTestSupport {
 
     @Test(expected = IllegalStateException.class)
     public void testReadIdentLongLine() {
-        StringBuilder sb = new StringBuilder(Session.MAX_VERSION_LINE_LENGTH + Integer.SIZE);
+        StringBuilder sb = new StringBuilder(SessionContext.MAX_VERSION_LINE_LENGTH + Integer.SIZE);
         sb.append("SSH-2.0-software");
         do {
             sb.append("01234567890123456789012345678901234567890123456789");
-        } while (sb.length() < Session.MAX_VERSION_LINE_LENGTH);
+        } while (sb.length() < SessionContext.MAX_VERSION_LINE_LENGTH);
 
         Buffer buf = new ByteArrayBuffer(sb.toString().getBytes(StandardCharsets.UTF_8));
         String ident = readIdentification(session, buf);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/c649ac5a/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 9944cde..5c3889f 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
@@ -60,6 +60,7 @@ import org.apache.sshd.common.channel.WindowClosedException;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.session.helpers.AbstractConnectionService;
 import org.apache.sshd.common.session.helpers.AbstractSession;
@@ -787,11 +788,11 @@ public class ServerTest extends BaseTestSupport {
     public void testIdentificationStringsOverrides() throws Exception {
         String clientIdent = getCurrentTestName() + "-client";
         PropertyResolverUtils.updateProperty(client, ClientFactoryManager.CLIENT_IDENTIFICATION, clientIdent);
-        final String expClientIdent = Session.DEFAULT_SSH_VERSION_PREFIX + clientIdent;
+        final String expClientIdent = SessionContext.DEFAULT_SSH_VERSION_PREFIX + clientIdent;
 
         String serverIdent = getCurrentTestName() + "-server";
         PropertyResolverUtils.updateProperty(sshd, ServerFactoryManager.SERVER_IDENTIFICATION, serverIdent);
-        String expServerIdent = Session.DEFAULT_SSH_VERSION_PREFIX + serverIdent;
+        String expServerIdent = SessionContext.DEFAULT_SSH_VERSION_PREFIX + serverIdent;
         SessionListener listener = new SessionListener() {
             @Override
             public void sessionException(Session session, Throwable t) {


[09/12] mina-sshd git commit: [SSHD-859] Moved 'resolveAttribute' method to AttributeRepository

Posted by lg...@apache.org.
[SSHD-859] Moved 'resolveAttribute' method to AttributeRepository


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

Branch: refs/heads/master
Commit: 8be6ef9dfd63d7558b2c89550143eb52c10cc39f
Parents: 71516eb
Author: Lyor Goldstein <lg...@apache.org>
Authored: Tue Nov 13 08:21:30 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:47 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                             |  9 ++++++---
 .../org/apache/sshd/common/AttributeRepository.java    | 13 +++++++++++++
 .../java/org/apache/sshd/common/AttributeStore.java    | 10 ----------
 3 files changed, 19 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8be6ef9d/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 7ed1296..95c17cf 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,10 +2,13 @@
 
 ## Major code re-factoring
 
-* `AttributeKey` moved from `AttributeStore` to `AttributeRepository`
+* `AttributeStore` "read" methods moved to (new class) `AttributeRepository`
 
-* `getAttribute` moved from `AttributeStore` to `AttributeRepository` + added
-`attributeKeys` method.
+    * `AttributeKey` moved to `AttributeRepository`
+
+    * `getAttribute` and `resolveAttribute` moved to `AttributeRepository`
+
+    * Added `attributeKeys` enumeration method to `AttributeRepository`.
 
 * `DEFAULT_PORT` moved from `SshConfigFileReader` to `SshConstants`
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8be6ef9d/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java b/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
index f754e87..c197100 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
@@ -72,6 +72,19 @@ public interface AttributeRepository {
     <T> T getAttribute(AttributeKey<T> key);
 
     /**
+     * Attempts to resolve the associated value by going up the store's
+     * hierarchy (if any)
+     *
+     * @param <T> The generic attribute type
+     * @param key The key of the attribute; must not be {@code null}.
+     * @return {@code null} if there is no value associated with the specified key
+     * either in this repository or any of its ancestors (if any available)
+     */
+    default <T> T resolveAttribute(AttributeKey<T> key) {
+        return getAttribute(key);
+    }
+
+    /**
      * @return A {@link Collection} <u>snapshot</u> of all the currently registered
      * attributes in the repository
      */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8be6ef9d/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
index cc5665a..466cafe 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
@@ -77,15 +77,5 @@ public interface AttributeStore extends AttributeRepository {
      * @return The removed value; {@code null} if no previous value
      */
     <T> T removeAttribute(AttributeKey<T> key);
-
-    /**
-     * Attempts to resolve the associated value by going up the store's
-     * hierarchy (if any)
-     *
-     * @param <T> The generic attribute type
-     * @param key The key of the attribute; must not be {@code null}.
-     * @return {@code null} if there is no value associated with the specified key
-     */
-    <T> T resolveAttribute(AttributeKey<T> key);
 }