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/22 05:05:30 UTC

[7/7] mina-sshd git commit: [SSHD-757] Converted most of the key-pair identity loaders to return an Iterable instead of single instance

[SSHD-757] Converted most of the key-pair identity loaders to return an Iterable<KeyPair> instead of single instance


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

Branch: refs/heads/master
Commit: 3efd1edf63fbb406524bd9d0cfa563f3b2305789
Parents: 2f92fe5
Author: Lyor Goldstein <lg...@apache.org>
Authored: Tue Nov 20 11:50:58 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Thu Nov 22 07:05:16 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  3 +
 README.md                                       |  2 +-
 .../java/org/apache/sshd/cli/CliSupport.java    |  2 +-
 .../apache/sshd/cli/client/ScpCommandMain.java  |  2 +-
 .../sshd/cli/client/SshClientCliSupport.java    | 20 ++---
 .../sshd/cli/server/SshServerCliSupport.java    | 26 +++++--
 .../apache/sshd/cli/client/ChannelExecMain.java |  2 +-
 .../apache/sshd/cli/server/SshFsMounter.java    |  2 +-
 .../config/keys/ClientIdentitiesWatcher.java    |  6 +-
 .../config/keys/ClientIdentityFileWatcher.java  | 43 +++++------
 .../config/keys/ClientIdentityLoader.java       |  6 +-
 .../config/keys/ClientIdentityProvider.java     | 13 ++--
 .../config/keys/LazyClientIdentityIterator.java | 20 +++--
 .../keys/LazyClientKeyIdentityProvider.java     | 16 +++-
 .../sshd/common/config/keys/IdentityUtils.java  | 23 ++++--
 .../AbstractResourceKeyPairProvider.java        | 54 +++++++-------
 .../common/keyprovider/FileKeyPairProvider.java |  4 +-
 .../common/keyprovider/KeyIdentityProvider.java | 18 +++++
 .../common/util/security/SecurityUtils.java     | 11 +--
 .../AbstractGeneratorHostKeyProvider.java       | 77 ++++++++++++--------
 .../SimpleGeneratorHostKeyProvider.java         |  8 +-
 .../BuiltinClientIdentitiesWatcherTest.java     |  6 +-
 .../keys/ClientIdentityFileWatcherTest.java     | 14 ++--
 .../pem/PKCS8PEMResourceKeyPairParserTest.java  | 11 ++-
 .../AbstractGeneratorHostKeyProviderTest.java   |  2 +-
 .../keys/LazyClientIdentityIteratorTest.java    |  7 +-
 .../hosts/HostConfigEntryResolverTest.java      |  8 +-
 .../sshd/common/auth/AuthenticationTest.java    |  5 +-
 28 files changed, 253 insertions(+), 158 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index db1e874..0c59a22 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -68,6 +68,9 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 
 * Removed API(s) that used string file paths to create `FileInputStream`-s - using only `java.nio.file.Path`-s
 
