This is an automated email from the ASF dual-hosted git repository. twolf pushed a commit to branch dev_3.0 in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 44654f2735a8342b8875e382c0e9edb5b094d40a Author: Thomas Wolf <tw...@apache.org> AuthorDate: Sat Sep 20 21:53:13 2025 +0200 GH-502: Do not load built-in registrars reflectively Instantiate them directly instead; after all the classes are right here in the same bundle. --- CHANGES.md | 3 ++ .../sshd/common/util/security/SecurityUtils.java | 50 ++++++++++++++++------ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9af65f2b9..8a84b3012 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,9 @@ Complete refactoring of the SSH transport protocol. New feature: support for cli ## Bug Fixes +* [GH-502](https://github.com/apache/mina-sshd/issues/502) Don't load security provider classes reflectively + for Bouncy Castle and `net.isp.crypto:eddsa:0.3.0`. + ## Major Code Re-factoring * The classes dealing with serializing or de-serializing public and private keys have been de-generified, 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 b3f621ae9..512c09066 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 @@ -49,6 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; +import java.util.function.Supplier; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; @@ -74,6 +75,8 @@ import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleEncryptedPr import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleGeneratorHostKeyProvider; import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleKeyPairResourceParser; import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFactory; +import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar; +import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar; import org.apache.sshd.common.util.security.eddsa.generic.EdDSAUtils; import org.apache.sshd.common.util.security.eddsa.generic.OpenSSHEd25519PrivateKeyEntryDecoder; import org.apache.sshd.common.util.security.eddsa.jce.JcePublicKeyFactory; @@ -155,6 +158,8 @@ public final class SecurityUtils { private static final AtomicInteger MIN_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0); private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0); + private static final Map<String, Supplier<SecurityProviderRegistrar>> REGISTRAR_FACTORIES = buildRegistrarsMap(); + /* * NOTE: we use a LinkedHashMap in order to preserve registration order in case several providers support the same * security entity @@ -176,6 +181,19 @@ public final class SecurityUtils { throw new UnsupportedOperationException("No instance"); } + private static Map<String, Supplier<SecurityProviderRegistrar>> buildRegistrarsMap() { + Map<String, Supplier<SecurityProviderRegistrar>> result = new HashMap<>(); + result.put("org.apache.sshd.common.util.security.SunJCESecurityProviderRegistrar", + SunJCESecurityProviderRegistrar::new); + result.put("org.apache.sshd.common.util.security.SunECSecurityProviderRegistrar", // + SunECSecurityProviderRegistrar::new); + result.put("org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar", + EdDSASecurityProviderRegistrar::new); + result.put("org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar", + BouncyCastleSecurityProviderRegistrar::new); + return Collections.unmodifiableMap(result); + } + /** * Unconditionally set FIPS mode, overriding the {@link #FIPS_ENABLED} system property. * @@ -423,22 +441,26 @@ public final class SecurityUtils { boolean debugEnabled = logger.isDebugEnabled(); for (String registrarClass : classes) { SecurityProviderRegistrar r; - try { - r = ThreadUtils.createDefaultInstance(SecurityUtils.class, SecurityProviderRegistrar.class, - registrarClass); - } catch (ReflectiveOperationException t) { - Throwable e = ExceptionUtils.peelException(t); - logger.error("Failed ({}) to create default {} registrar instance: {}", - e.getClass().getSimpleName(), registrarClass, e.getMessage()); - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else if (e instanceof Error) { - throw (Error) e; - } else { - throw new IllegalStateException(e); + Supplier<SecurityProviderRegistrar> factory = REGISTRAR_FACTORIES.get(registrarClass); + if (factory != null) { + r = factory.get(); + } else { + try { + r = ThreadUtils.createDefaultInstance(SecurityUtils.class, SecurityProviderRegistrar.class, + registrarClass); + } catch (ReflectiveOperationException t) { + Throwable e = ExceptionUtils.peelException(t); + logger.error("Failed ({}) to create default {} registrar instance: {}", + e.getClass().getSimpleName(), registrarClass, e.getMessage()); + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else if (e instanceof Error) { + throw (Error) e; + } else { + throw new IllegalStateException(e); + } } } - String name = r.getName(); SecurityProviderRegistrar registeredInstance = registerSecurityProvider(r); if (registeredInstance == null) {