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/19 12:50:16 UTC

[1/3] mina-sshd git commit: [SSHD-865] 'SshClient' and 'ClientSession' use only a KeyIdentityProvider instead of KeyPairProvider

Repository: mina-sshd
Updated Branches:
  refs/heads/master e88a08326 -> fc7a8e7c2


[SSHD-865] 'SshClient' and 'ClientSession' use only a KeyIdentityProvider instead of KeyPairProvider


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

Branch: refs/heads/master
Commit: e849cc5a4c1fc1d14ad556cd656b6bca54dd0840
Parents: e88a083
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 15:32:47 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Mon Nov 19 14:50:09 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  3 ++
 .../sshd/cli/client/SshClientCliSupport.java    |  5 +--
 .../keys/DefaultClientIdentitiesWatcher.java    |  3 +-
 .../keyprovider/KeyIdentityProviderHolder.java  | 36 ++++++++++++++++++++
 .../client/ClientAuthenticationManager.java     |  7 ++--
 .../java/org/apache/sshd/client/SshClient.java  | 34 ++++++++++++------
 .../client/session/AbstractClientSession.java   | 13 +++++++
 .../sshd/client/session/ClientSession.java      |  2 +-
 .../common/kex/AbstractKexFactoryManager.java   | 14 --------
 .../sshd/common/kex/KexFactoryManager.java      |  3 +-
 .../server/ServerAuthenticationManager.java     |  3 +-
 .../java/org/apache/sshd/server/SshServer.java  | 12 +++++++
 .../server/global/OpenSshHostKeysHandler.java   | 11 +++---
 .../server/session/AbstractServerSession.java   | 15 ++++++++
 .../client/ClientAuthenticationManagerTest.java | 14 ++++----
 .../hosts/HostConfigEntryResolverTest.java      | 12 +++----
 .../sshd/common/auth/AuthenticationTest.java    |  3 +-
 .../sshd/common/kex/KexFactoryManagerTest.java  | 11 ------
 .../server/ServerAuthenticationManagerTest.java | 11 ++++++
 .../sshd/util/test/CoreTestSupportUtils.java    |  4 +--
 20 files changed, 150 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 60f941b..3ca0d3d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -57,6 +57,9 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 
 * `ApacheSshdSftpSessionFactory#get/setPrivateKey` has been renamed to `get/setPrivateKeyLocation`.
 
+* `SshClient` and `ClientSession` use a `KeyIdentityProvider` instead of a full blown `KeyPairProvider`.
+`KeyPairProvider` is used only in the context of an `SshServer` and/or `ServerSession`.
+
 ## 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/e849cc5a/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 9286324..4741e72 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
@@ -343,7 +343,8 @@ public abstract class SshClientCliSupport extends CliSupport {
         }
     }
 
