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 "lazily" - 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);
}