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 2016/12/03 14:57:39 UTC

[2/2] mina-sshd git commit: [SSHD-713] Provide pluggable security providers capability

[SSHD-713] Provide pluggable security providers capability


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

Branch: refs/heads/master
Commit: ab72900ff8a1eb224e75011ae298dc2469341185
Parents: 8cf57bc
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Sat Dec 3 16:57:38 2016 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Sat Dec 3 16:57:38 2016 +0200

----------------------------------------------------------------------
 README.md                                       |  79 +++-
 sshd-core/pom.xml                               |   2 +
 .../sshd/client/ClientFactoryManager.java       |   4 +-
 .../java/org/apache/sshd/client/SshKeyScan.java |   2 +-
 .../org/apache/sshd/common/FactoryManager.java  |  44 +-
 .../sshd/common/PropertyResolverUtils.java      |   8 +-
 .../org/apache/sshd/common/cipher/ECCurves.java |   2 +-
 .../common/config/keys/BuiltinIdentities.java   |   2 +-
 .../sshd/common/config/keys/KeyUtils.java       |   2 +-
 .../keys/impl/ECDSAPublicKeyEntryDecoder.java   |  10 +-
 .../OpenSSHECDSAPrivateKeyEntryDecoder.java     |  10 +-
 .../openssh/OpenSSHKeyPairResourceParser.java   |   2 +-
 .../pem/ECDSAPEMResourceKeyPairParser.java      |   2 +-
 .../common/signature/BuiltinSignatures.java     |   6 +-
 .../apache/sshd/common/util/GenericUtils.java   |  12 +-
 .../sshd/common/util/IgnoringEmptyMap.java      | 128 ++++++
 .../AbstractSecurityProviderRegistrar.java      | 129 ++++++
 .../util/security/SecurityEntityFactory.java    | 194 ++++++++
 .../util/security/SecurityProviderChoice.java   | 130 ++++++
 .../security/SecurityProviderRegistrar.java     | 331 ++++++++++++++
 .../common/util/security/SecurityUtils.java     | 456 +++++++++++--------
 .../BouncyCastleKeyPairResourceParser.java      |  13 +-
 .../BouncyCastleSecurityProviderRegistrar.java  | 116 +++++
 .../eddsa/EdDSASecurityProviderRegistrar.java   | 105 +++++
 .../sshd/common/util/threads/ThreadUtils.java   |  12 +
 .../sshd/server/ServerFactoryManager.java       |   6 +-
 .../server/subsystem/sftp/SftpSubsystem.java    |   1 +
 .../sshd/server/x11/X11ForwardSupport.java      |  10 +-
 .../config/keys/AuthorizedKeysTestSupport.java  |   2 +-
 .../config/keys/BuiltinIdentitiesTest.java      |  85 ++--
 .../common/config/keys/KeyUtilsCloneTest.java   |   2 +-
 .../signature/SignatureFactoriesTest.java       |   2 +-
 .../sshd/common/util/SecurityUtilsTest.java     |  43 +-
 ...SecurityProviderRegistrarCipherNameTest.java |  76 ++++
 .../SecurityProviderRegistrarTestSupport.java   |  59 +++
 .../EdDSASecurityProviderRegistrarTest.java     |  83 ++++
 .../PEMGeneratorHostKeyProviderTest.java        |   6 +-
 .../SimpleGeneratorHostKeyProviderTest.java     |   6 +-
 38 files changed, 1848 insertions(+), 334 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index bf8b2b8..0a1685c 100644