-    public static FileKeyPairProvider setupSessionIdentities(ClientFactoryManager client, Collection<? extends Path> identities,
+    public static FileKeyPairProvider setupSessionIdentities(
+            ClientFactoryManager client, Collection<? extends Path> identities,
             BufferedReader stdin, PrintStream stdout, PrintStream stderr)
                 throws Throwable {
         client.setFilePasswordProvider((session, file, index) -> {
@@ -362,7 +363,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             }
         };
         provider.setPaths(identities);
-        client.setKeyPairProvider(provider);
+        client.setKeyIdentityProvider(provider);
         return provider;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
index 3afa129..ee710c3 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
@@ -40,7 +40,8 @@ public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatch
         this(true, loader, provider, strict);
     }
 
-    public DefaultClientIdentitiesWatcher(boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+    public DefaultClientIdentitiesWatcher(
+            boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
         this(supportedOnly,
              GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
              GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderHolder.java
new file mode 100644
index 0000000..b33064a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderHolder.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.keyprovider;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyIdentityProviderHolder {
+    /**
+     * @return The {@link KeyIdentityProvider} used to provide key-pair(s)
+     * for public key authentication
+     */
+    KeyIdentityProvider getKeyIdentityProvider();
+
+    void setKeyIdentityProvider(KeyIdentityProvider provider);
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
index a9ae68c..4b84752 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
@@ -33,7 +33,7 @@ import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
+import org.apache.sshd.common.keyprovider.KeyIdentityProviderHolder;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -41,7 +41,7 @@ import org.apache.sshd.common.util.ValidateUtils;
  * Holds information required for the client to perform authentication with the server
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface ClientAuthenticationManager extends KeyPairProviderHolder {
+public interface ClientAuthenticationManager extends KeyIdentityProviderHolder {
 
     /**
      * Ordered comma separated list of authentications methods.
@@ -74,7 +74,8 @@ public interface ClientAuthenticationManager extends KeyPairProviderHolder {
      * candidates
      *
      * @return The {@link PasswordIdentityProvider} instance - ignored if {@code null}
-     * (i.e., no passwords available)
+     * (i.e., no passwords available).
+     * @see #addPasswordIdentity(String)
      */
     PasswordIdentityProvider getPasswordIdentityProvider();
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 fba078c..796d781 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
@@ -80,6 +80,7 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.IoConnectFuture;
 import org.apache.sshd.common.io.IoConnector;
 import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
@@ -167,6 +168,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
     private ServerKeyVerifier serverKeyVerifier;
     private HostConfigEntryResolver hostConfigEntryResolver;
     private ClientIdentityLoader clientIdentityLoader;
+    private KeyIdentityProvider keyIdentityProvider;
     private FilePasswordProvider filePasswordProvider;
     private PasswordIdentityProvider passwordIdentityProvider;
 
@@ -325,6 +327,16 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
     }
 
     @Override
+    public KeyIdentityProvider getKeyIdentityProvider() {
+        return keyIdentityProvider;
+    }
+
+    @Override
+    public void setKeyIdentityProvider(KeyIdentityProvider keyIdentityProvider) {
+        this.keyIdentityProvider = keyIdentityProvider;
+    }
+
+    @Override
     protected void checkConfig() {
         super.checkConfig();
 
@@ -335,17 +347,17 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         Objects.requireNonNull(getFilePasswordProvider(), "FilePasswordProvider not set");
 
         // if no client identities override use the default
-        KeyPairProvider defaultIdentities = getKeyPairProvider();
+        KeyIdentityProvider defaultIdentities = getKeyIdentityProvider();
         if (defaultIdentities == null) {
-            setKeyPairProvider(new DefaultClientIdentitiesWatcher(this::getClientIdentityLoader, this::getFilePasswordProvider));
+            setKeyIdentityProvider(new DefaultClientIdentitiesWatcher(this::getClientIdentityLoader, this::getFilePasswordProvider));
         }
 
         // Register the additional agent forwarding channel if needed
         SshAgentFactory agentFactory = getAgentFactory();
         if (agentFactory != null) {
             List<NamedFactory<Channel>> forwarders =
-                    ValidateUtils.checkNotNullAndNotEmpty(
-                            agentFactory.getChannelForwardingFactories(this), "No agent channel forwarding factories for %s", agentFactory);
+                ValidateUtils.checkNotNullAndNotEmpty(
+                    agentFactory.getChannelForwardingFactories(this), "No agent channel forwarding factories for %s", agentFactory);
             List<NamedFactory<Channel>> factories = getChannelFactories();
             if (GenericUtils.isEmpty(factories)) {
                 factories = forwarders;
@@ -656,15 +668,15 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
     protected void setupDefaultSessionIdentities(ClientSession session) {
         // check if session listener intervened
-        KeyPairProvider kpSession = session.getKeyPairProvider();
-        KeyPairProvider kpClient = getKeyPairProvider();
+        KeyIdentityProvider kpSession = session.getKeyIdentityProvider();
+        KeyIdentityProvider kpClient = getKeyIdentityProvider();
         boolean debugEnabled = log.isDebugEnabled();
         if (kpSession == null) {
-            session.setKeyPairProvider(kpClient);
+            session.setKeyIdentityProvider(kpClient);
         } else {
             if (kpSession != kpClient) {
                 if (debugEnabled) {
-                    log.debug("setupDefaultSessionIdentities({}) key-pair provider override", session);
+                    log.debug("setupDefaultSessionIdentities({}) key identity provider override", session);
                 }
             }
         }
@@ -828,7 +840,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
      * @param options       The {@link LinkOption}s to apply when checking
      *                      for existence
      * @return The updated <tt>client</tt> instance - provided a non-{@code null}
-     * {@link KeyPairProvider} was generated
+     * {@link KeyIdentityProvider} was generated
      * @throws IOException              If failed to access the file system
      * @throws GeneralSecurityException If failed to load the keys
      * @see ClientIdentity#loadDefaultKeyPairProvider(Path, boolean, boolean, FilePasswordProvider, LinkOption...)
@@ -836,10 +848,10 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
     public static <C extends SshClient> C setKeyPairProvider(
             C client, Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
                 throws IOException, GeneralSecurityException {
-        KeyPairProvider kpp =
+        KeyIdentityProvider kpp =
             ClientIdentity.loadDefaultKeyPairProvider(dir, strict, supportedOnly, provider, options);
         if (kpp != null) {
-            client.setKeyPairProvider(kpp);
+            client.setKeyIdentityProvider(kpp);
         }
 
         return client;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 1c329ef..57485ad 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
@@ -58,6 +58,7 @@ import org.apache.sshd.common.io.IoSession;
 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.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.helpers.AbstractConnectionService;
@@ -80,6 +81,7 @@ public abstract class AbstractClientSession extends AbstractSession implements C
     private ServerKeyVerifier serverKeyVerifier;
     private UserInteraction userInteraction;
     private PasswordIdentityProvider passwordIdentityProvider;
+    private KeyIdentityProvider keyIdentityProvider;
     private List<NamedFactory<UserAuth>> userAuthFactories;
     private SocketAddress connectAddress;
     private ClientProxyConnector proxyConnector;
@@ -160,6 +162,17 @@ public abstract class AbstractClientSession extends AbstractSession implements C
     }
 
     @Override
+    public KeyIdentityProvider getKeyIdentityProvider() {
+        ClientFactoryManager manager = getFactoryManager();
+        return resolveEffectiveProvider(KeyIdentityProvider.class, keyIdentityProvider, manager.getKeyIdentityProvider());
+    }
+
+    @Override
+    public void setKeyIdentityProvider(KeyIdentityProvider keyIdentityProvider) {
+        this.keyIdentityProvider = keyIdentityProvider;
+    }
+
+    @Override
     public ClientProxyConnector getClientProxyConnector() {
         ClientFactoryManager manager = getFactoryManager();
         return resolveEffectiveProvider(ClientProxyConnector.class, proxyConnector, manager.getClientProxyConnector());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 b7b5377..59df28c 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.getKeyIdentityProvider());
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
index aa111af..eb502e4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java
@@ -25,7 +25,6 @@ import java.util.List;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.cipher.Cipher;
 import org.apache.sshd.common.compression.Compression;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.GenericUtils;
@@ -43,7 +42,6 @@ public abstract class AbstractKexFactoryManager
     private List<NamedFactory<Compression>> compressionFactories;
     private List<NamedFactory<Mac>> macFactories;
     private List<NamedFactory<Signature>> signatureFactories;
-    private KeyPairProvider keyPairProvider;
 
     protected AbstractKexFactoryManager() {
         this(null);
@@ -117,18 +115,6 @@ public abstract class AbstractKexFactoryManager
         this.signatureFactories = signatureFactories;
     }
 
-    @Override
-    public KeyPairProvider getKeyPairProvider() {
-        KexFactoryManager parent = getDelegate();
-        return resolveEffectiveProvider(KeyPairProvider.class, keyPairProvider,
-            (parent == null) ? null : parent.getKeyPairProvider());
-    }
-
-    @Override
-    public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
-        this.keyPairProvider = keyPairProvider;
-    }
-
     protected <V> List<NamedFactory<V>> resolveEffectiveFactories(
             Class<V> factoryType, List<NamedFactory<V>> local, List<NamedFactory<V>> inherited) {
         if (GenericUtils.isEmpty(local)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
index 7aa8cba..6e46154 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java
@@ -30,7 +30,6 @@ import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.Cipher;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
-import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.signature.SignatureFactoriesManager;
@@ -41,7 +40,7 @@ import org.apache.sshd.common.util.ValidateUtils;
  * Holds KEX negotiation stage configuration
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface KexFactoryManager extends KeyPairProviderHolder, SignatureFactoriesManager {
+public interface KexFactoryManager extends SignatureFactoriesManager {
     /**
      * Retrieve the list of named factories for <code>KeyExchange</code>.
      *

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java
index 1d3a96d..c9a1b51 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java
@@ -27,6 +27,7 @@ import java.util.List;
 
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.server.auth.BuiltinUserAuthFactories;
@@ -46,7 +47,7 @@ import org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory;
  * Holds providers and helpers related to the server side authentication process
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface ServerAuthenticationManager {
+public interface ServerAuthenticationManager extends KeyPairProviderHolder {
     /**
      * Key used to retrieve the value in the configuration properties map
      * of the maximum number of failed authentication requests before the

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 c5a6918..72ed860 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
@@ -40,6 +40,7 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.IoAcceptor;
 import org.apache.sshd.common.io.IoServiceFactory;
 import org.apache.sshd.common.io.IoSession;
+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;
@@ -103,6 +104,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
     private CommandFactory commandFactory;
     private List<NamedFactory<Command>> subsystemFactories;
     private List<NamedFactory<UserAuth>> userAuthFactories;
+    private KeyPairProvider keyPairProvider;
     private PasswordAuthenticator passwordAuthenticator;
     private PublickeyAuthenticator publickeyAuthenticator;
     private KeyboardInteractiveAuthenticator interactiveAuthenticator;
@@ -249,6 +251,16 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
     }
 
     @Override
+    public KeyPairProvider getKeyPairProvider() {
+        return keyPairProvider;
+    }
+
+    @Override
+    public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
+        this.keyPairProvider = keyPairProvider;
+    }
+
+    @Override
     protected void checkConfig() {
         super.checkConfig();
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 35a7d68..c3f9477 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
@@ -40,6 +40,7 @@ import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.common.util.buffer.keys.BufferPublicKeyParser;
+import org.apache.sshd.server.session.ServerSession;
 
 /**
  * An initial handler for &quot;hostkeys-prove-00@openssh.com&quot; request
@@ -90,10 +91,10 @@ public class OpenSshHostKeysHandler extends AbstractOpenSshHostKeysHandler imple
         // 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 =
-                ValidateUtils.checkNotNullAndNotEmpty(
-                        SignatureFactoriesManager.resolveSignatureFactories(this, session),
-                        "No signature factories available for host keys of session=%s",
-                        session);
+            ValidateUtils.checkNotNullAndNotEmpty(
+                    SignatureFactoriesManager.resolveSignatureFactories(this, session),
+                    "No signature factories available for host keys of session=%s",
+                    session);
         if (log.isDebugEnabled()) {
             log.debug("handleHostKeys({})[want-reply={}] received {} keys - factories={}",
                       session, wantReply, GenericUtils.size(keys), NamedResource.getNames(factories));
@@ -104,7 +105,7 @@ public class OpenSshHostKeysHandler extends AbstractOpenSshHostKeysHandler imple
 
         Buffer buf = new ByteArrayBuffer();
         byte[] sessionId = session.getSessionId();
-        KeyPairProvider kpp = Objects.requireNonNull(session.getKeyPairProvider(), "No server keys provider");
+        KeyPairProvider kpp = Objects.requireNonNull(((ServerSession) session).getKeyPairProvider(), "No server keys provider");
         for (PublicKey k : keys) {
             String keyType = KeyUtils.getKeyType(k);
             Signature verifier = ValidateUtils.checkNotNull(

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 6af869e..2a5209c 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
@@ -40,6 +40,7 @@ import org.apache.sshd.common.auth.AbstractUserAuthServiceFactory;
 import org.apache.sshd.common.io.IoService;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.kex.KexFactoryManager;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
@@ -50,6 +51,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.buffer.ByteArrayBuffer;
+import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.auth.UserAuth;
 import org.apache.sshd.server.auth.WelcomeBannerPhase;
@@ -73,6 +75,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
     private GSSAuthenticator gssAuthenticator;
     private HostBasedAuthenticator hostBasedAuthenticator;
     private List<NamedFactory<UserAuth>> userAuthFactories;
+    private KeyPairProvider keyPairProvider;
 
     protected AbstractServerSession(ServerFactoryManager factoryManager, IoSession ioSession) {
         super(true, factoryManager, ioSession);
@@ -168,6 +171,18 @@ public abstract class AbstractServerSession extends AbstractSession implements S
         this.userAuthFactories = userAuthFactories; // OK if null/empty - inherit from parent
     }
 
+    @Override
+    public KeyPairProvider getKeyPairProvider() {
+        KexFactoryManager parent = getDelegate();
+        return resolveEffectiveProvider(KeyPairProvider.class, keyPairProvider,
+            (parent == null) ? null : ((ServerAuthenticationManager) parent).getKeyPairProvider());
+    }
+
+    @Override
+    public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
+        this.keyPairProvider = keyPairProvider;
+    }
+
     /**
      * Sends the server identification + any extra header lines
      *

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
index 30ce313..d4279f9 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
@@ -43,7 +43,7 @@ import org.apache.sshd.common.forward.DefaultForwarderFactory;
 import org.apache.sshd.common.forward.PortForwardingEventListener;
 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.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.random.JceRandomFactory;
 import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.random.SingletonRandomFactory;
@@ -81,13 +81,13 @@ public class ClientAuthenticationManagerTest extends BaseTestSupport {
             }
 
             @Override
-            public KeyPairProvider getKeyPairProvider() {
+            public KeyIdentityProvider getKeyIdentityProvider() {
                 return null;
             }
 
             @Override
-            public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
-                throw new UnsupportedOperationException("setKeyPairProvider(" + keyPairProvider + ")");
+            public void setKeyIdentityProvider(KeyIdentityProvider provider) {
+                throw new UnsupportedOperationException("setKeyIdentityProvider(" + provider + ")");
             }
 
             @Override
@@ -183,7 +183,7 @@ public class ClientAuthenticationManagerTest extends BaseTestSupport {
                     PasswordIdentityProvider.class,
                     ServerKeyVerifier.class,
                     UserInteraction.class,
-                    KeyPairProvider.class
+                    KeyIdentityProvider.class
                 }) {
                     testClientProvidersPropagation(provider, client, session);
                 }
@@ -191,7 +191,9 @@ public class ClientAuthenticationManagerTest extends BaseTestSupport {
         }
     }
 
-    private void testClientProvidersPropagation(Class<?> type, ClientAuthenticationManager client, ClientAuthenticationManager session) throws Exception {
+    private void testClientProvidersPropagation(
+            Class<?> type, ClientAuthenticationManager client, ClientAuthenticationManager session)
+                throws Exception {
         String baseName = type.getSimpleName();
         outputDebugMessage("testClientProvidersPropagation(%s)", baseName);
         assertTrue(baseName + ": not an interface", type.isInterface());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 1a767c6..b76e46c 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,7 +43,7 @@ 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.keyprovider.AbstractKeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -185,13 +185,13 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
     @Test
     public void testUseIdentitiesOnly() throws Exception {
         Path clientIdFile = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
-        KeyPairProvider clientIdProvider =
+        KeyIdentityProvider clientIdProvider =
             CommonTestSupportUtils.createTestHostKeyProvider(clientIdFile.resolve(getCurrentTestName() + ".pem"));
         KeyPair specificIdentity = CommonTestSupportUtils.getFirstKeyPair(sshd);
         KeyPair defaultIdentity = CommonTestSupportUtils.getFirstKeyPair(clientIdProvider);
         ValidateUtils.checkTrue(!KeyUtils.compareKeyPairs(specificIdentity, defaultIdentity),
                 "client identity not different then entry one");
-        client.setKeyPairProvider(clientIdProvider);
+        client.setKeyIdentityProvider(clientIdProvider);
 
         String user = getCurrentTestName();
         AtomicBoolean defaultClientIdentityAttempted = new AtomicBoolean(false);
@@ -234,17 +234,17 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
         PropertyResolverUtils.updateProperty(client, ClientFactoryManager.IGNORE_INVALID_IDENTITIES, false);
 
         Collection<KeyPair> clientIdentities = Collections.singletonList(defaultIdentity);
-        KeyPairProvider provider = new AbstractKeyPairProvider() {
+        KeyIdentityProvider provider = new AbstractKeyPairProvider() {
             @Override
             public Iterable<KeyPair> loadKeys(SessionContext session) {
                 return clientIdentities;
             }
         };
-        client.setKeyPairProvider(provider);
+        client.setKeyIdentityProvider(provider);
 
         client.start();
         try (ClientSession session = client.connect(entry).verify(7L, TimeUnit.SECONDS).getSession()) {
-            assertSame("Unexpected session key pairs provider", provider, session.getKeyPairProvider());
+            assertSame("Unexpected session key pairs provider", provider, session.getKeyIdentityProvider());
             session.auth().verify(5L, TimeUnit.SECONDS);
             assertFalse("Unexpected default client identity attempted", defaultClientIdentityAttempted.get());
             assertNull("Default client identity auto-added", session.removePublicKeyIdentity(defaultIdentity));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/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 e55d150..34c418b 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
@@ -53,6 +53,7 @@ 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.KeyIdentityProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionContext;
@@ -916,7 +917,7 @@ public class AuthenticationTest extends BaseTestSupport {
                         return "super secret passphrase";
                     }
                 };
-                s.setKeyPairProvider(new KeyPairProvider() {
+                s.setKeyIdentityProvider(new KeyIdentityProvider() {
                     @Override
                     public Iterable<KeyPair> loadKeys(SessionContext session) throws IOException, GeneralSecurityException {
                         assertSame("Mismatched session context", s, session);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
index 63c0346..8242a62 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java
@@ -27,7 +27,6 @@ import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.Cipher;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.signature.BuiltinSignatures;
@@ -129,16 +128,6 @@ public class KexFactoryManagerTest extends BaseTestSupport {
         }
 
         @Override
-        public KeyPairProvider getKeyPairProvider() {
-            return null;
-        }
-
-        @Override
-        public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
-            throw new UnsupportedOperationException("N/A");
-        }
-
-        @Override
         public List<NamedFactory<Signature>> getSignatureFactories() {
             return signatures;
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/test/java/org/apache/sshd/server/ServerAuthenticationManagerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerAuthenticationManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerAuthenticationManagerTest.java
index fc7b11c..9be1cb0 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerAuthenticationManagerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerAuthenticationManagerTest.java
@@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.server.auth.BuiltinUserAuthFactories;
 import org.apache.sshd.server.auth.UserAuth;
@@ -113,6 +114,16 @@ public class ServerAuthenticationManagerTest extends BaseTestSupport {
             public void setHostBasedAuthenticator(HostBasedAuthenticator hostBasedAuthenticator) {
                 throw new UnsupportedOperationException("setHostBasedAuthenticator(" + hostBasedAuthenticator + ")");
             }
+
+            @Override
+            public KeyPairProvider getKeyPairProvider() {
+                return null;
+            }
+
+            @Override
+            public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
+                throw new UnsupportedOperationException("setKeyPairProvider(" + keyPairProvider + ")");
+            }
         };
         assertEquals("Mismatched initial factories list", "", manager.getUserAuthFactoriesNameList());
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e849cc5a/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java b/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
index e6bb6cb..7d2b9b9 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
@@ -25,7 +25,7 @@ import java.net.ServerSocket;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
 import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
 import org.apache.sshd.server.shell.UnknownCommandFactory;
@@ -47,7 +47,7 @@ public final class CoreTestSupportUtils {
         SshClient client = SshClient.setUpDefaultClient();
         client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE);
         client.setHostConfigEntryResolver(HostConfigEntryResolver.EMPTY);
-        client.setKeyPairProvider(KeyPairProvider.EMPTY_KEYPAIR_PROVIDER);
+        client.setKeyIdentityProvider(KeyIdentityProvider.EMPTY_KEYS_PROVIDER);
         return client;
     }
 


[3/3] mina-sshd git commit: The various 'ClientIdentitiesWatcher'(s) use a type-safe 'ClientIdentityLoaderHolder' and 'FilePasswordProviderHolder' instead of the generic 'Supplier' definition

Posted by lg...@apache.org.
The various 'ClientIdentitiesWatcher'(s) use a type-safe 'ClientIdentityLoaderHolder' and 'FilePasswordProviderHolder' instead of the generic 'Supplier' definition


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

Branch: refs/heads/master
Commit: fc7a8e7c2f90a2b20ea4882a4d8b08be31753446
Parents: 98a708a
Author: Lyor Goldstein <lg...@apache.org>
Authored: Mon Nov 19 14:46:10 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Mon Nov 19 14:50:10 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                        |  6 ++++++
 .../sshd/client/config/hosts/HostConfigEntry.java |  4 ++--
 .../keys/BuiltinClientIdentitiesWatcher.java      | 10 +++++-----
 .../config/keys/ClientIdentitiesWatcher.java      | 18 +++++++++---------
 .../config/keys/ClientIdentityFileWatcher.java    | 18 ++++++++----------
 .../config/keys/ClientIdentityLoaderHolder.java   |  6 ++++--
 .../keys/DefaultClientIdentitiesWatcher.java      | 13 ++++++-------
 .../config/keys/FilePasswordProviderHolder.java   |  6 ++++--
 8 files changed, 44 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 9cbb99f..5074ade 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -63,6 +63,9 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 * `SshClient#loadClientIdentities` has been renamed to `preloadClientIdentities` + it returns a
 `KeyIdentityProvider` instead of a collection of strings representing paths.
 
+* The various `ClientIdentitiesWatcher`(s) use a type-safe `ClientIdentityLoaderHolder` and
+`FilePasswordProviderHolder` instead of the generic `Supplier` definition.
+
 ## Behavioral changes and enhancements
 
 * [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all
@@ -92,6 +95,9 @@ 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
 
+* [SSHD-865](https://issues.apache.org/jira/browse/SSHD-865) - Key identities overrides specified in the
+[ssh_config](http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config) configuration file are also lazy loaded
+
 * [SSHD-866](https://issues.apache.org/jira/browse/SSHD-866) - Counting empty challenges separately when enforcing
 max. attempts during `keyboard-interactive` authentication
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
index eafedd8..c2d3bcb 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
@@ -61,8 +61,8 @@ import org.apache.sshd.common.util.io.NoCloseReader;
 
 /**
  * Represents an entry in the client's configuration file as defined by
- * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
- * file format</A>
+ * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">ssh_config</A>
+ * configuration file format
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/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 bb3f738..5986e25 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
@@ -26,11 +26,11 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.function.Supplier;
 
 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.FilePasswordProviderHolder;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
@@ -49,18 +49,18 @@ public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
     public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
             ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
         this(keysFolder, ids, supportedOnly,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             ClientIdentityLoaderHolder.loaderHolderOf(Objects.requireNonNull(loader, "No client identity loader")),
+             FilePasswordProviderHolder.providerHolderOf(Objects.requireNonNull(provider, "No password provider")),
              strict);
     }
 
     public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+            ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider, boolean strict) {
         this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
     }
 
     public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+            ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider, boolean strict) {
         super(getBuiltinIdentitiesPaths(keysFolder, ids), loader, provider, strict);
         this.supportedOnly = supportedOnly;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/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 eed14e9..d3e567c 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
@@ -26,9 +26,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProviderHolder;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.SessionContext;
@@ -50,18 +50,18 @@ public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements
     public ClientIdentitiesWatcher(Collection<? extends Path> paths,
             ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
         this(paths,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             ClientIdentityLoaderHolder.loaderHolderOf(Objects.requireNonNull(loader, "No client identity loader")),
+             FilePasswordProviderHolder.providerHolderOf(Objects.requireNonNull(provider, "No password provider")),
              strict);
     }
 
     public ClientIdentitiesWatcher(Collection<? extends Path> paths,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+            ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider) {
         this(paths, loader, provider, true);
     }
 
     public ClientIdentitiesWatcher(Collection<? extends Path> paths,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+            ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider, boolean strict) {
         this(buildProviders(paths, loader, provider, strict));
     }
 
@@ -102,14 +102,14 @@ public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements
     public static List<ClientIdentityProvider> buildProviders(
             Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
         return buildProviders(paths,
-                GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-                GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+                ClientIdentityLoaderHolder.loaderHolderOf(Objects.requireNonNull(loader, "No client identity loader")),
+                FilePasswordProviderHolder.providerHolderOf(Objects.requireNonNull(provider, "No password provider")),
                 strict);
     }
 
     public static List<ClientIdentityProvider> buildProviders(
-            Collection<? extends Path> paths, Supplier<? extends ClientIdentityLoader> loader,
-            Supplier<? extends FilePasswordProvider> provider, boolean strict) {
+            Collection<? extends Path> paths, ClientIdentityLoaderHolder loader,
+            FilePasswordProviderHolder provider, boolean strict) {
         if (GenericUtils.isEmpty(paths)) {
             return Collections.emptyList();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/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 5832911..56f946d 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
@@ -27,13 +27,11 @@ import java.security.PublicKey;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.FilePasswordProviderHolder;
 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;
 import org.apache.sshd.common.util.io.resource.PathResource;
@@ -48,8 +46,8 @@ public class ClientIdentityFileWatcher
         extends ModifiableFileWatcher
         implements ClientIdentityProvider, ClientIdentityLoaderHolder, FilePasswordProviderHolder {
     private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
-    private final Supplier<? extends ClientIdentityLoader> loaderHolder;
-    private final Supplier<? extends FilePasswordProvider> providerHolder;
+    private final ClientIdentityLoaderHolder loaderHolder;
+    private final FilePasswordProviderHolder providerHolder;
     private final boolean strict;
 
     public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider) {
@@ -58,18 +56,18 @@ public class ClientIdentityFileWatcher
 
     public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
         this(path,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             ClientIdentityLoaderHolder.loaderHolderOf(Objects.requireNonNull(loader, "No client identity loader")),
+             FilePasswordProviderHolder.providerHolderOf(Objects.requireNonNull(provider, "No password provider")),
              strict);
     }
 
     public ClientIdentityFileWatcher(
-            Path path, Supplier<? extends ClientIdentityLoader> loader, Supplier<? extends FilePasswordProvider> provider) {
+            Path path, ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider) {
         this(path, loader, provider, true);
     }
 
     public ClientIdentityFileWatcher(
-            Path path, Supplier<? extends ClientIdentityLoader> loader, Supplier<? extends FilePasswordProvider> provider, boolean strict) {
+            Path path, ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider, boolean strict) {
         super(path);
         this.loaderHolder = Objects.requireNonNull(loader, "No client identity loader");
         this.providerHolder = Objects.requireNonNull(provider, "No password provider");
@@ -82,12 +80,12 @@ public class ClientIdentityFileWatcher
 
     @Override
     public ClientIdentityLoader getClientIdentityLoader() {
-        return loaderHolder.get();
+        return loaderHolder.getClientIdentityLoader();
     }
 
     @Override
     public FilePasswordProvider getFilePasswordProvider() {
-        return providerHolder.get();
+        return providerHolder.getFilePasswordProvider();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
index ecd0f0e..922f346 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
@@ -20,8 +20,6 @@
 package org.apache.sshd.client.config.keys;
 
 /**
- * TODO Add javadoc
- *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 @FunctionalInterface
@@ -31,4 +29,8 @@ public interface ClientIdentityLoaderHolder {
      * key pair identities - never {@code null}
      */
     ClientIdentityLoader getClientIdentityLoader();
+
+    static ClientIdentityLoaderHolder loaderHolderOf(ClientIdentityLoader loader) {
+        return () -> loader;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
index ee710c3..cee0af5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
@@ -22,11 +22,10 @@ package org.apache.sshd.client.config.keys;
 import java.nio.file.Path;
 import java.util.List;
 import java.util.Objects;
-import java.util.function.Supplier;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProviderHolder;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -43,21 +42,21 @@ public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatch
     public DefaultClientIdentitiesWatcher(
             boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
         this(supportedOnly,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             ClientIdentityLoaderHolder.loaderHolderOf(Objects.requireNonNull(loader, "No client identity loader")),
+             FilePasswordProviderHolder.providerHolderOf(Objects.requireNonNull(provider, "No password provider")),
              strict);
     }
 
-    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+    public DefaultClientIdentitiesWatcher(ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider) {
         this(loader, provider, true);
     }
 
-    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+    public DefaultClientIdentitiesWatcher(ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider, boolean strict) {
         this(true, loader, provider, strict);
     }
 
     public DefaultClientIdentitiesWatcher(boolean supportedOnly,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+            ClientIdentityLoaderHolder loader, FilePasswordProviderHolder provider, boolean strict) {
         super(PublicKeyEntry.getDefaultKeysFolderPath(), supportedOnly, loader, provider, strict);
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/fc7a8e7c/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java
index a872fac..9e2ce16 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java
@@ -20,8 +20,6 @@
 package org.apache.sshd.common.config.keys;
 
 /**
- * TODO Add javadoc
- *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 @FunctionalInterface
@@ -32,4 +30,8 @@ public interface FilePasswordProviderHolder {
      * @see FilePasswordProvider#EMPTY
      */
     FilePasswordProvider getFilePasswordProvider();
+
+    static FilePasswordProviderHolder providerHolderOf(FilePasswordProvider provider) {
+        return () -> provider;
+    }
 }


[2/3] mina-sshd git commit: [SSHD-865] Use lazy-loading for keys specified in the HostConfigEntry

Posted by lg...@apache.org.
[SSHD-865] Use lazy-loading for keys specified in the HostConfigEntry


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

Branch: refs/heads/master
Commit: 98a708a555cd5ba123161c8cbaa627cb855dff65
Parents: e849cc5
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 17:32:30 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Mon Nov 19 14:50:10 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |   3 +
 .../config/keys/ClientIdentityFileWatcher.java  |  13 +-
 .../config/keys/ClientIdentityLoader.java       |  22 +++
 .../config/keys/ClientIdentityLoaderHolder.java |  34 ++++
 .../keys/ClientIdentityLoaderManager.java       |  29 ++++
 .../keys/LazyClientKeyIdentityProvider.java     | 157 +++++++++++++++++++
 .../config/keys/FilePasswordProviderHolder.java |  35 +++++
 .../keys/FilePasswordProviderManager.java       |   9 +-
 .../common/keyprovider/KeyIdentityProvider.java |  19 ++-
 .../KeyIdentityProviderResolutionTest.java      |  90 +++++++++++
 .../sshd/client/ClientFactoryManager.java       |  10 +-
 .../java/org/apache/sshd/client/SshClient.java  | 112 +++++--------
 .../hosts/HostConfigEntryResolverTest.java      |  10 +-
 13 files changed, 438 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 3ca0d3d..9cbb99f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -60,6 +60,9 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 * `SshClient` and `ClientSession` use a `KeyIdentityProvider` instead of a full blown `KeyPairProvider`.
 `KeyPairProvider` is used only in the context of an `SshServer` and/or `ServerSession`.
 
+* `SshClient#loadClientIdentities` has been renamed to `preloadClientIdentities` + it returns a
+`KeyIdentityProvider` instead of a collection of strings representing paths.
+
 ## 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/98a708a5/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 ca81e47..5832911 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
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Supplier;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProviderHolder;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
@@ -43,7 +44,9 @@ import org.apache.sshd.common.util.io.resource.PathResource;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements ClientIdentityProvider {
+public class ClientIdentityFileWatcher
+        extends ModifiableFileWatcher
+        implements ClientIdentityProvider, ClientIdentityLoaderHolder, FilePasswordProviderHolder {
     private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
     private final Supplier<? extends ClientIdentityLoader> loaderHolder;
     private final Supplier<? extends FilePasswordProvider> providerHolder;
@@ -73,15 +76,17 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
         this.strict = strict;
     }
 
-    public final boolean isStrict() {
+    public boolean isStrict() {
         return strict;
     }
 
-    public final ClientIdentityLoader getClientIdentityLoader() {
+    @Override
+    public ClientIdentityLoader getClientIdentityLoader() {
         return loaderHolder.get();
     }
 
-    public final FilePasswordProvider getFilePasswordProvider() {
+    @Override
+    public FilePasswordProvider getFilePasswordProvider() {
         return providerHolder.get();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/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 1d941ac..ceb914e 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.Collection;
 import java.util.Objects;
 
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
+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;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.resource.PathResource;
@@ -105,4 +108,23 @@ public interface ClientIdentityLoader {
     KeyPair loadClientIdentity(
         SessionContext session, NamedResource location, FilePasswordProvider provider)
             throws IOException, GeneralSecurityException;
+
+    /**
+     * Uses the provided {@link ClientIdentityLoader} to <U>lazy</U> load the keys locations
+     *
+     * @param loader The loader instance to use
+     * @param locations The locations to load - ignored if {@code null}/empty
+     * @param passwordProvider The {@link FilePasswordProvider} to use if any
+     * encrypted keys found
+     * @param ignoreNonExisting Whether to ignore non existing locations as indicated
+     * by {@link #isValidLocation(NamedResource)}
+     * @return The {@link KeyIdentityProvider} wrapper
+     */
+    static KeyIdentityProvider asKeyIdentityProvider(
+            ClientIdentityLoader loader, Collection<? extends NamedResource> locations,
+            FilePasswordProvider passwordProvider, boolean ignoreNonExisting) {
+        return GenericUtils.isEmpty(locations)
+            ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER
+            : new LazyClientKeyIdentityProvider(loader, locations, passwordProvider, ignoreNonExisting);
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
new file mode 100644
index 0000000..ecd0f0e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderHolder.java
@@ -0,0 +1,34 @@
+/*
+ * 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.client.config.keys;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ClientIdentityLoaderHolder {
+    /**
+     * @return The {@link ClientIdentityLoader} to use in order to load client
+     * key pair identities - never {@code null}
+     */
+    ClientIdentityLoader getClientIdentityLoader();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderManager.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderManager.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderManager.java
new file mode 100644
index 0000000..9aa2e55
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoaderManager.java
@@ -0,0 +1,29 @@
+/*
+ * 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.client.config.keys;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ClientIdentityLoaderManager extends ClientIdentityLoaderHolder {
+    void setClientIdentityLoader(ClientIdentityLoader loader);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientKeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientKeyIdentityProvider.java
new file mode 100644
index 0000000..05596be
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientKeyIdentityProvider.java
@@ -0,0 +1,157 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProviderHolder;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LazyClientKeyIdentityProvider implements KeyIdentityProvider, ClientIdentityLoaderHolder, FilePasswordProviderHolder {
+    private final ClientIdentityLoader clientIdentityLoader;
+    private final Collection<? extends NamedResource> locations;
+    private final FilePasswordProvider passwordProvider;
+    private final boolean ignoreNonExisting;
+
+    public LazyClientKeyIdentityProvider(
+            ClientIdentityLoader loader, Collection<? extends NamedResource> locations,
+            FilePasswordProvider passwordProvider, boolean ignoreNonExisting) {
+        this.clientIdentityLoader = Objects.requireNonNull(loader, "No client identity loader provided");
+        this.locations = locations;
+        this.passwordProvider = passwordProvider;
+        this.ignoreNonExisting = ignoreNonExisting;
+    }
+
+    @Override
+    public ClientIdentityLoader getClientIdentityLoader() {
+        return clientIdentityLoader;
+    }
+
+    public Collection<? extends NamedResource> getLocations() {
+        return locations;
+    }
+
+    @Override
+    public FilePasswordProvider getFilePasswordProvider() {
+        return passwordProvider;
+    }
+
+    public boolean isIgnoreNonExisting() {
+        return ignoreNonExisting;
+    }
+
+    @Override
+    public Iterable<KeyPair> loadKeys(SessionContext session)
+            throws IOException, GeneralSecurityException {
+        Collection<? extends NamedResource> locs = getLocations();
+        if (GenericUtils.isEmpty(locs)) {
+            return Collections.emptyList();
+        }
+
+        return () -> new Iterator<KeyPair>() {
+            private final Iterator<? extends NamedResource> iter = locs.iterator();
+            private KeyPair currentPair;
+            private boolean finished;
+
+            @Override
+            public boolean hasNext() {
+                if (finished) {
+                    return false;
+                }
+
+                while (iter.hasNext()) {
+                    NamedResource l = iter.next();
+                    try {
+                        currentPair = loadClientIdentity(session, l);
+                    } catch (IOException | GeneralSecurityException e) {
+                        throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ")"
+                            + " to load key from " + l.getName() + ": " + e.getMessage(), e);
+                    }
+
+                    if (currentPair != null) {
+                        return true;
+                    }
+                }
+
+                finished = true;
+                return false;
+            }
+
+            @Override
+            public KeyPair next() {
+                if (finished) {
+                    throw new NoSuchElementException("All identities have been exhausted");
+                }
+                if (currentPair == null) {
+                    throw new IllegalStateException("'next()' called without asking 'hasNext()'");
+                }
+
+                KeyPair kp = currentPair;
+                currentPair = null;
+                return kp;
+            }
+
+            @Override
+            public String toString() {
+                return Iterator.class.getSimpleName() + "[" + LazyClientKeyIdentityProvider.class.getSimpleName() + "]";
+            }
+        };
+    }
+
+    protected KeyPair loadClientIdentity(SessionContext session, NamedResource location)
+            throws IOException, GeneralSecurityException {
+        ClientIdentityLoader loader = getClientIdentityLoader();
+        boolean ignoreInvalid = isIgnoreNonExisting();
+        try {
+            if (!loader.isValidLocation(location)) {
+                if (ignoreInvalid) {
+                    return null;
+                }
+
+                throw new FileNotFoundException("Invalid identity location: " + location.getName());
+            }
+        } catch (IOException e) {
+            if (ignoreInvalid) {
+                return null;
+            }
+
+            throw e;
+        }
+
+        return loader.loadClientIdentity(session, location, getFilePasswordProvider());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.java
new file mode 100644
index 0000000..a872fac
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProviderHolder.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.config.keys;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface FilePasswordProviderHolder {
+    /**
+     * @return The {@link FilePasswordProvider} to use if need to load encrypted
+     * identities keys - never {@code null}
+     * @see FilePasswordProvider#EMPTY
+     */
+    FilePasswordProvider getFilePasswordProvider();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/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
index aae151c..e38443a 100644
--- 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
@@ -24,13 +24,6 @@ package org.apache.sshd.common.config.keys;
  *
  * @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();
-
+public interface FilePasswordProviderManager extends FilePasswordProviderHolder {
     void setFilePasswordProvider(FilePasswordProvider provider);
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/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 3298f20..6ce5996 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
@@ -62,12 +62,20 @@ public interface KeyIdentityProvider {
     Iterable<KeyPair> loadKeys(SessionContext session) throws IOException, GeneralSecurityException;
 
     /**
+     * @param provider The {@link KeyIdentityProvider} instance to verify
+     * @return {@code true} if instance is {@code null} or the {@link #EMPTY_KEYS_PROVIDER}
+     */
+    static boolean isEmpty(KeyIdentityProvider provider) {
+        return (provider == null) || GenericUtils.isSameReference(provider, EMPTY_KEYS_PROVIDER);
+    }
+
+    /**
      * <P>Creates a &quot;unified&quot; {@link KeyIdentityProvider} out of 2 possible ones
      * as follows:</P></BR>
      * <UL>
      *      <LI>If both are {@code null} then return {@code null}.</LI>
-     *      <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
-     *      <LI>If both are the same instance then use it.</U>
+     *      <LI>If either one is {@code null}/{@link #EMPTY_KEYS_PROVIDER empty} then use the non-{@code null} one.</LI>
+     *      <LI>If both are the same instance then use the instance.</U>
      *      <LI>Otherwise, returns a wrapper that groups both providers.</LI>
      * </UL>
      *
@@ -78,9 +86,10 @@ public interface KeyIdentityProvider {
      */
     static KeyIdentityProvider resolveKeyIdentityProvider(
             KeyIdentityProvider identities, KeyIdentityProvider keys) {
-        if ((keys == null) || (identities == keys)) {
-            return identities;
-        } else if (identities == null) {
+        if (isEmpty(keys) || GenericUtils.isSameReference(identities, keys)) {
+            // Prefer EMPTY over null
+            return (identities == null) ? keys : identities;
+        } else if (isEmpty(identities)) {
             return keys;
         } else {
             return multiProvider(identities, keys);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderResolutionTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderResolutionTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderResolutionTest.java
new file mode 100644
index 0000000..15ebb5d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyIdentityProviderResolutionTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.keyprovider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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;
+import org.mockito.Mockito;
+
+/**
+ * TODO Add javadoc
+ *
+ * @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 KeyIdentityProviderResolutionTest extends JUnitTestSupport {
+    private final KeyIdentityProvider p1;
+    private final KeyIdentityProvider p2;
+    private final KeyIdentityProvider expected;
+
+    public KeyIdentityProviderResolutionTest(
+            KeyIdentityProvider p1, KeyIdentityProvider p2, KeyIdentityProvider expected) {
+        this.p1 = p1;
+        this.p2 = p2;
+        this.expected = expected;
+    }
+
+    @Parameters(name = "p1={0}, p2={1}, expected={2}")
+    public static List<Object[]> parameters() {
+        return new ArrayList<Object[]>() {
+            // Not serializing it
+            private static final long serialVersionUID = 1L;
+
+            {
+                add(new Object[]{null, null, null});
+                add(new Object[]{null, KeyIdentityProvider.EMPTY_KEYS_PROVIDER, KeyIdentityProvider.EMPTY_KEYS_PROVIDER});
+                add(new Object[]{KeyIdentityProvider.EMPTY_KEYS_PROVIDER, null, KeyIdentityProvider.EMPTY_KEYS_PROVIDER});
+                add(new Object[]{KeyIdentityProvider.EMPTY_KEYS_PROVIDER, KeyIdentityProvider.EMPTY_KEYS_PROVIDER, KeyIdentityProvider.EMPTY_KEYS_PROVIDER});
+
+                KeyIdentityProvider p = createKeyIdentityProvider("MOCK");
+                add(new Object[]{null, p, p});
+                add(new Object[]{KeyIdentityProvider.EMPTY_KEYS_PROVIDER, p, p});
+                add(new Object[]{p, null, p});
+                add(new Object[]{p, KeyIdentityProvider.EMPTY_KEYS_PROVIDER, p});
+            }
+
+            private KeyIdentityProvider createKeyIdentityProvider(String name) {
+                KeyIdentityProvider p = Mockito.mock(KeyIdentityProvider.class);
+                Mockito.when(p.toString()).thenReturn(name);
+                return p;
+            }
+        };
+    }
+
+    @Test
+    public void testResolveKeyIdentityProvider() {
+        assertSame(expected, KeyIdentityProvider.resolveKeyIdentityProvider(p1, p2));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/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 35b42b1..368ebc2 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
@@ -20,6 +20,7 @@ package org.apache.sshd.client;
 
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
 import org.apache.sshd.client.config.keys.ClientIdentityLoader;
+import org.apache.sshd.client.config.keys.ClientIdentityLoaderManager;
 import org.apache.sshd.client.session.ClientProxyConnectorHolder;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.config.keys.FilePasswordProviderManager;
@@ -34,6 +35,7 @@ public interface ClientFactoryManager
         extends FactoryManager,
                 ClientProxyConnectorHolder,
                 FilePasswordProviderManager,
+                ClientIdentityLoaderManager,
                 ClientAuthenticationManager {
 
     /**
@@ -84,12 +86,4 @@ public interface ClientFactoryManager
     HostConfigEntryResolver getHostConfigEntryResolver();
 
     void setHostConfigEntryResolver(HostConfigEntryResolver resolver);
-
-    /**
-     * @return The {@link ClientIdentityLoader} to use in order to load client
-     * key pair identities - never {@code null}
-     */
-    ClientIdentityLoader getClientIdentityLoader();
-
-    void setClientIdentityLoader(ClientIdentityLoader loader);
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/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 796d781..1dc6859 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
@@ -18,9 +18,7 @@
  */
 package org.apache.sshd.client;
 
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.StreamCorruptedException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.net.SocketTimeoutException;
@@ -501,7 +499,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
                     log.debug("connect({}@{}:{}) no overrides", username, host, port);
                 }
 
-                return doConnect(username, targetAddress, context, localAddress, Collections.emptyList(), true);
+                return doConnect(username, targetAddress, context, localAddress, KeyIdentityProvider.EMPTY_KEYS_PROVIDER, true);
             } else {
                 if (log.isDebugEnabled()) {
                     log.debug("connect({}@{}:{}) effective: {}", username, host, port, entry);
@@ -513,7 +511,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
             if (log.isDebugEnabled()) {
                 log.debug("connect({}@{}) not an InetSocketAddress: {}", username, targetAddress, targetAddress.getClass().getName());
             }
-            return doConnect(username, targetAddress, context, localAddress, Collections.emptyList(), true);
+            return doConnect(username, targetAddress, context, localAddress, KeyIdentityProvider.EMPTY_KEYS_PROVIDER, true);
         }
     }
 
@@ -530,60 +528,27 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         Collection<PathResource> idFiles = GenericUtils.isEmpty(hostIds)
             ? Collections.emptyList()
             : hostIds.stream()
-                .map(l -> Paths.get(l))
+                .map(Paths::get)
                 .map(PathResource::new)
                 .collect(Collectors.toCollection(() -> new ArrayList<>(hostIds.size())));
-        Collection<KeyPair> keys = loadClientIdentities(idFiles);
+        KeyIdentityProvider keys = preloadClientIdentities(idFiles);
         return doConnect(hostConfig.getUsername(), new InetSocketAddress(host, port),
                 context, localAddress, keys, !hostConfig.isIdentitiesOnly());
     }
 
-    protected List<KeyPair> loadClientIdentities(Collection<? extends NamedResource> locations) throws IOException {
-        if (GenericUtils.isEmpty(locations)) {
-            return Collections.emptyList();
-        }
-
-        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 = getFilePasswordProvider();
-        boolean debugEnabled = log.isDebugEnabled();
-        for (NamedResource l : locations) {
-            if (!loader.isValidLocation(l)) {
-                if (ignoreNonExisting) {
-                    if (debugEnabled) {
-                        log.debug("loadClientIdentities - skip non-existing identity location: {}", l);
-                    }
-                    continue;
-                }
-
-                throw new FileNotFoundException("Invalid identity location: " + l);
-            }
-
-            try {
-                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()));
-                }
-
-                ids.add(kp);
-            } catch (GeneralSecurityException e) {
-                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ") to load identity from " + l + ": " + e.getMessage());
-            }
-        }
-
-        return ids;
+    protected KeyIdentityProvider preloadClientIdentities(Collection<? extends NamedResource> locations) throws IOException {
+        return GenericUtils.isEmpty(locations)
+             ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER
+             : ClientIdentityLoader.asKeyIdentityProvider(
+                     Objects.requireNonNull(getClientIdentityLoader(), "No ClientIdentityLoader"),
+                     locations, getFilePasswordProvider(),
+                     this.getBooleanProperty(IGNORE_INVALID_IDENTITIES, DEFAULT_IGNORE_INVALID_IDENTITIES));
     }
 
     protected ConnectFuture doConnect(
             String username, SocketAddress targetAddress,
             AttributeRepository context, SocketAddress localAddress,
-            Iterable<? extends KeyPair> identities, boolean useDefaultIdentities)
+            KeyIdentityProvider identities, boolean useDefaultIdentities)
                 throws IOException {
         if (connector == null) {
             throw new IllegalStateException("SshClient not started. Please call start() method before connecting to a server");
@@ -600,7 +565,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
     protected SshFutureListener<IoConnectFuture> createConnectCompletionListener(
             ConnectFuture connectFuture, String username, SocketAddress address,
-            Iterable<? extends KeyPair> identities, boolean useDefaultIdentities) {
+            KeyIdentityProvider identities, boolean useDefaultIdentities) {
         return new SshFutureListener<IoConnectFuture>() {
             @Override
             @SuppressWarnings("synthetic-access")
@@ -643,53 +608,46 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
     protected void onConnectOperationComplete(
             IoSession ioSession, ConnectFuture connectFuture,  String username,
-            SocketAddress address, Iterable<? extends KeyPair> identities, boolean useDefaultIdentities) {
+            SocketAddress address, KeyIdentityProvider identities, boolean useDefaultIdentities) {
         AbstractClientSession session = (AbstractClientSession) AbstractSession.getSession(ioSession);
         session.setUsername(username);
         session.setConnectAddress(address);
 
         if (useDefaultIdentities) {
-            setupDefaultSessionIdentities(session);
-        }
-
-        if (identities != null) {
-            boolean traceEnabled = log.isTraceEnabled();
-            for (KeyPair kp : identities) {
-                if (traceEnabled) {
-                    log.trace("onConnectOperationComplete({}) add identity type={}, fingerprint={}",
-                        session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
-                }
-                session.addPublicKeyIdentity(kp);
-            }
+            setupDefaultSessionIdentities(session, identities);
+        } else {
+            session.setKeyIdentityProvider((identities == null) ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER : identities);
         }
 
         connectFuture.setSession(session);
     }
 
-    protected void setupDefaultSessionIdentities(ClientSession session) {
+    protected void setupDefaultSessionIdentities(ClientSession session, KeyIdentityProvider extraIdentities) {
+        boolean debugEnabled = log.isDebugEnabled();
         // check if session listener intervened
         KeyIdentityProvider kpSession = session.getKeyIdentityProvider();
         KeyIdentityProvider kpClient = getKeyIdentityProvider();
-        boolean debugEnabled = log.isDebugEnabled();
-        if (kpSession == null) {
-            session.setKeyIdentityProvider(kpClient);
-        } else {
-            if (kpSession != kpClient) {
-                if (debugEnabled) {
-                    log.debug("setupDefaultSessionIdentities({}) key identity provider override", session);
-                }
+        if (GenericUtils.isSameReference(kpSession, kpClient)) {
+            if (debugEnabled) {
+                log.debug("setupDefaultSessionIdentities({}) key identity provider override in session listener", session);
+            }
+        }
+
+        // Prefer the extra identities to come first since they were probably indicate by the host-config entry
+        KeyIdentityProvider kpEffective =
+            KeyIdentityProvider.resolveKeyIdentityProvider(extraIdentities, kpSession);
+        if (!GenericUtils.isSameReference(kpSession, kpEffective)) {
+            if (debugEnabled) {
+                log.debug("setupDefaultSessionIdentities({}) key identity provider enhanced", session);
             }
+            session.setKeyIdentityProvider(kpEffective);
         }
 
         PasswordIdentityProvider passSession = session.getPasswordIdentityProvider();
         PasswordIdentityProvider passClient = getPasswordIdentityProvider();
-        if (passSession == null) {
-            session.setPasswordIdentityProvider(passClient);
-        } else {
-            if (passSession != passClient) {
-                if (debugEnabled) {
-                    log.debug("setupDefaultSessionIdentities({}) password provider override", session);
-                }
+        if (!GenericUtils.isSameReference(passSession, passClient)) {
+            if (debugEnabled) {
+                log.debug("setupDefaultSessionIdentities({}) password provider override", session);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/98a708a5/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 b76e46c..8ede710 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
@@ -32,6 +32,7 @@ import java.util.Collections;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.client.SshClient;
@@ -214,6 +215,7 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
         entry.addIdentity(clientIdentity);
         entry.setIdentitiesOnly(true);
 
+        AtomicInteger specificIdentityLoadCount = new AtomicInteger(0);
         client.setClientIdentityLoader(new ClientIdentityLoader() {
             @Override
             public boolean isValidLocation(NamedResource location) throws IOException {
@@ -225,6 +227,7 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
                     SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
+                    specificIdentityLoadCount.incrementAndGet();
                     return specificIdentity;
                 }
 
@@ -243,12 +246,13 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
         client.setKeyIdentityProvider(provider);
 
         client.start();
-        try (ClientSession session = client.connect(entry).verify(7L, TimeUnit.SECONDS).getSession()) {
-            assertSame("Unexpected session key pairs provider", provider, session.getKeyIdentityProvider());
+        try (ClientSession session = client.connect(entry)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
             session.auth().verify(5L, TimeUnit.SECONDS);
             assertFalse("Unexpected default client identity attempted", defaultClientIdentityAttempted.get());
             assertNull("Default client identity auto-added", session.removePublicKeyIdentity(defaultIdentity));
-            assertNotNull("Entry identity not automatically added", session.removePublicKeyIdentity(specificIdentity));
+            assertEquals("Entry identity not used", 1, specificIdentityLoadCount.get());
             assertEffectiveRemoteAddress(session, entry);
         } finally {
             client.stop();