+* Converted most of the key-pair identity loaders (e.g., `ClientIdentityLoader`, `ClientIdentityProvider`, etc.)
+to return an `Iterable<KeyPair>` instead of single `KeyPair` instance.
+
 ## Behavioral changes and enhancements
 
 * [SSHD-757](https://issues.apache.org/jira/browse/SSHD-757) - Added hooks and some initial code to allow (limited) usage

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 811911d..6c8a1cb 100644
--- a/README.md
+++ b/README.md
@@ -1936,7 +1936,7 @@ This code relies on the [jpgpgj](https://github.com/justinludwig/jpgpj) support
 
 In order to be able to read `authorized_keys` files that may contain _OpenPGP_ keys references, one needs to register
 the relevant `PublicKeyEntryDataResolver`-s. This is done by calling `PGPPublicKeyEntryDataResolver#registerDefaultKeyEntryDataResolvers`
-once during the _main_ code setup. This will enable the code to read authorized keys entries having the format
+once during the _main_ code setup. This will enable the code to safely read authorized keys entries having the format
 specified in the [OpenSSH PGP configuration](https://www.red-bean.com/~nemo/openssh-gpg/):
 
 ```

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
index 1ba94d2..8e59456 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
@@ -48,7 +48,7 @@ public abstract class CliSupport {
     }
 
     public static boolean showError(PrintStream stderr, String message) {
-        stderr.println(message);
+        stderr.append("ERROR: ").println(message);
         return true;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
index 3421cca..88910c2 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
@@ -149,7 +149,7 @@ public class ScpCommandMain extends SshClientCliSupport {
             Class<?> clazz = cl.loadClass(className);
             return ScpClientCreator.class.cast(clazz.newInstance());
         } catch (Exception e) {
-            stderr.append("Failed (").append(e.getClass().getSimpleName()).append(')')
+            stderr.append("WARNING: Failed (").append(e.getClass().getSimpleName()).append(')')
                 .append(" to instantiate ").append(className)
                 .append(": ").println(e.getMessage());
             stderr.flush();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 4741e72..6917c94 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
@@ -397,7 +397,8 @@ public abstract class SshClientCliSupport extends CliSupport {
                         answers[i] = stdin.readLine();
                     }
                 } catch (IOException e) {
-                    stderr.append(e.getClass().getSimpleName()).append(" while read prompts: ").println(e.getMessage());
+                    stderr.append("WARNING: ").append(e.getClass().getSimpleName())
+                        .append(" while read prompts: ").println(e.getMessage());
                 }
                 return answers;
             }
@@ -408,7 +409,8 @@ public abstract class SshClientCliSupport extends CliSupport {
                 try {
                     return stdin.readLine();
                 } catch (IOException e) {
-                    stderr.append(e.getClass().getSimpleName()).append(" while read password: ").println(e.getMessage());
+                    stderr.append("WARNING: ").append(e.getClass().getSimpleName())
+                        .append(" while read password: ").println(e.getMessage());
                     return null;
                 }
             }
@@ -438,11 +440,11 @@ public abstract class SshClientCliSupport extends CliSupport {
         }
 
         ((KnownHostsServerKeyVerifier) current).setModifiedServerKeyAcceptor((clientSession, remoteAddress, entry, expected, actual) -> {
-            stderr.append("Mismatched keys presented by ").append(Objects.toString(remoteAddress))
+            stderr.append("WARNING: Mismatched keys presented by ").append(Objects.toString(remoteAddress))
                   .append(" for entry=").println(entry);
-            stderr.append('\t').append("Expected=").append(KeyUtils.getKeyType(expected))
+            stderr.append("    ").append("Expected=").append(KeyUtils.getKeyType(expected))
                   .append('-').println(KeyUtils.getFingerPrint(expected));
-            stderr.append('\t').append("Actual=").append(KeyUtils.getKeyType(actual))
+            stderr.append("    ").append("Actual=").append(KeyUtils.getKeyType(actual))
                   .append('-').println(KeyUtils.getFingerPrint(actual));
             stderr.flush(); // just making sure
 
@@ -538,7 +540,7 @@ public abstract class SshClientCliSupport extends CliSupport {
 
         Collection<String> unsupported = result.getUnsupportedFactories();
         if (GenericUtils.size(unsupported) > 0) {
-            stderr.append("Ignored unsupported compressions: ").println(GenericUtils.join(unsupported, ','));
+            stderr.append("WARNING: Ignored unsupported compressions: ").println(GenericUtils.join(unsupported, ','));
         }
 
         return new ArrayList<>(available);
@@ -566,7 +568,7 @@ public abstract class SshClientCliSupport extends CliSupport {
 
         Collection<String> unsupported = result.getUnsupportedFactories();
         if (GenericUtils.size(unsupported) > 0) {
-            stderr.append("Ignored unsupported MACs: ").println(GenericUtils.join(unsupported, ','));
+            stderr.append("WARNING: Ignored unsupported MACs: ").println(GenericUtils.join(unsupported, ','));
         }
 
         return new ArrayList<>(available);
@@ -589,13 +591,13 @@ public abstract class SshClientCliSupport extends CliSupport {
         BuiltinCiphers.ParseResult result = BuiltinCiphers.parseCiphersList(argVal);
         Collection<? extends NamedFactory<Cipher>> available = result.getParsedFactories();
         if (GenericUtils.isEmpty(available)) {
-            showError(stderr, "No known ciphers in " + argVal);
+            showError(stderr, "WARNING: No known ciphers in " + argVal);
             return null;
         }
 
         Collection<String> unsupported = result.getUnsupportedFactories();
         if (GenericUtils.size(unsupported) > 0) {
-            stderr.append("Ignored unsupported ciphers: ").println(GenericUtils.join(unsupported, ','));
+            stderr.append("WARNING: Ignored unsupported ciphers: ").println(GenericUtils.join(unsupported, ','));
         }
 
         return new ArrayList<>(available);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
index 2521ea1..a5357f5 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
@@ -117,19 +117,33 @@ public abstract class SshServerCliSupport extends CliSupport {
             for (String keyFilePath : keyFiles) {
                 Path path = Paths.get(keyFilePath);
                 PathResource location = new PathResource(path);
+                Iterable<KeyPair> ids;
                 try (InputStream inputStream = location.openInputStream()) {
-                    KeyPair kp = SecurityUtils.loadKeyPairIdentity(null, location, inputStream, null);
-                    pairs.add(kp);
+                    ids = SecurityUtils.loadKeyPairIdentities(null, location, inputStream, null);
                 } catch (Exception e) {
-                    stderr.append("Failed (").append(e.getClass().getSimpleName()).append(')')
+                    stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')')
                         .append(" to load host key file=").append(keyFilePath)
                         .append(": ").println(e.getMessage());
                     stderr.flush();
                     throw e;
                 }
+
+                if (ids == null) {
+                    stderr.append("WARNING: No keys loaded from ").println(keyFilePath);
+                    continue;
+                }
+
+                for (KeyPair kp : ids) {
+                    if (kp == null) {
+                        stderr.append("WARNING: empty key found in ").println(keyFilePath);
+                        continue;   // debug breakpoint
+                    }
+                    pairs.add(kp);
+                }
             }
 
-            return new MappedKeyPairProvider(pairs);
+            return new MappedKeyPairProvider(
+                ValidateUtils.checkNotNullAndNotEmpty(pairs, "No key pairs loaded for provided key files"));
         }
     }
 
@@ -157,7 +171,7 @@ public abstract class SshServerCliSupport extends CliSupport {
                     SubsystemFactory factory = SubsystemFactory.class.cast(clazz.newInstance());
                     subsystems.add(factory);
                 } catch (Exception e) {
-                    stderr.append("Failed (").append(e.getClass().getSimpleName()).append(')')
+                    stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')')
                         .append(" to instantiate subsystem=").append(fqcn)
                         .append(": ").println(e.getMessage());
                     stderr.flush();
@@ -208,7 +222,7 @@ public abstract class SshServerCliSupport extends CliSupport {
             Object instance = clazz.newInstance();
             return ShellFactory.class.cast(instance);
         } catch (Exception e) {
-            stderr.append("Failed (").append(e.getClass().getSimpleName()).append(')')
+            stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')')
                 .append(" to instantiate shell factory=").append(factory)
                 .append(": ").println(e.getMessage());
             stderr.flush();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
index 99d0a7d..8afcd42 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
@@ -59,7 +59,7 @@ public class ChannelExecMain extends BaseTestSupport {
                         stdout.append('\t').println(l);
                     }
                 } catch (Exception e) {
-                    stderr.append(e.getClass().getSimpleName()).append(": ").println(e.getMessage());
+                    stderr.append("WARNING: ").append(e.getClass().getSimpleName()).append(": ").println(e.getMessage());
                 }
 
                 stdout.append("Execute ").append(command).print(" again [y]/n ");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
index 0fbc926..6c17416 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
@@ -126,7 +126,7 @@ public final class SshFsMounter extends SshServerCliSupport {
                 callback.onExit(0);
             } catch (Exception e) {
                 log.error("run(" + username + ")[" + command + "] " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
-                stderr.append(e.getClass().getSimpleName()).append(": ").println(e.getMessage());
+                stderr.append("ERROR: ").append(e.getClass().getSimpleName()).append(": ").println(e.getMessage());
                 callback.onExit(-1, e.toString());
             }
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 d3e567c..dbf9c4a 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
@@ -75,12 +75,12 @@ public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements
     }
 
     protected Iterable<KeyPair> loadKeys(SessionContext session, Predicate<? super KeyPair> filter) {
-        return ClientIdentityProvider.lazyKeysLoader(providers, p -> doGetKeyPair(session, p), filter);
+        return ClientIdentityProvider.lazyKeysLoader(providers, p -> doGetKeyPairs(session, p), filter);
     }
 
-    protected KeyPair doGetKeyPair(SessionContext session, ClientIdentityProvider p) {
+    protected Iterable<KeyPair> doGetKeyPairs(SessionContext session, ClientIdentityProvider p) {
         try {
-            KeyPair kp = p.getClientIdentity(session);
+            Iterable<KeyPair> kp = p.getClientIdentities(session);
             if (kp == null) {
                 if (log.isDebugEnabled()) {
                     log.debug("loadKeys({}) no key loaded", p);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 56f946d..eabfab8 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
@@ -45,7 +45,7 @@ import org.apache.sshd.common.util.io.resource.PathResource;
 public class ClientIdentityFileWatcher
         extends ModifiableFileWatcher
         implements ClientIdentityProvider, ClientIdentityLoaderHolder, FilePasswordProviderHolder {
-    private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
+    private final AtomicReference<Iterable<KeyPair>> identitiesHolder = new AtomicReference<>(null);
     private final ClientIdentityLoaderHolder loaderHolder;
     private final FilePasswordProviderHolder providerHolder;
     private final boolean strict;
@@ -89,30 +89,27 @@ public class ClientIdentityFileWatcher
     }
 
     @Override
-    public KeyPair getClientIdentity(SessionContext session) throws IOException, GeneralSecurityException {
+    public Iterable<KeyPair> getClientIdentities(SessionContext session)
+            throws IOException, GeneralSecurityException {
         if (!checkReloadRequired()) {
-            return identityHolder.get();
+            return identitiesHolder.get();
         }
 
-        KeyPair kp = identityHolder.getAndSet(null);     // start fresh
+        Iterable<KeyPair> kp = identitiesHolder.getAndSet(null);     // start fresh
         Path path = getPath();
         if (!exists()) {
-            return identityHolder.get();
+            return identitiesHolder.get();
         }
 
-        KeyPair id = reloadClientIdentity(session, path);
-        if (!KeyUtils.compareKeyPairs(kp, id)) {
-            if (log.isDebugEnabled()) {
-                log.debug("getClientIdentity({}) identity {}", path, (kp == null) ? "loaded" : "re-loaded");
-            }
-        }
+        Iterable<KeyPair> id = reloadClientIdentities(session, path);
 
         updateReloadAttributes();
-        identityHolder.set(id);
-        return identityHolder.get();
+        identitiesHolder.set(id);
+        return identitiesHolder.get();
     }
 
-    protected KeyPair reloadClientIdentity(SessionContext session, Path path) throws IOException, GeneralSecurityException {
+    protected Iterable<KeyPair> reloadClientIdentities(SessionContext session, Path path)
+            throws IOException, GeneralSecurityException {
         if (isStrict()) {
             Map.Entry<String, Object> violation =
                 KeyUtils.validateStrictKeyFilePermissions(path, IoUtils.EMPTY_LINK_OPTIONS);
@@ -127,18 +124,22 @@ public class ClientIdentityFileWatcher
         PathResource location = new PathResource(path);
         ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
         if (idLoader.isValidLocation(location)) {
-            KeyPair kp = idLoader.loadClientIdentity(session, location, getFilePasswordProvider());
+            Iterable<KeyPair> ids = idLoader.loadClientIdentities(session, location, getFilePasswordProvider());
             if (log.isTraceEnabled()) {
-                PublicKey key = (kp == null) ? null : kp.getPublic();
-                if (key != null) {
-                    log.trace("reloadClientIdentity({}) loaded {}-{}",
-                          location, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                if (ids == null) {
+                    log.trace("reloadClientIdentity({}) no keys loaded", location);
                 } else {
-                    log.trace("reloadClientIdentity({}) no key loaded", location);
+                    for (KeyPair kp : ids) {
+                        PublicKey key = (kp == null) ? null : kp.getPublic();
+                        if (key != null) {
+                            log.trace("reloadClientIdentity({}) loaded {}-{}",
+                                location, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                        }
+                    }
                 }
             }
 
-            return kp;
+            return ids;
         }
 
         if (log.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 ceb914e..ae30f40 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
@@ -58,13 +58,13 @@ public interface ClientIdentityLoader {
         }
 
         @Override
-        public KeyPair loadClientIdentity(
+        public Iterable<KeyPair> loadClientIdentities(
                 SessionContext session, NamedResource location, FilePasswordProvider provider)
                     throws IOException, GeneralSecurityException {
             Path path = toPath(location);
             PathResource resource = new PathResource(path);
             try (InputStream inputStream = resource.openInputStream()) {
-                return SecurityUtils.loadKeyPairIdentity(session, resource, inputStream, provider);
+                return SecurityUtils.loadKeyPairIdentities(session, resource, inputStream, provider);
             }
         }
 
@@ -105,7 +105,7 @@ public interface ClientIdentityLoader {
      * @throws GeneralSecurityException If failed to convert the contents into
      * a valid identity
      */
-    KeyPair loadClientIdentity(
+    Iterable<KeyPair> loadClientIdentities(
         SessionContext session, NamedResource location, FilePasswordProvider provider)
             throws IOException, GeneralSecurityException;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
index 8c52d4f..9d0f989 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
@@ -40,24 +40,25 @@ public interface ClientIdentityProvider {
      *
      * @param session The {@link SessionContext} for invoking this load command - may
      * be {@code null} if not invoked within a session context (e.g., offline tool).
-     * @return The client identity - may be {@code null} if no currently
+     * @return The client identities - may be {@code null}/empty if no currently
      * available identity from this provider. <B>Note:</B> the provider
      * may return a <U>different</U> value every time this method is called
      * - e.g., if it is (re-)loading contents from a file.
      * @throws IOException If failed to load the identity
      * @throws GeneralSecurityException If failed to parse the identity
      */
-    KeyPair getClientIdentity(SessionContext session) throws IOException, GeneralSecurityException;
+    Iterable<KeyPair> getClientIdentities(SessionContext session)
+        throws IOException, GeneralSecurityException;
 
     /**
      * Wraps a {@link KeyPair} into a {@link ClientIdentityProvider} that
-     * simply returns this value as it {@link #getClientIdentity()}.
+     * simply returns this value as it {@link #getClientIdentities(SessionContext)}.
      *
      * @param kp The {@link KeyPair} instance (including {@code null})
      * @return The wrapping provider
      */
     static ClientIdentityProvider of(KeyPair kp) {
-        return session -> kp;
+        return session -> Collections.singletonList(kp);
     }
 
     /**
@@ -80,7 +81,7 @@ public interface ClientIdentityProvider {
      */
     static Iterable<KeyPair> lazyKeysLoader(
             Iterable<? extends ClientIdentityProvider> providers,
-            Function<? super ClientIdentityProvider, ? extends KeyPair> kpExtractor,
+            Function<? super ClientIdentityProvider, ? extends Iterable<? extends KeyPair>> kpExtractor,
             Predicate<? super KeyPair> filter) {
         Objects.requireNonNull(kpExtractor, "No key pair extractor provided");
         if (providers == null) {
@@ -116,7 +117,7 @@ public interface ClientIdentityProvider {
      */
     static Iterator<KeyPair> lazyKeysIterator(
             Iterator<? extends ClientIdentityProvider> providers,
-            Function<? super ClientIdentityProvider, ? extends KeyPair> kpExtractor,
+            Function<? super ClientIdentityProvider, ? extends Iterable<? extends KeyPair>> kpExtractor,
             Predicate<? super KeyPair> filter) {
         Objects.requireNonNull(kpExtractor, "No key pair extractor provided");
         return (providers == null)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientIdentityIterator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientIdentityIterator.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientIdentityIterator.java
index b8b04ae..a3e5a2e 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientIdentityIterator.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/LazyClientIdentityIterator.java
@@ -26,6 +26,8 @@ import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+
 /**
  * Wraps several {@link ClientIdentityProvider} into a {@link KeyPair}
  * {@link Iterator} that invokes each provider &quot;lazily&quot; - i.e.,
@@ -36,10 +38,11 @@ import java.util.function.Predicate;
  */
 public class LazyClientIdentityIterator implements Iterator<KeyPair> {
     protected boolean finished;
+    protected Iterator<? extends KeyPair> currentIdentities;
     protected KeyPair currentPair;
 
     private final Iterator<? extends ClientIdentityProvider> providers;
-    private final Function<? super ClientIdentityProvider, ? extends KeyPair> kpExtractor;
+    private final Function<? super ClientIdentityProvider, ? extends Iterable<? extends KeyPair>> kpExtractor;
     private final Predicate<? super KeyPair> filter;
 
     /**
@@ -52,7 +55,7 @@ public class LazyClientIdentityIterator implements Iterator<KeyPair> {
      */
     public LazyClientIdentityIterator(
             Iterator<? extends ClientIdentityProvider> providers,
-            Function<? super ClientIdentityProvider, ? extends KeyPair> kpExtractor,
+            Function<? super ClientIdentityProvider, ? extends Iterable<? extends KeyPair>> kpExtractor,
             Predicate<? super KeyPair> filter) {
         this.providers = providers;
         this.kpExtractor = Objects.requireNonNull(kpExtractor, "No key pair extractor provided");
@@ -63,7 +66,7 @@ public class LazyClientIdentityIterator implements Iterator<KeyPair> {
         return providers;
     }
 
-    public Function<? super ClientIdentityProvider, ? extends KeyPair> getIdentityExtractor() {
+    public Function<? super ClientIdentityProvider, ? extends Iterable<? extends KeyPair>> getIdentitiesExtractor() {
         return kpExtractor;
     }
 
@@ -83,7 +86,12 @@ public class LazyClientIdentityIterator implements Iterator<KeyPair> {
             return false;
         }
 
-        Function<? super ClientIdentityProvider, ? extends KeyPair> x = getIdentityExtractor();
+        currentPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
+        if (currentPair != null) {
+            return true;
+        }
+
+        Function<? super ClientIdentityProvider, ? extends Iterable<? extends KeyPair>> x = getIdentitiesExtractor();
         Predicate<? super KeyPair> f = getFilter();
         while (provs.hasNext()) {
             ClientIdentityProvider p = provs.next();
@@ -91,7 +99,9 @@ public class LazyClientIdentityIterator implements Iterator<KeyPair> {
                 continue;
             }
 
-            currentPair = x.apply(p);
+            Iterable<? extends KeyPair> ids = x.apply(p);
+            currentIdentities = (ids == null) ? null : ids.iterator();
+            currentPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
             if (currentPair == null) {
                 continue;
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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
index 05596be..cd7a8e0 100644
--- 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
@@ -75,6 +75,7 @@ public class LazyClientKeyIdentityProvider implements KeyIdentityProvider, Clien
     }
 
     @Override
+    @SuppressWarnings("checkstyle:anoninnerlength")
     public Iterable<KeyPair> loadKeys(SessionContext session)
             throws IOException, GeneralSecurityException {
         Collection<? extends NamedResource> locs = getLocations();
@@ -84,6 +85,7 @@ public class LazyClientKeyIdentityProvider implements KeyIdentityProvider, Clien
 
         return () -> new Iterator<KeyPair>() {
             private final Iterator<? extends NamedResource> iter = locs.iterator();
+            private Iterator<KeyPair> currentIdentities;
             private KeyPair currentPair;
             private boolean finished;
 
@@ -93,15 +95,23 @@ public class LazyClientKeyIdentityProvider implements KeyIdentityProvider, Clien
                     return false;
                 }
 
+                currentPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
+                if (currentPair != null) {
+                    return true;
+                }
+
                 while (iter.hasNext()) {
                     NamedResource l = iter.next();
+                    Iterable<KeyPair> ids;
                     try {
-                        currentPair = loadClientIdentity(session, l);
+                        ids = loadClientIdentities(session, l);
                     } catch (IOException | GeneralSecurityException e) {
                         throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ")"
                             + " to load key from " + l.getName() + ": " + e.getMessage(), e);
                     }
 
+                    currentIdentities = (ids == null) ? null : ids.iterator();
+                    currentPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
                     if (currentPair != null) {
                         return true;
                     }
@@ -132,7 +142,7 @@ public class LazyClientKeyIdentityProvider implements KeyIdentityProvider, Clien
         };
     }
 
-    protected KeyPair loadClientIdentity(SessionContext session, NamedResource location)
+    protected Iterable<KeyPair> loadClientIdentities(SessionContext session, NamedResource location)
             throws IOException, GeneralSecurityException {
         ClientIdentityLoader loader = getClientIdentityLoader();
         boolean ignoreInvalid = isIgnoreNonExisting();
@@ -152,6 +162,6 @@ public class LazyClientKeyIdentityProvider implements KeyIdentityProvider, Clien
             throw e;
         }
 
-        return loader.loadClientIdentity(session, location, getFilePasswordProvider());
+        return loader.loadClientIdentities(session, location, getFilePasswordProvider());
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
index 0212806..82f8454 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
@@ -28,6 +28,7 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.Collections;
 import java.util.Map;
+import java.util.NavigableMap;
 import java.util.TreeMap;
 
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
@@ -133,29 +134,37 @@ public final class IdentityUtils {
      *                 to {@code FilePasswordProvider#getPassword} is the path of the
      *                 file whose key is to be loaded
      * @param options  The {@link OpenOption}s to use when reading the key data
-     * @return A {@link Map} of the identities where key=identity type (case
+     * @return A {@link NavigableMap} of the identities where key=identity type (case
      * <U>insensitive</U>), value=the {@link KeyPair} of the identity
      * @throws IOException              If failed to access the file system
      * @throws GeneralSecurityException If failed to load the keys
      * @see SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)
      */
-    public static Map<String, KeyPair> loadIdentities(
+    public static NavigableMap<String, KeyPair> loadIdentities(
             SessionContext session, Map<String, ? extends Path> paths, FilePasswordProvider provider, OpenOption... options)
                 throws IOException, GeneralSecurityException {
         if (GenericUtils.isEmpty(paths)) {
-            return Collections.emptyMap();
+            return Collections.emptyNavigableMap();
         }
 
-        Map<String, KeyPair> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        NavigableMap<String, KeyPair> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
         // Cannot use forEach because the potential for IOExceptions being thrown
         for (Map.Entry<String, ? extends Path> pe : paths.entrySet()) {
             String type = pe.getKey();
             Path path = pe.getValue();
-            PathResource location = new PathResource(path);
+            PathResource location = new PathResource(path, options);
+            Iterable<KeyPair> pairs;
             try (InputStream inputStream = location.openInputStream()) {
-                KeyPair kp = SecurityUtils.loadKeyPairIdentity(session, location, inputStream, provider);
+                pairs = SecurityUtils.loadKeyPairIdentities(session, location, inputStream, provider);
+            }
+
+            if (pairs == null) {
+                continue;
+            }
+
+            for (KeyPair kp : pairs) {
                 KeyPair prev = ids.put(type, kp);
-                ValidateUtils.checkTrue(prev == null, "Multiple keys for type=%s", type);
+                ValidateUtils.checkTrue(prev == null, "Multiple keys for type=%s due to %s", type, path);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
index fd33ec2..ff78317 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
@@ -24,7 +24,6 @@ import java.io.InputStream;
 import java.io.StreamCorruptedException;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
-import java.security.PublicKey;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -36,7 +35,6 @@ import java.util.TreeSet;
 
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -55,7 +53,7 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
      * practice to have 2 key files that differ from one another only in their
      * case...
      */
-    private final Map<String, KeyPair> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    private final Map<String, Iterable<KeyPair>> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
 
     protected AbstractResourceKeyPairProvider() {
         super();
@@ -126,60 +124,55 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
         return IoResource.forResource(resource);
     }
 
-    protected KeyPair doLoadKey(SessionContext session, R resource)
+    protected Iterable<KeyPair> doLoadKeys(SessionContext session, R resource)
             throws IOException, GeneralSecurityException {
         IoResource<?> ioResource =
             ValidateUtils.checkNotNull(getIoResource(session, resource), "No I/O resource available for %s", resource);
         String resourceKey =
             ValidateUtils.checkNotNullAndNotEmpty(ioResource.getName(), "No resource string value for %s", resource);
-        KeyPair kp;
+        Iterable<KeyPair> ids;
         synchronized (cacheMap) {
             // check if lucky enough to have already loaded this file
-            kp = cacheMap.get(resourceKey);
+            ids = cacheMap.get(resourceKey);
         }
 
-        if (kp != null) {
+        if (ids != null) {
             if (log.isTraceEnabled()) {
-                PublicKey key = kp.getPublic();
-                log.trace("doLoadKey({}) use cached key {}-{}",
-                      resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                log.trace("doLoadKeys({}) using cached identifiers", resourceKey);
             }
-            return kp;
+            return ids;
         }
 
-        kp = doLoadKey(session, ioResource, resource, getPasswordFinder());
-        if (kp != null) {
+        ids = doLoadKeys(session, ioResource, resource, getPasswordFinder());
+        if (ids != null) {
             boolean reusedKey;
             synchronized (cacheMap) {
                 // if somebody else beat us to it, use the cached key - just in case file contents changed
                 reusedKey = cacheMap.containsKey(resourceKey);
                 if (reusedKey) {
-                    kp = cacheMap.get(resourceKey);
+                    ids = cacheMap.get(resourceKey);
                 } else {
-                    cacheMap.put(resourceKey, kp);
+                    cacheMap.put(resourceKey, ids);
                 }
             }
 
             if (log.isDebugEnabled()) {
-                PublicKey key = kp.getPublic();
-                log.debug("doLoadKey({}) {} {}-{}",
-                      resourceKey, reusedKey ? "re-loaded" : "loaded",
-                      KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                log.debug("doLoadKeys({}) {}", resourceKey, reusedKey ? "re-loaded" : "loaded");
             }
         } else {
             if (log.isDebugEnabled()) {
-                log.debug("doLoadKey({}) no key loaded", resourceKey);
+                log.debug("doLoadKeys({}) no key loaded", resourceKey);
             }
         }
 
-        return kp;
+        return ids;
     }
 
-    protected KeyPair doLoadKey(
+    protected Iterable<KeyPair> doLoadKeys(
             SessionContext session, NamedResource resourceKey, R resource, FilePasswordProvider provider)
                 throws IOException, GeneralSecurityException {
         try (InputStream inputStream = openKeyPairResource(session, resourceKey, resource)) {
-            return doLoadKey(session, resourceKey, inputStream, provider);
+            return doLoadKeys(session, resourceKey, inputStream, provider);
         }
     }
 
@@ -193,15 +186,16 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
         throw new StreamCorruptedException("Cannot open resource data for " + resource);
     }
 
-    protected KeyPair doLoadKey(
+    protected Iterable<KeyPair> doLoadKeys(
             SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
                 throws IOException, GeneralSecurityException {
-        return SecurityUtils.loadKeyPairIdentity(session, resourceKey, inputStream, provider);
+        return SecurityUtils.loadKeyPairIdentities(session, resourceKey, inputStream, provider);
     }
 
     protected class KeyPairIterator implements Iterator<KeyPair> {
         protected final SessionContext session;
         private final Iterator<? extends R> iterator;
+        private Iterator<KeyPair> currentIdentities;
         private KeyPair nextKeyPair;
         private boolean nextKeyPairSet;
 
@@ -233,11 +227,19 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
 
         @SuppressWarnings("synthetic-access")
         private boolean setNextObject() {
+            nextKeyPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
+            if (nextKeyPair != null) {
+                nextKeyPairSet = true;
+                return true;
+            }
+
             boolean debugEnabled = log.isDebugEnabled();
             while (iterator.hasNext()) {
                 R r = iterator.next();
                 try {
-                    nextKeyPair = doLoadKey(session, r);
+                    Iterable<KeyPair> ids = doLoadKeys(session, r);
+                    currentIdentities = (ids == null) ? null : ids.iterator();
+                    nextKeyPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
                 } catch (Throwable e) {
                     log.warn("Failed (" + e.getClass().getSimpleName() + ")"
                            + " to load key resource=" + r + ": " + e.getMessage());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
index 613e0cf..acb1767 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
@@ -81,8 +81,8 @@ public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
     }
 
     @Override
-    protected KeyPair doLoadKey(SessionContext session, Path resource)
+    protected Iterable<KeyPair> doLoadKeys(SessionContext session, Path resource)
             throws IOException, GeneralSecurityException {
-        return super.doLoadKey(session, (resource == null) ? null : resource.toAbsolutePath());
+        return super.doLoadKeys(session, (resource == null) ? null : resource.toAbsolutePath());
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 6ce5996..dafbbaf 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
@@ -186,4 +186,22 @@ public interface KeyIdentityProvider {
     static KeyIdentityProvider wrapKeyPairs(Iterable<KeyPair> pairs) {
         return (pairs == null) ? EMPTY_KEYS_PROVIDER : session -> pairs;
     }
+
+    /**
+     * Attempts to find the first non-{@code null} {@link KeyPair}
+     *
+     * @param ids The {@link Iterator} - ignored if {@code null} or no next element available
+     * @return The first non-{@code null} key pair found in the iterator - {@code null} if
+     * all elements exhausted without such an entry
+     */
+    static KeyPair exhaustCurrentIdentities(Iterator<? extends KeyPair> ids) {
+        while ((ids != null) && ids.hasNext()) {
+            KeyPair kp = ids.next();
+            if (kp != null) {
+                return kp;
+            }
+        }
+
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
index cac845e..622a55c 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -35,7 +35,6 @@ import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.Signature;
 import java.security.cert.CertificateFactory;
-import java.security.spec.InvalidKeySpecException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -477,11 +476,11 @@ public final class SecurityUtils {
      * @param inputStream The {@link InputStream} for the <U>private</U> key
      * @param provider    A {@link FilePasswordProvider} - may be {@code null}
      *                    if the loaded key is <U>guaranteed</U> not to be encrypted
-     * @return The loaded {@link KeyPair}
+     * @return The loaded {@link KeyPair}-s - or {@code null} if none loaded
      * @throws IOException              If failed to read/parse the input stream
      * @throws GeneralSecurityException If failed to generate the keys
      */
-    public static KeyPair loadKeyPairIdentity(
+    public static Iterable<KeyPair> loadKeyPairIdentities(
             SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
                 throws IOException, GeneralSecurityException {
         KeyPairResourceParser parser = getKeyPairResourceParser();
@@ -495,11 +494,7 @@ public final class SecurityUtils {
             return null;
         }
 
-        if (numLoaded != 1) {
-            throw new InvalidKeySpecException("Multiple private key pairs N/A: " + resourceKey);
-        }
-
-        return GenericUtils.head(ids);
+        return ids;
     }
 
     /* -------------------------------------------------------------------- */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
index 1ed3a6c..bce84d6 100644
--- a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
@@ -30,6 +30,7 @@ import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.PublicKey;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -43,6 +44,7 @@ import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeySizeIndicator;
 import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -61,7 +63,7 @@ public abstract class AbstractGeneratorHostKeyProvider
     public static final String DEFAULT_ALGORITHM = KeyUtils.RSA_ALGORITHM;
     public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
 
-    private final AtomicReference<KeyPair> keyPairHolder = new AtomicReference<>();
+    private final AtomicReference<Iterable<KeyPair>> keyPairHolder = new AtomicReference<>();
 
     private Path path;
     private String algorithm = DEFAULT_ALGORITHM;
@@ -116,29 +118,27 @@ public abstract class AbstractGeneratorHostKeyProvider
     }
 
     public void clearLoadedKeys() {
-        KeyPair kp;
+        Iterable<KeyPair> ids;
         synchronized (keyPairHolder) {
-            kp = keyPairHolder.getAndSet(null);
+            ids = keyPairHolder.getAndSet(null);
         }
 
-        if ((kp != null) & log.isDebugEnabled()) {
-            PublicKey key = kp.getPublic();
-            log.debug("clearLoadedKeys({}) removed key={}-{}",
-                  getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+        if ((ids != null) & log.isDebugEnabled()) {
+            log.debug("clearLoadedKeys({}) removed keys", getPath());
         }
     }
 
     @Override   // co-variant return
     public synchronized List<KeyPair> loadKeys(SessionContext session) {
         Path keyPath = getPath();
-        KeyPair kp;
+        Iterable<KeyPair> ids;
         synchronized (keyPairHolder) {
-            kp = keyPairHolder.get();
-            if (kp == null) {
+            ids = keyPairHolder.get();
+            if (ids == null) {
                 try {
-                    kp = resolveKeyPair(session, keyPath);
-                    if (kp != null) {
-                        keyPairHolder.set(kp);
+                    ids = resolveKeyPairs(session, keyPath);
+                    if (ids != null) {
+                        keyPairHolder.set(ids);
                     }
                 } catch (Throwable t) {
                     log.warn("loadKeys({}) Failed ({}) to resolve: {}",
@@ -150,21 +150,32 @@ public abstract class AbstractGeneratorHostKeyProvider
             }
         }
 
-        if (kp == null) {
-            return Collections.emptyList();
-        } else {
-            return Collections.singletonList(kp);
+        List<KeyPair> pairs = Collections.emptyList();
+        if (ids instanceof List<?>) {
+            pairs = (List<KeyPair>) ids;
+        } else if (ids != null) {
+            pairs = new ArrayList<>();
+            for (KeyPair kp : ids) {
+                if (kp == null) {
+                    continue;
+                }
+
+                pairs.add(kp);
+            }
         }
+
+        return pairs;
     }
 
-    protected KeyPair resolveKeyPair(SessionContext session, Path keyPath) throws IOException, GeneralSecurityException {
+    protected Iterable<KeyPair> resolveKeyPairs(SessionContext session, Path keyPath)
+            throws IOException, GeneralSecurityException {
         String alg = getAlgorithm();
-        KeyPair kp;
         if (keyPath != null) {
             try {
-                kp = loadFromFile(session, alg, keyPath);
+                Iterable<KeyPair> ids = loadFromFile(session, alg, keyPath);
+                KeyPair kp = GenericUtils.head(ids);
                 if (kp != null) {
-                    return kp;
+                    return ids;
                 }
             } catch (Throwable e) {
                 log.warn("resolveKeyPair({}) Failed ({}) to load: {}",
@@ -176,6 +187,7 @@ public abstract class AbstractGeneratorHostKeyProvider
         }
 
         // either no file specified or no key in file
+        KeyPair kp = null;
         try {
             kp = generateKeyPair(alg);
             if (kp == null) {
@@ -185,11 +197,11 @@ public abstract class AbstractGeneratorHostKeyProvider
             if (log.isDebugEnabled()) {
                 PublicKey key = kp.getPublic();
                 log.debug("resolveKeyPair({}) generated {} key={}-{}",
-                          keyPath, alg, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                      keyPath, alg, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
             }
         } catch (Throwable e) {
             log.warn("resolveKeyPair({})[{}] Failed ({}) to generate {} key-pair: {}",
-                     keyPath, alg, e.getClass().getSimpleName(), alg, e.getMessage());
+                 keyPath, alg, e.getClass().getSimpleName(), alg, e.getMessage());
             if (log.isDebugEnabled()) {
                 log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] key-pair generation failure details", e);
             }
@@ -209,20 +221,23 @@ public abstract class AbstractGeneratorHostKeyProvider
             }
         }
 
-        return kp;
+        return Collections.singletonList(kp);
     }
 
-    protected KeyPair loadFromFile(SessionContext session, String alg, Path keyPath) throws IOException, GeneralSecurityException {
+    protected Iterable<KeyPair> loadFromFile(SessionContext session, String alg, Path keyPath)
+            throws IOException, GeneralSecurityException {
         LinkOption[] options = IoUtils.getLinkOptions(true);
         if ((!Files.exists(keyPath, options)) || (!Files.isRegularFile(keyPath, options))) {
             return null;
         }
 
-        KeyPair kp = readKeyPair(session, keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
+        Iterable<KeyPair> ids = readKeyPairs(session, keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
+        KeyPair kp = GenericUtils.head(ids);
         if (kp == null) {
             return null;
         }
 
+        // Assume all keys are of same type
         PublicKey key = kp.getPublic();
         String keyAlgorithm = key.getAlgorithm();
         if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyAlgorithm)) {
@@ -236,7 +251,7 @@ public abstract class AbstractGeneratorHostKeyProvider
                 log.debug("resolveKeyPair({}) loaded key={}-{}",
                       keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
             }
-            return kp;
+            return ids;
         }
 
         // Not same algorithm - start again
@@ -248,17 +263,17 @@ public abstract class AbstractGeneratorHostKeyProvider
         return null;
     }
 
-    protected KeyPair readKeyPair(SessionContext session, Path keyPath, OpenOption... options)
+    protected Iterable<KeyPair> readKeyPairs(SessionContext session, Path keyPath, OpenOption... options)
             throws IOException, GeneralSecurityException {
         PathResource location = new PathResource(keyPath, options);
         try (InputStream inputStream = location.openInputStream()) {
-            return doReadKeyPair(session, location, inputStream);
+            return doReadKeyPairs(session, location, inputStream);
         }
     }
 
-    protected KeyPair doReadKeyPair(SessionContext session, NamedResource resourceKey, InputStream inputStream)
+    protected Iterable<KeyPair> doReadKeyPairs(SessionContext session, NamedResource resourceKey, InputStream inputStream)
             throws IOException, GeneralSecurityException {
-        return SecurityUtils.loadKeyPairIdentity(session, resourceKey, inputStream, null);
+        return SecurityUtils.loadKeyPairIdentities(session, resourceKey, inputStream, null);
     }
 
     protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
index c0ccd16..95c6bb0 100644
--- a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
@@ -27,6 +27,7 @@ import java.nio.file.Path;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
 
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.session.SessionContext;
@@ -46,15 +47,18 @@ public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProv
     }
 
     @Override
-    protected KeyPair doReadKeyPair(SessionContext session, NamedResource resourceKey, InputStream inputStream)
+    protected Iterable<KeyPair> doReadKeyPairs(SessionContext session, NamedResource resourceKey, InputStream inputStream)
             throws IOException, GeneralSecurityException {
+        KeyPair kp;
         try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
             try {
-                return (KeyPair) r.readObject();
+                kp = (KeyPair) r.readObject();
             } catch (ClassNotFoundException e) {
                 throw new InvalidKeySpecException("Missing classes: " + e.getMessage(), e);
             }
         }
+
+        return Collections.singletonList(kp);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
index 64573c4..6e6541f 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
@@ -30,6 +30,7 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.EnumMap;
 import java.util.Map;
@@ -80,12 +81,13 @@ public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
 
         ClientIdentityLoader loader = new ClientIdentityLoader() {
             @Override
-            public KeyPair loadClientIdentity(
+            public Iterable<KeyPair> loadClientIdentities(
                     SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 BuiltinIdentities id = findIdentity(location);
                 assertNotNull("Invalid location: " + location, id);
-                return idsMap.get(id);
+                KeyPair kp = idsMap.get(id);
+                return (kp == null) ? null : Collections.singletonList(kp);
             }
 
             @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
index 2cf3828..4205205 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
@@ -28,6 +28,7 @@ import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -35,6 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JUnitTestSupport;
@@ -61,11 +63,11 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
         KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
         ClientIdentityLoader loader = new ClientIdentityLoader() {
             @Override
-            public KeyPair loadClientIdentity(
+            public Iterable<KeyPair> loadClientIdentities(
                     SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 assertTrue("Invalid location: " + location, isValidLocation(location));
-                return identity;
+                return Collections.singletonList(identity);
             }
 
             @Override
@@ -82,10 +84,11 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
         AtomicInteger reloadCount = new AtomicInteger(0);
         ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) {
             @Override
-            protected KeyPair reloadClientIdentity(SessionContext session, Path path) throws IOException, GeneralSecurityException {
+            protected Iterable<KeyPair> reloadClientIdentities(SessionContext session, Path path)
+                    throws IOException, GeneralSecurityException {
                 assertEquals("Mismatched client identity path", idFile, path);
                 reloadCount.incrementAndGet();
-                return super.reloadClientIdentity(session, path);
+                return super.reloadClientIdentities(session, path);
             }
         };
         Files.deleteIfExists(idFile);
@@ -118,7 +121,8 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
     private static void testIdentityReload(
             String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount)
                 throws Exception {
-        KeyPair actualIdentity = provider.getClientIdentity(null);
+        Iterable<KeyPair> ids = provider.getClientIdentities(null);
+        KeyPair actualIdentity = GenericUtils.head(ids);
         assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity);
         assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue());
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
index 445de2e..afa3738 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
@@ -33,6 +33,7 @@ import org.apache.commons.ssl.PEMItem;
 import org.apache.commons.ssl.PEMUtil;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.JUnitTestSupport;
@@ -93,10 +94,12 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
             os.close();
 
             try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
-                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(null, NamedResource.ofName(getCurrentTestName()), bais, null);
-
-                assertEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
-                assertEquals("Mismatched private key", prv1, kp2.getPrivate());
+                Iterable<KeyPair> ids = SecurityUtils.loadKeyPairIdentities(
+                        null, NamedResource.ofName(getCurrentTestName()), bais, null);
+                KeyPair kp2 = GenericUtils.head(ids);
+                assertNotNull("No identity loaded", kp2);
+                assertKeyEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
+                assertKeyEquals("Mismatched private key", prv1, kp2.getPrivate());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
index 7cd690c..29a77c7 100644
--- a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
@@ -69,7 +69,7 @@ public class AbstractGeneratorHostKeyProviderTest extends JUnitTestSupport {
         }
 
         @Override
-        protected KeyPair doReadKeyPair(
+        protected Iterable<KeyPair> doReadKeyPairs(
                 SessionContext session, NamedResource resourceKey, InputStream inputStream)
                     throws IOException, GeneralSecurityException {
             return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java b/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
index 32f2712..8e072c1 100644
--- a/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
+++ b/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java
@@ -23,6 +23,7 @@ import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -57,7 +58,7 @@ public class LazyClientIdentityIteratorTest extends JUnitTestSupport {
         Iterable<KeyPair> ids = ClientIdentityProvider.lazyKeysLoader(
             providers, p -> {
                 try {
-                    return p.getClientIdentity(null);
+                    return p.getClientIdentities(null);
                 } catch (Exception e) {
                     throw new RuntimeException("Unexpected " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
                 }
@@ -94,9 +95,9 @@ public class LazyClientIdentityIteratorTest extends JUnitTestSupport {
         }
 
         @Override
-        public KeyPair getClientIdentity(SessionContext session) {
+        public Iterable<KeyPair> getClientIdentities(SessionContext session) {
             loadCount++;
-            return getKeyPair();
+            return Collections.singletonList(getKeyPair());
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 8ede710..2fd8584 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
@@ -156,11 +156,11 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
             }
 
             @Override
-            public KeyPair loadClientIdentity(
+            public Iterable<KeyPair> loadClientIdentities(
                     SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
-                    return identity;
+                    return Collections.singletonList(identity);
                 }
 
                 throw new FileNotFoundException("Unknown location: " + location);
@@ -223,12 +223,12 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
             }
 
             @Override
-            public KeyPair loadClientIdentity(
+            public Iterable<KeyPair> loadClientIdentities(
                     SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 if (isValidLocation(location)) {
                     specificIdentityLoadCount.incrementAndGet();
-                    return specificIdentity;
+                    return Collections.singletonList(specificIdentity);
                 }
 
                 throw new FileNotFoundException("Unknown location: " + location);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/3efd1edf/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 34c418b..a2f3374 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
@@ -924,11 +924,12 @@ public class AuthenticationTest extends BaseTestSupport {
                         URL location = getClass().getResource(keyLocation);
                         assertNotNull("Missing key file " + keyLocation, location);
 
-                        KeyPair kp;
                         URLResource resourceKey = new URLResource(location);
+                        Iterable<KeyPair> ids;
                         try (InputStream keyData = resourceKey.openInputStream()) {
-                            kp = SecurityUtils.loadKeyPairIdentity(session, resourceKey, keyData, passwordProvider);
+                            ids = SecurityUtils.loadKeyPairIdentities(session, resourceKey, keyData, passwordProvider);
                         }
+                        KeyPair kp = GenericUtils.head(ids);
                         assertNotNull("No identity loaded from " + resourceKey, kp);
                         return Collections.singletonList(kp);
                     }