--- a/README.md
+++ b/README.md
@@ -17,12 +17,22 @@ The code only requires the core abstract [slf4j-api](https://mvnrepository.com/a
 * [Bouncy Castle](https://www.bouncycastle.org/)
 
 
-Required mainly for writing keys from/to PEM files or for special keys/ciphers/etc. that are not part of the standard [Java Cryptography Extension](https://en.wikipedia.org/wiki/Java_Cryptography_Extension). See [Java Cryptography Architecture (JCA) Reference Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) for key classes and explanations as to how _Bouncy Castle_ is plugged in (other security providers).
+Required mainly for writing keys to PEM files or for special keys/ciphers/etc. that are not part of the standard [Java Cryptography Extension](https://en.wikipedia.org/wiki/Java_Cryptography_Extension). See [Java Cryptography Architecture (JCA) Reference Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) for key classes and explanations as to how _Bouncy Castle_ is plugged in (other security providers).
 
-**Caveat**: If _Bouncy Castle_ modules are registered, then the code will use its implementation of the ciphers, keys, signatures, etc. rather than the default JCE provided in the JVM. Furthermore, one can use the `BouncyCastleKeyPairResourceParser.INSTANCE` to load standard PEM files instead of the core one - either directly or via `SecurityUtils#setKeyPairResourceParser` for **global** usage.
+**Caveat**: If _Bouncy Castle_ modules are registered, then the code will use its implementation of the ciphers, keys, signatures, etc. rather than the default JCE provided in the JVM.
 
- **Note:** the required Maven module(s) are defined as `optional` so must be added as an
-**explicit** dependency in order to be included in the classpath:
+ **Note:**
+ 
+ - The security provider can also be registered for keys/ciphers/etc. that are already supported by the standard JCE as a **replacement** for them.
+ 
+ 
+ - The _BouncyCastle_ code can also be used to load keys from PEM files instead or in parallel with the built-in code that already parses the standard PEM formats for the default JCE supported key types.
+ 
+ 
+- One can use the `BouncyCastleKeyPairResourceParser` to load standard PEM files instead of the core one - either directly or via `SecurityUtils#setKeyPairResourceParser` for **global** usage - even without registering or enabling the provider.
+ 
+ 
+ - The required _Maven_ module(s) are defined as `optional` so must be added as an **explicit** dependency in order to be included in the classpath:
 
 ```xml
 
@@ -261,7 +271,7 @@ Configuring supported ciphers can be done with the following code:
 
 ```
 
-You can configure other security components using builtin factories the same way.
+You can configure other security components using built-in factories the same way.
 
 ## Starting the Server
 
@@ -269,6 +279,65 @@ Once we have configured the server, one need only call `sshd.start();`. **Note**
 
 # SSH functionality breakdown
 
+## Security providers setup
+
+While the code supports _BouncyCastle_ and _EdDSA_ security providers out-of-the-box, 
+it also provides a way to [add security providers](https://issues.apache.org/jira/browse/SSHD-713) via the `SecurityProviderRegistrar` interface implementation. In order to add support for a new security provider one needs to implement the registrar interface and make the code aware of it.
+
+### Default/built-in security provider registrars
+
+The code contains built-in security provider registrars for _BouncyCastle_ and _EdDSA_ (a.k.a. `ed25519`). It automatically detects the existence of the required artifacts (since they are optional dependencies) and executes the respective security provider registration. This behavior is controlled by the `org.apache.sshd.security.registrars` system property. This property contains a comma-separated list of **fully-qualified** class names implementing the `SecurityProviderRegistrar` interface and assumed to contain a default **public** no-arguments constructor. The code automatically parses the list and attempts to instantiate and invoke the registrar.
+
+**Note:**
+
+
+- The registration code that automatically parses the configured reigstrars list and instantiates them. In this context, one can use the special `none` value to indicate that the code should not attempt to automatically register the default providers.
+ 
+- A registrar instance might be created but eventually discarded and not invoked if it is disabled, unsupported or already registered programmatically via `SecurityUtils#registerSecurityProvider`.
+
+
+- The registration attempt is a **one-shot** deal - i.e., once the registrars list is parsed and successfully resolved, any modifications to the registered security providers must be done **programatically**. One can call `SecurityUtils#isRegistrationCompleted()` to find out if the registration phase has already been executed.
+
+
+- The registrars are consulted in the same order as they were initially registered - either programmatically or via the system property configuration. Therefore, if two or more registrars support the same algorithm, then the earlier registered one will be used.
+
+
+- If no matching registrar was found, then the default security provider is used. If none set, the JCE defaults are invoked. The default security provider can be configured either via the `org.apache.sshd.security.defaultProvider` system property or by programmatically invoking `SecurityUtils#setDefaultProviderChoice`. **Note:** if the system property option is used, then it is assumed to contain a security provider's **name** (rather than its `Provider` class name...).
+
+
+- The if programmatic selection of the default security provider choice is required, then the code flow must ensure that `SecurityUtils#setDefaultProviderChoice` is called before **any** security entity (e.g., ciphers, keys, etc...) are required. Theoretically, one could change the choice after ciphers have been been requested but before keys were generated (e.g....), but it is dangerous and may yield unpredictable behavior.
+
+
+### Implementing a new security provider registrar
+
+See `AbstractSecurityProviderRegistrar` helper class for a default implementation of most of the required functionality, as well as the existing implementations for _BouncyCastle_ and _EdDSA_ for examples of how to implement it. The most important issues to consider when adding such an implementation are:
+
+* Try using reflection API to detect the existence of the registered provider class and/or instantiate it. The main reason for this recommendation is that it isolates the code from a direct dependency on the provider's classes and makes class loading issue less likely.
+
+
+* Decide whether to use the provider's name or instance when creating security related entities such as ciphers, keys, etc... **Note:** the default preference is to use the provider name, thus registering via `Security.addProvider` call. In order to change that, either register the instance yourself or override the `isNamedProviderUsed` method. In this context, **cache** the generated `Provider` instance if the instance rather than the name is used. **Note:** using only the provider instance instead of the name is a rather new feature and has not been fully tested. It is possible though to decide and use it anyway as long as it can be configurably disabled.
+
+
+* The default implementation provides fine-grained control over the declared supported security entities - ciphers, signatures, key generators, etc... By default, it is done via consulting a system property composed of `org.apache.sshd.security.provider`, followed by the security provider name and the relevant security entity - e.g., `org.apache.sshd.security.provider.BC.KeyFactory` is assumed to contain a comma-separated list of supported `KeyFactory` algorithms.
+
+**Note:**
+
+
+* The same naming convention can be used to enable/disable the registrar - even if supported - e.g., `org.apache.sshd.security.provider.BC.enabled=false` disables the _BouncyCastle_ registrar.
+
+
+* One can use `all` or `*` to specify that all entities of the specified type are supported - e.g., `org.apache.sshd.security.provider.BC.MessageDigest=all`. In this context, one can override the `getDefaultSecurityEntitySupportValue` method if no fine-grained configuration is required per-entity type,
+
+    
+* The result of an `isXxxSupported` call is/should be **cached** (see `AbstractSecurityProviderRegistrar`).
+
+    
+* For ease of implementation, all support query calls are routed to the `isSecurityEntitySupported` method so that one can concentrate all the configuration in a single method. This is done for **convenience** reasons - the code will invoke the correct support query as per the type of entity it needs. E.g., if it needs a cipher, it will invoke `isCipherSupported` - which by default will invoke `isSecurityEntitySupported` with the `Cipher` class as its argument.
+
+
+* Specifically for **ciphers** the argument to the support query contains a **transformation** (e.g., `AES/CBC/NoPadding`) so one should take that into account - see `SecurityProviderRegistrar.getEffectiveSecurityEntityName(Class<?>, String)` helper method
+
+
 ## `FileSystemFactory` usage
 
 This interface is used to provide "file"-related services - e.g., SCP and SFTP - although it can be used for remote command execution

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml
index 3ddcfd1..fdbe0d8 100644
--- a/sshd-core/pom.xml
+++ b/sshd-core/pom.xml
@@ -189,6 +189,8 @@
 								<configuration>
 									<reportsDirectory>${project.build.directory}/surefire-reports-jce</reportsDirectory>
 									<systemProperties>
+                                        <org.apache.sshd.security.provider.BC.enabled>false</org.apache.sshd.security.provider.BC.enabled>
+                                            <!-- deprecated -->
 										<org.apache.sshd.registerBouncyCastle>false</org.apache.sshd.registerBouncyCastle>
 									</systemProperties>
 								</configuration>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
index 3ecdb42..7d4a07b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
@@ -52,7 +52,7 @@ public interface ClientFactoryManager
     String HEARTBEAT_INTERVAL = "heartbeat-interval";
 
     /**
-     * Default value for {@link #HEARTBEAT_INTERVAL} if none configured
+     * Default value for {@value #HEARTBEAT_INTERVAL} if none configured
      */
     long DEFAULT_HEARTBEAT_INTERVAL = 0L;
 
@@ -74,7 +74,7 @@ public interface ClientFactoryManager
     String IGNORE_INVALID_IDENTITIES = "ignore-invalid-identities";
 
     /**
-     * Default value of {@link #IGNORE_INVALID_IDENTITIES} if none configured
+     * Default value of {@value #IGNORE_INVALID_IDENTITIES} if none configured
      */
     boolean DEFAULT_IGNORE_INVALID_IDENTITIES = true;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java b/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
index d75cfaf..162732d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
@@ -547,7 +547,7 @@ public class SshKeyScan implements Channel, Callable<Void>, ServerKeyVerifier, S
         } else if (BuiltinIdentities.Constants.DSA.equalsIgnoreCase(keyType)) {
             return Collections.singletonList(KeyUtils.generateKeyPair(KeyPairProvider.SSH_DSS, 512));
         } else if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyType)) {
-            if (!SecurityUtils.hasEcc()) {
+            if (!SecurityUtils.isECCSupported()) {
                 throw new NoSuchAlgorithmException("ECC not supported: " + keyType);
             }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index 800473a..0717323 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -60,7 +60,7 @@ public interface FactoryManager
     String WINDOW_SIZE = "window-size";
 
     /**
-     * Default {@link #WINDOW_SIZE} if none set
+     * Default {@value #WINDOW_SIZE} if none set
      */
     long DEFAULT_WINDOW_SIZE = 0x200000L;   // actually a UINT32
 
@@ -73,7 +73,7 @@ public interface FactoryManager
     String WINDOW_TIMEOUT = "window-timeout";
 
     /**
-     * Default {@link #WINDOW_TIMEOUT} value
+     * Default {@value #WINDOW_TIMEOUT} value
      */
     long DEFAULT_WINDOW_TIMEOUT = 0L;
 
@@ -85,7 +85,7 @@ public interface FactoryManager
     String MAX_PACKET_SIZE = "packet-size";
 
     /**
-     * Default {@link #MAX_PACKET_SIZE} if none set
+     * Default {@value #MAX_PACKET_SIZE} if none set
      */
     long DEFAULT_MAX_PACKET_SIZE = 0x8000L;   // actually a UINT32
 
@@ -97,7 +97,7 @@ public interface FactoryManager
     String LIMIT_PACKET_SIZE = "max-packet-size";
 
     /**
-     * Default {@link #LIMIT_PACKET_SIZE} if none set
+     * Default {@value #LIMIT_PACKET_SIZE} if none set
      */
     long DEFAULT_LIMIT_PACKET_SIZE = Integer.MAX_VALUE / 4L;
 
@@ -122,7 +122,7 @@ public interface FactoryManager
     String AUTH_TIMEOUT = "auth-timeout";
 
     /**
-     * Default value for {@link #AUTH_TIMEOUT} if none set
+     * Default value for {@value #AUTH_TIMEOUT} if none set
      */
     long DEFAULT_AUTH_TIMEOUT = TimeUnit.MINUTES.toMillis(2L);
 
@@ -134,7 +134,7 @@ public interface FactoryManager
     String IDLE_TIMEOUT = "idle-timeout";
 
     /**
-     * Default value for {@link #IDLE_TIMEOUT} if none set
+     * Default value for {@value #IDLE_TIMEOUT} if none set
      */
     long DEFAULT_IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10L);
 
@@ -146,7 +146,7 @@ public interface FactoryManager
     String NIO2_READ_TIMEOUT = "nio2-read-timeout";
 
     /**
-     * Default value for {@link #NIO2_READ_TIMEOUT} if none set
+     * Default value for {@value #NIO2_READ_TIMEOUT} if none set
      */
     long DEFAULT_NIO2_READ_TIMEOUT = DEFAULT_IDLE_TIMEOUT + TimeUnit.SECONDS.toMillis(15L);
 
@@ -158,7 +158,7 @@ public interface FactoryManager
     String NIO2_MIN_WRITE_TIMEOUT = "nio2-min-write-timeout";
 
     /**
-     * Default value for {@link #NIO2_MIN_WRITE_TIMEOUT} if none set
+     * Default value for {@value #NIO2_MIN_WRITE_TIMEOUT} if none set
      */
     long DEFAULT_NIO2_MIN_WRITE_TIMEOUT = TimeUnit.SECONDS.toMillis(30L);
 
@@ -172,7 +172,7 @@ public interface FactoryManager
     String DISCONNECT_TIMEOUT = "disconnect-timeout";
 
     /**
-     * Default value for {@link #DISCONNECT_TIMEOUT} if none set
+     * Default value for {@value #DISCONNECT_TIMEOUT} if none set
      */
     long DEFAULT_DISCONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(10L);
 
@@ -185,7 +185,7 @@ public interface FactoryManager
     String CHANNEL_CLOSE_TIMEOUT = "channel-close-timeout";
 
     /**
-     * Default {@link #CHANNEL_CLOSE_TIMEOUT} value if none set
+     * Default {@value #CHANNEL_CLOSE_TIMEOUT} value if none set
      */
     long DEFAULT_CHANNEL_CLOSE_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
 
@@ -197,7 +197,7 @@ public interface FactoryManager
     String STOP_WAIT_TIME = "stop-wait-time";
 
     /**
-     * Default value for {@link #STOP_WAIT_TIME} if none specified
+     * Default value for {@value #STOP_WAIT_TIME} if none specified
      */
     long DEFAULT_STOP_WAIT_TIME = TimeUnit.MINUTES.toMillis(1L);
 
@@ -262,19 +262,19 @@ public interface FactoryManager
     String MAX_IDENTIFICATION_SIZE = "max-identification-size";
 
     /**
-     * Default value for {@link #MAX_IDENTIFICATION_SIZE} if none set
+     * Default value for {@value #MAX_IDENTIFICATION_SIZE} if none set
      */
     int DEFAULT_MAX_IDENTIFICATION_SIZE = 16 * 1024;
 
     /**
      * Key re-exchange will be automatically performed after the session
      * has sent or received the given amount of bytes. If non-positive,
-     * then disabled. The default value is {@link #DEFAULT_REKEY_BYTES_LIMIT}
+     * then disabled. The default value is {@value #DEFAULT_REKEY_BYTES_LIMIT}
      */
     String REKEY_BYTES_LIMIT = "rekey-bytes-limit";
 
     /**
-     * Default value for {@link #REKEY_BYTES_LIMIT} if no override
+     * Default value for {@value #REKEY_BYTES_LIMIT} if no override
      * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-9">RFC4253 section 9</A>
      */
     long DEFAULT_REKEY_BYTES_LIMIT = 1024L * 1024L * 1024L; // 1GB
@@ -282,12 +282,12 @@ public interface FactoryManager
     /**
      * Key re-exchange will be automatically performed after the specified
      * amount of time has elapsed since the last key exchange - in milliseconds.
-     * If non-positive then disabled. The default value is {@link #DEFAULT_REKEY_TIME_LIMIT}
+     * If non-positive then disabled. The default value is {@value #DEFAULT_REKEY_TIME_LIMIT}
      */
     String REKEY_TIME_LIMIT = "rekey-time-limit";
 
     /**
-     * Default value for {@link #REKEY_TIME_LIMIT} if none specified
+     * Default value for {@value #REKEY_TIME_LIMIT} if none specified
      * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-9">RFC4253 section 9</A>
      */
     long DEFAULT_REKEY_TIME_LIMIT = 60L * 60L * 1000L; // 1 hour
@@ -295,12 +295,12 @@ public interface FactoryManager
     /**
      * Key re-exchange will be automatically performed after the specified
      * number of packets has been exchanged - positive 64-bit value. If
-     * non-positive then disabled. The default is {@link #DEFAULT_REKEY_PACKETS_LIMIT}
+     * non-positive then disabled. The default is {@value #DEFAULT_REKEY_PACKETS_LIMIT}
      */
     String REKEY_PACKETS_LIMIT = "rekey-packets-limit";
 
     /**
-     * Default value for {@link #REKEY_PACKETS_LIMIT} if none specified
+     * Default value for {@value #REKEY_PACKETS_LIMIT} if none specified
      * @see <A HREF="https://tools.ietf.org/html/rfc4344#section-3.1">RFC4344 section 3.1</A>
      */
     long DEFAULT_REKEY_PACKETS_LIMIT = 1L << 31;
@@ -322,12 +322,12 @@ public interface FactoryManager
     String IGNORE_MESSAGE_FREQUENCY = "ignore-message-frequency";
 
     /**
-     * Default value of {@link #IGNORE_MESSAGE_FREQUENCY} if none set.
+     * Default value of {@value #IGNORE_MESSAGE_FREQUENCY} if none set.
      */
     long DEFAULT_IGNORE_MESSAGE_FREQUENCY = 1024L;
 
     /**
-     * The variance to be used around the configured {@link #IGNORE_MESSAGE_FREQUENCY}
+     * The variance to be used around the configured {@value #IGNORE_MESSAGE_FREQUENCY}
      * value in order to avoid insertion at a set frequency. If zero, then <U>exact</U>
      * frequency is used. If negative, then the <U>absolute</U> value is used. If
      * greater or equal to the frequency, then assumed to be zero - i.e., no variance
@@ -336,7 +336,7 @@ public interface FactoryManager
     String IGNORE_MESSAGE_VARIANCE = "ignore-message-variance";
 
     /**
-     * Default value for {@link #IGNORE_MESSAGE_VARIANCE} if none configured
+     * Default value for {@value #IGNORE_MESSAGE_VARIANCE} if none configured
      */
     int DEFAULT_IGNORE_MESSAGE_VARIANCE = 32;
 
@@ -349,7 +349,7 @@ public interface FactoryManager
     String IGNORE_MESSAGE_SIZE = "ignore-message-size";
 
     /**
-     * Value of {@link #IGNORE_MESSAGE_SIZE} if none configured
+     * Value of {@value #IGNORE_MESSAGE_SIZE} if none configured
      */
     int DEFAULT_IGNORE_MESSAGE_SIZE = 16;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
index 06fe537..a4fc994 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
@@ -380,7 +380,9 @@ public final class PropertyResolverUtils {
 
     /**
      * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
-     * for the requested property or reached top.
+     * for the requested property or reached top. If still no value found and the key
+     * starts with &quot;org.apache.sshd&quot; then the system properties are also
+     * consulted
      *
      * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
      * @param name     The requested property name
@@ -396,6 +398,10 @@ public final class PropertyResolverUtils {
             }
         }
 
+        if (key.startsWith("org.apache.sshd")) {
+            return System.getProperty(key);
+        }
+
         return null;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index 8da9f9c..083778b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -178,7 +178,7 @@ public enum ECCurves implements NamedResource, OptionalFeature {
 
     @Override
     public final boolean isSupported() {
-        return SecurityUtils.hasEcc() && digestFactory.isSupported();
+        return SecurityUtils.isECCSupported() && digestFactory.isSupported();
     }
 
     public final ECParameterSpec getParameters() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
index 8dddfe7..ed178e6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
@@ -47,7 +47,7 @@ public enum BuiltinIdentities implements Identity {
     ECDSA(Constants.ECDSA, KeyUtils.EC_ALGORITHM, ECPublicKey.class, ECPrivateKey.class) {
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return SecurityUtils.isECCSupported();
         }
     },
     ED25119(Constants.ED25519, SecurityUtils.EDDSA, SecurityUtils.getEDDSAPublicKeyType(), SecurityUtils.getEDDSAPrivateKeyType()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
index bd56559..6ff6fde 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
@@ -139,7 +139,7 @@ public final class KeyUtils {
         registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
         registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE);
 
-        if (SecurityUtils.hasEcc()) {
+        if (SecurityUtils.isECCSupported()) {
             registerPublicKeyEntryDecoder(ECDSAPublicKeyEntryDecoder.INSTANCE);
         }
         if (SecurityUtils.isEDDSACurveSupported()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
index 56ff39e..e6d4652 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
@@ -65,7 +65,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
             throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
         }
 
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 
@@ -97,7 +97,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
 
     @Override
     public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 
@@ -115,7 +115,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
 
     @Override
     public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 
@@ -148,7 +148,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
 
     @Override
     public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        if (SecurityUtils.hasEcc()) {
+        if (SecurityUtils.isECCSupported()) {
             return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
         } else {
             throw new NoSuchProviderException("ECC not supported");
@@ -169,7 +169,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
 
     @Override
     public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        if (SecurityUtils.hasEcc()) {
+        if (SecurityUtils.isECCSupported()) {
             return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
         } else {
             throw new NoSuchProviderException("ECC not supported");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
index bdb6d28..bceca59 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
@@ -62,7 +62,7 @@ public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryD
             throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
         }
 
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 
@@ -98,7 +98,7 @@ public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryD
 
     @Override
     public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 
@@ -116,7 +116,7 @@ public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryD
 
     @Override
     public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 
@@ -134,7 +134,7 @@ public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryD
 
     @Override
     public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        if (SecurityUtils.hasEcc()) {
+        if (SecurityUtils.isECCSupported()) {
             return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
         } else {
             throw new NoSuchProviderException("ECC not supported");
@@ -155,7 +155,7 @@ public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryD
 
     @Override
     public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        if (SecurityUtils.hasEcc()) {
+        if (SecurityUtils.isECCSupported()) {
             return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
         } else {
             throw new NoSuchProviderException("ECC not supported");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
index 07f970f..bc068ea 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
@@ -81,7 +81,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
         registerPrivateKeyEntryDecoder(OpenSSHRSAPrivateKeyDecoder.INSTANCE);
         registerPrivateKeyEntryDecoder(OpenSSHDSSPrivateKeyEntryDecoder.INSTANCE);
 
-        if (SecurityUtils.hasEcc()) {
+        if (SecurityUtils.isECCSupported()) {
             registerPrivateKeyEntryDecoder(OpenSSHECDSAPrivateKeyEntryDecoder.INSTANCE);
         }
         if (SecurityUtils.isEDDSACurveSupported()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
index 33db700..9b62ccd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
@@ -74,7 +74,7 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
             String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
                     throws IOException, GeneralSecurityException {
         Pair<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
-        if (!SecurityUtils.hasEcc()) {
+        if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
index a938acc..d6b2bac 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -67,7 +67,7 @@ public enum BuiltinSignatures implements SignatureFactory {
 
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return SecurityUtils.isECCSupported();
         }
     },
     nistp384(KeyPairProvider.ECDSA_SHA2_NISTP384) {
@@ -78,7 +78,7 @@ public enum BuiltinSignatures implements SignatureFactory {
 
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return SecurityUtils.isECCSupported();
         }
     },
     nistp521(KeyPairProvider.ECDSA_SHA2_NISTP521) {
@@ -89,7 +89,7 @@ public enum BuiltinSignatures implements SignatureFactory {
 
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return SecurityUtils.isECCSupported();
         }
     },
     ed25519(KeyPairProvider.SSH_ED25519) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index b4dd2e2..92d89b1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -542,6 +542,16 @@ public final class GenericUtils {
         return map;
     }
 
+    @SafeVarargs
+    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, T ... values) {
+        return findFirstMatchingMember(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, Collection<? extends T> values) {
+        List<T> matches = selectMatchingMembers(acceptor, values);
+        return GenericUtils.isEmpty(matches) ? null : matches.get(0);
+    }
+
     /**
      * Returns a list of all the values that were accepted by a predicate
      *
@@ -565,7 +575,7 @@ public final class GenericUtils {
      */
     public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
         return GenericUtils.stream(values)
-                .filter(acceptor::test)
+                .filter(acceptor)
                 .collect(Collectors.toList());
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java b/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
new file mode 100644
index 0000000..8f18bfe
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A dummy map that ignores all {@code put/remove} calls
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class IgnoringEmptyMap<K, V> implements Map<K, V> {
+    @SuppressWarnings("rawtypes")
+    private static final IgnoringEmptyMap INSTANCE = new IgnoringEmptyMap();
+
+    public IgnoringEmptyMap() {
+        super();
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return true;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        Objects.requireNonNull(value, "No value provided");
+        return false;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return false;
+    }
+
+    @Override
+    public V get(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return null;
+    }
+
+    @Override
+    public V put(K key, V value) {
+        Objects.requireNonNull(key, "No key provided");
+        Objects.requireNonNull(value, "No value provided");
+        return null;
+    }
+
+    @Override
+    public V remove(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return null;
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        // ignored
+    }
+
+    @Override
+    public void clear() {
+        // ignored
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof IgnoringEmptyMap<?, ?>;
+    }
+
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "{}";
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return Collections.emptySet();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <K, V> IgnoringEmptyMap<K, V> getInstance() {
+        return INSTANCE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
new file mode 100644
index 0000000..b665166
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSecurityProviderRegistrar
+                extends AbstractLoggingBean
+                implements SecurityProviderRegistrar {
+    protected final Map<String, Object> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    protected final Map<Class<?>, Map<String, Boolean>> supportedEntities = new HashMap<>();
+    protected final AtomicReference<Provider> providerHolder = new AtomicReference<>(null);
+
+    private final String name;
+
+    protected AbstractSecurityProviderRegistrar(String name) {
+        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return props;
+    }
+
+    @Override
+    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+        Map<String, Boolean> supportMap;
+        synchronized (supportedEntities) {
+            supportMap = supportedEntities.computeIfAbsent(
+                    entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
+        }
+
+        Boolean supportFlag;
+        synchronized (supportMap) {
+            supportFlag = supportMap.computeIfAbsent(
+                    name, k -> SecurityProviderRegistrar.super.isSecurityEntitySupported(entityType, name));
+        }
+
+        return supportFlag;
+    }
+
+    /**
+     * Attempts to see if a provider with this name already registered. If not,
+     * then uses reflection API in order to load and instantiate the specified
+     * <tt>providerClassName</tt>
+     *
+     * @param providerClassName The fully-qualified class name to instantiate
+     * if a provider not already registered
+     * @return The resolved {@link Provider} instance - <B>Note:</B> the result
+     * is <U>cached</U> - i.e., successful resolution result will not cause
+     * the code to re-resolve the provider
+     * @throws ReflectiveOperationException If failed to instantiate the provider
+     * @throws UnsupportedOperationException If registrar not supported
+     * @see #isSupported()
+     * @see Security#getProvider(String)
+     * @see #createProviderInstance(String)
+     */
+    protected Provider getOrCreateProvider(String providerClassName) throws ReflectiveOperationException {
+        if (!isSupported()) {
+            throw new UnsupportedOperationException("Provider not supported");
+        }
+
+        Provider provider;
+        boolean created = false;
+        synchronized (providerHolder) {
+            provider = providerHolder.get();
+            if (provider != null) {
+                return provider;
+            }
+
+            provider = Security.getProvider(getName());
+            if (provider == null) {
+                provider = createProviderInstance(providerClassName);
+                created = true;
+            }
+            providerHolder.set(provider);
+        }
+
+        if (created) {
+            log.info("getOrCreateProvider({}) created instance of {}", getName(), providerClassName);
+        } else {
+            log.info("getOrCreateProvider({}) resolved instance of {}", getName(), provider.getClass().getName());
+        }
+
+        return provider;
+    }
+
+    protected Provider createProviderInstance(String providerClassName) throws ReflectiveOperationException {
+        return SecurityProviderChoice.createProviderInstance(getClass(), providerClassName);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + getName() + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
new file mode 100644
index 0000000..94b9454
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.lang.reflect.Method;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <T> Type of security entity being generated by this factory
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityEntityFactory<T> {
+    Class<T> getEntityType();
+
+    T getInstance(String algorithm) throws GeneralSecurityException;
+
+    /**
+     * Uses reflection in order to wrap the {@code getInstance} method(s)
+     * as a security entity factory.
+     *
+     * @param <F> Type of entity being generated by the factor
+     * @param entityType The entity type class
+     * @param registrar The {@code SecurityProviderRegistrar} to use - if
+     * {@code null} then default provider is used (if specified).
+     * @param defaultProvider Default provider choice to use if no registrar
+     * provided. If {@code null}/empty then JCE default is used
+     * @return The {@link SecurityEntityFactory} for the entity
+     * @throws ReflectiveOperationException If failed to create the factory
+     * @see #toDefaultFactory(Class)
+     * @see #toNamedProviderFactory(Class, String)
+     * @see #toProviderInstanceFactory(Class, Provider)
+     * @see SecurityProviderChoice#isNamedProviderUsed()
+     * @see SecurityProviderChoice#getSecurityProvider()
+     */
+    static <F> SecurityEntityFactory<F> toFactory(
+            Class<F> entityType, SecurityProviderChoice registrar, SecurityProviderChoice defaultProvider)
+            throws ReflectiveOperationException {
+        if (registrar == null) {
+            if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
+                return toDefaultFactory(entityType);
+            } else if (defaultProvider.isNamedProviderUsed()) {
+                return toNamedProviderFactory(entityType, defaultProvider.getName());
+            } else {
+                return toProviderInstanceFactory(entityType, defaultProvider.getSecurityProvider());
+            }
+        } else if (registrar.isNamedProviderUsed()) {
+            return toNamedProviderFactory(entityType, registrar.getName());
+        } else {
+            return toProviderInstanceFactory(entityType, registrar.getSecurityProvider());
+        }
+    }
+
+    static <F> SecurityEntityFactory<F> toDefaultFactory(Class<F> entityType)
+            throws ReflectiveOperationException {
+        Method m = entityType.getDeclaredMethod("getInstance", String.class);
+        return new SecurityEntityFactory<F>() {
+            private final String s = SecurityEntityFactory.class.getSimpleName()
+                    + "[" + entityType.getSimpleName() + "]"
+                    + "[default]";
+
+            @Override
+            public Class<F> getEntityType() {
+                return entityType;
+            }
+
+            @Override
+            public F getInstance(String algorithm) throws GeneralSecurityException {
+                try {
+                    Object value = m.invoke(null, algorithm);
+                    return entityType.cast(value);
+                } catch (ReflectiveOperationException t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    if (e instanceof GeneralSecurityException) {
+                        throw (GeneralSecurityException) e;
+                    } else if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else if (e instanceof Error) {
+                        throw (Error) e;
+                    } else {
+                        throw new GeneralSecurityException(e);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static <F> SecurityEntityFactory<F> toNamedProviderFactory(Class<F> entityType, String name)
+            throws ReflectiveOperationException {
+        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+        Method m = entityType.getDeclaredMethod("getInstance", String.class, String.class);
+        return new SecurityEntityFactory<F>() {
+            private final String s = SecurityEntityFactory.class.getSimpleName()
+                    + "[" + entityType.getSimpleName() + "]"
+                    + "[" + name + "]";
+
+            @Override
+            public Class<F> getEntityType() {
+                return entityType;
+            }
+
+            @Override
+            public F getInstance(String algorithm) throws GeneralSecurityException {
+                try {
+                    Object value = m.invoke(null, algorithm, name);
+                    return entityType.cast(value);
+                } catch (ReflectiveOperationException t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    if (e instanceof GeneralSecurityException) {
+                        throw (GeneralSecurityException) e;
+                    } else if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else if (e instanceof Error) {
+                        throw (Error) e;
+                    } else {
+                        throw new GeneralSecurityException(e);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static <F> SecurityEntityFactory<F> toProviderInstanceFactory(Class<F> entityType, Provider provider)
+            throws ReflectiveOperationException {
+        Objects.requireNonNull(provider, "No provider instance");
+        Method m = entityType.getDeclaredMethod("getInstance", String.class, Provider.class);
+        return new SecurityEntityFactory<F>() {
+            private final String s = SecurityEntityFactory.class.getSimpleName()
+                    + "[" + entityType.getSimpleName() + "]"
+                    + "[" + Provider.class.getSimpleName() + "]"
+                    + "[" + provider.getName() + "]";
+
+            @Override
+            public Class<F> getEntityType() {
+                return entityType;
+            }
+
+            @Override
+            public F getInstance(String algorithm) throws GeneralSecurityException {
+                try {
+                    Object value = m.invoke(null, algorithm, provider);
+                    return entityType.cast(value);
+                } catch (ReflectiveOperationException t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    if (e instanceof GeneralSecurityException) {
+                        throw (GeneralSecurityException) e;
+                    } else if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else if (e instanceof Error) {
+                        throw (Error) e;
+                    } else {
+                        throw new GeneralSecurityException(e);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
new file mode 100644
index 0000000..c12e747
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.security.Provider;
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityProviderChoice extends NamedResource {
+    SecurityProviderChoice EMPTY = new SecurityProviderChoice() {
+        @Override
+        public String getName() {
+            return null;
+        }
+
+        @Override
+        public boolean isNamedProviderUsed() {
+            return false;
+        }
+
+        @Override
+        public Provider getSecurityProvider() {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * @return {@code true} if to use the provider's name rather than its
+     * {@link Provider} instance - default={@code true}.
+     */
+    default boolean isNamedProviderUsed() {
+        return true;
+    }
+
+    /**
+     * @return The security {@link Provider} to use in case {@link #isNamedProviderUsed()}
+     * is {@code false}. Can be {@code null} if {@link #isNamedProviderUsed()} is {@code true},
+     * but not recommended.
+     */
+    Provider getSecurityProvider();
+
+    static SecurityProviderChoice toSecurityProviderChoice(String name) {
+        ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
+        return new SecurityProviderChoice() {
+            private final String s = SecurityProviderChoice.class.getSimpleName() + "[" + name + "]";
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isNamedProviderUsed() {
+                return true;
+            }
+
+            @Override
+            public Provider getSecurityProvider() {
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static SecurityProviderChoice toSecurityProviderChoice(Provider provider) {
+        Objects.requireNonNull(provider, "No provider instance");
+        return new SecurityProviderChoice() {
+            private final String s = SecurityProviderChoice.class.getSimpleName()
+                    + "[" + Provider.class.getSimpleName() + "]"
+                    + "[" + provider.getName() + "]";
+
+            @Override
+            public String getName() {
+                return provider.getName();
+            }
+
+            @Override
+            public boolean isNamedProviderUsed() {
+                return false;
+            }
+
+            @Override
+            public Provider getSecurityProvider() {
+                return provider;
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static Provider createProviderInstance(Class<?> anchor, String providerClassName)
+            throws ReflectiveOperationException {
+        return ThreadUtils.createDefaultInstance(anchor, Provider.class, providerClassName);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
new file mode 100644
index 0000000..110d160
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SyspropsMapWrapper;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.IgnoringEmptyMap;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityProviderRegistrar extends SecurityProviderChoice, OptionalFeature, PropertyResolver {
+    /**
+     * Base name for configuration properties related to security providers
+     */
+    String CONFIG_PROP_BASE = "org.apache.sshd.security.provider";
+
+    /**
+     * Property used to configure whether the provider is enabled regardless of
+     * whether it is supported.
+     *
+     * @see #isEnabled()
+     */
+    String ENABLED_PROPERTY = "enabled";
+
+    /**
+     * Property used to configure whether to use the provider's name rather than its
+     * {@link Provider} instance
+     *
+     * @see #isNamedProviderUsed()
+     */
+    String NAMED_PROVIDER_PROPERTY = "useNamed";
+
+    String ALL_OPTIONS_VALUE = "all";
+    String ALL_OPTIONS_WILDCARD = "*";
+
+    /**
+     * All the entities that are used in calls to {@link #isSecurityEntitySupported(Class, String)}
+     */
+    List<Class<?>> SECURITY_ENTITIES =
+            Collections.unmodifiableList(
+                    Arrays.asList(
+                            Cipher.class, KeyFactory.class, MessageDigest.class,
+                            KeyPairGenerator.class, KeyAgreement.class, Mac.class,
+                            Signature.class, CertificateFactory.class));
+
+    default String getBasePropertyName() {
+        return CONFIG_PROP_BASE + "." + getName();
+    }
+
+    default String getConfigurationPropertyName(String name) {
+        return getBasePropertyName() + "." + name;
+    }
+
+    /**
+     * @return {@code true} if the provider is enabled regardless of
+     * whether it is supported - default={@code true}. <B>Note:</B>
+     * checks if the provider has been <U>programmatically</U> disabled
+     * via {@link SecurityUtils#setAPrioriDisabledProvider(String, boolean)}
+     * @see #ENABLED_PROPERTY
+     */
+    default boolean isEnabled() {
+        if (SecurityUtils.isAPrioriDisabledProvider(getName())) {
+            return false;
+        }
+
+        return PropertyResolverUtils.getBooleanProperty(this, getConfigurationPropertyName(ENABLED_PROPERTY), true);
+    }
+
+    @Override
+    default PropertyResolver getParentPropertyResolver() {
+        return SyspropsMapWrapper.SYSPROPS_RESOLVER;
+    }
+
+    @Override
+    default Map<String, Object> getProperties() {
+        return IgnoringEmptyMap.getInstance();
+    }
+
+    /**
+     * @param transformation The requested {@link Cipher} transformation
+     * @return {@code true} if this security provider supports the transformation
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isCipherSupported(String transformation) {
+        return isSecurityEntitySupported(Cipher.class, transformation);
+    }
+
+    /**
+     * @param algorithm The {@link KeyFactory} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isKeyFactorySupported(String algorithm) {
+        return isSecurityEntitySupported(KeyFactory.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link MessageDigest} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isMessageDigestSupported(String algorithm) {
+        return isSecurityEntitySupported(MessageDigest.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link KeyPairGenerator} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isKeyPairGeneratorSupported(String algorithm) {
+        return isSecurityEntitySupported(KeyPairGenerator.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link KeyAgreement} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isKeyAgreementSupported(String algorithm) {
+        return isSecurityEntitySupported(KeyAgreement.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link Mac} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isMacSupported(String algorithm) {
+        return isSecurityEntitySupported(Mac.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link Signature} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isSignatureSupported(String algorithm) {
+        return isSecurityEntitySupported(Signature.class, algorithm);
+    }
+
+    /**
+     * @param type The {@link CertificateFactory} type
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isCertificateFactorySupported(String type) {
+        return isSecurityEntitySupported(CertificateFactory.class, type);
+    }
+
+    /**
+     * @param entityType The requested entity type - its simple name serves to
+     * build the configuration property name.
+     * @return Configuration value to use if no specific configuration provided
+     * - default=empty
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
+        return "";
+    }
+
+    default boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+        String defaultValue = getDefaultSecurityEntitySupportValue(entityType);
+        return isSecurityEntitySupported(this, entityType, name, defaultValue);
+    }
+
+    /**
+     * @return {@code true} if to use the provider's name rather than its
+     * {@link Provider} instance - default={@code true}
+     * @see #NAMED_PROVIDER_PROPERTY
+     * @see #getSecurityProvider()
+     * @see #registerSecurityProvider(SecurityProviderRegistrar)
+     */
+    @Override
+    default boolean isNamedProviderUsed() {
+        return PropertyResolverUtils.getBooleanProperty(this,
+                getConfigurationPropertyName(NAMED_PROVIDER_PROPERTY),
+                SecurityProviderChoice.super.isNamedProviderUsed());
+    }
+
+    /**
+     * @param v Value to be examined
+     * @return {@code true} if the value equals (case insensitive) to
+     * either {@link #ALL_OPTIONS_VALUE} or {@link #ALL_OPTIONS_WILDCARD}
+     */
+    static boolean isAllOptionsValue(String v) {
+        return ALL_OPTIONS_VALUE.equalsIgnoreCase(v)
+            || ALL_OPTIONS_WILDCARD.equalsIgnoreCase(v);
+    }
+
+    /**
+     * Checks whether the requested entity type algorithm/name is listed
+     * as supported by the registrar's configuration
+     *
+     * @param registrar The {@link SecurityProviderRegistrar}
+     * @param entityType The requested entity type - its simple name serves to
+     * build the configuration property name.
+     * @param name The requested algorithm/name - <B>Note:</B> if the requested
+     * entity is a {@link Cipher} then the argument is assumed to be a possible
+     * &quot;/&quot; separated transformation and parsed as such in order to
+     * retrieve the pure cipher name
+     * @param defaultValue Configuration value to use if no specific configuration provided
+     * @return {@code true} registrar is supported and the value is listed
+     * (case <U>insensitive</U>) or * the property is one of the &quot;all&quot; markers
+     * @see SecurityProviderRegistrar#isSupported()
+     * @see #isAllOptionsValue(String)
+     */
+    static boolean isSecurityEntitySupported(SecurityProviderRegistrar registrar, Class<?> entityType, String name, String defaultValue) {
+        return Objects.requireNonNull(registrar, "No registrar instance").isSupported()
+            && isSecurityEntitySupported(registrar, registrar.getConfigurationPropertyName(entityType.getSimpleName()), entityType, name, defaultValue);
+    }
+
+    static boolean isSecurityEntitySupported(PropertyResolver resolver, String propName, Class<?> entityType, String name, String defaultValue) {
+        if (GenericUtils.isEmpty(name)) {
+            return false;
+        }
+
+        String propValue = PropertyResolverUtils.getString(resolver, propName);
+        if (GenericUtils.isEmpty(propValue)) {
+            propValue = defaultValue;
+        }
+
+        String[] values = GenericUtils.split(propValue, ',');
+        if (GenericUtils.isEmpty(values)) {
+            return false;
+        }
+
+        if ((values.length == 1) && isAllOptionsValue(values[0])) {
+            return true;
+        }
+
+        String effectiveName = getEffectiveSecurityEntityName(entityType, name);
+        int index = Arrays.binarySearch(values, effectiveName, String.CASE_INSENSITIVE_ORDER);
+        return index >= 0;
+    }
+
+    /**
+     * Determines the &quot;pure&quot; security entity name - e.g., for {@link Cipher}s
+     * it strips the trailing transformation specification in order to extract the
+     * base cipher name - e.g., &quot;AES/CBC/NoPadding&quot; =&gt; &quot;AES&quot;
+     *
+     * @param entityType The security entity type - ignored if {@code null}
+     * @param name The effective name - ignored if {@code null}/empty
+     * @return The resolved name
+     */
+    static String getEffectiveSecurityEntityName(Class<?> entityType, String name) {
+        if ((entityType == null) || GenericUtils.isEmpty(name) || (!Cipher.class.isAssignableFrom(entityType))) {
+            return name;
+        }
+
+        int pos = name.indexOf('/');
+        return (pos > 0) ? name.substring(0, pos) : name;
+    }
+
+    /**
+     * Attempts to register the security provider represented by the registrar
+     * if not already registered. <B>Note:</B> if {@link SecurityProviderRegistrar#isNamedProviderUsed()}
+     * is {@code true} then the generated provider will be added to the system's
+     * list of known providers.
+     *
+     * @param registrar The {@link SecurityProviderRegistrar}
+     * @return {@code true} if no provider was previously registered
+     * @see Security#getProvider(String)
+     * @see SecurityProviderRegistrar#getSecurityProvider()
+     * @see Security#addProvider(Provider)
+     */
+    static boolean registerSecurityProvider(SecurityProviderRegistrar registrar) {
+        String name = ValidateUtils.checkNotNullAndNotEmpty(
+                (registrar == null) ? null : registrar.getName(), "No name for registrar=%s", registrar);
+        Provider p = Security.getProvider(name);
+        if (p != null) {
+            return false;
+        }
+
+        p = ValidateUtils.checkNotNull(
+                registrar.getSecurityProvider(), "No provider created for registrar of %s", name);
+        if (registrar.isNamedProviderUsed()) {
+            Security.addProvider(p);
+        }
+
+        return true;
+    }
+
+    static SecurityProviderRegistrar findSecurityProviderRegistrarBySecurityEntity(
+            Predicate<? super SecurityProviderRegistrar> entitySelector,
+            Collection<? extends SecurityProviderRegistrar> registrars) {
+        return GenericUtils.findFirstMatchingMember(
+            r -> r.isEnabled() && r.isSupported() && entitySelector.test(r), registrars);
+    }
+}