Repository: mina-sshd Updated Branches: refs/heads/master 8cf57bc08 -> ab72900ff
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java index 9ea871d..62b9e3b 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java @@ -32,20 +32,25 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; -import java.security.Provider; 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; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; @@ -63,7 +68,6 @@ import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.random.JceRandomFactory; import org.apache.sshd.common.random.RandomFactory; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ReflectionUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleGeneratorHostKeyProvider; @@ -72,7 +76,6 @@ import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFacto import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProvider; import org.apache.sshd.common.util.threads.ThreadUtils; import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,6 +95,9 @@ public final class SecurityUtils { */ public static final String EDDSA = "EdDSA"; + // A copy-paste from the original, but we don't want to drag the classes into the classpath + public static final String CURVE_ED25519_SHA512 = "ed25519-sha-512"; + /** * System property used to configure the value for the maximum supported Diffie-Hellman * Group Exchange key size. If not set, then an internal auto-discovery mechanism is employed. @@ -114,9 +120,23 @@ public final class SecurityUtils { public static final int MAX_DHGEX_KEY_SIZE = 8192; /** + * Comma separated list of fully qualified {@link SecurityProviderRegistrar}s + * to automatically register + */ + public static final String SECURITY_PROVIDER_REGISTRARS = "org.apache.sshd.security.registrars"; + public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS = + Collections.unmodifiableList( + Arrays.asList( + "org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar", + "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar")); + + + /** * System property used to control whether to automatically register the * {@code Bouncyastle} JCE provider + * @deprecated Please use "org.apache.sshd.security.provider.BC.enabled" */ + @Deprecated public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle"; /** @@ -131,29 +151,82 @@ public final class SecurityUtils { * (in addition or even in spite of {@link #isEDDSACurveSupported()}). If not * set or set to {@code true}, then the existence of the optional support classes * determines the support. + * @deprecated Please use "org.apache.sshd.security.provider.EdDSA.enabled&qupt; */ + @Deprecated public static final String EDDSA_SUPPORTED_PROP = "org.apache.sshd.eddsaSupport"; + public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider"; + private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0); - private static final Map<String, Provider> REGISTERED_PROVIDERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + /* + * NOTE: we use a LinkedHashMap in order to preserve registration order + * in case several providers support the same security entity + */ + private static final Map<String, SecurityProviderRegistrar> REGISTERED_PROVIDERS = new LinkedHashMap<>(); private static final AtomicReference<KeyPairResourceParser> KEYPAIRS_PARSER_HODLER = new AtomicReference<>(); + // If an entry already exists for the named provider, then it overrides its SecurityProviderRegistrar#isEnabled() + private static final Set<String> APRIORI_DISABLED_PROVIDERS = new TreeSet<>(); + private static final AtomicBoolean REGISTRATION_STATE_HOLDER = new AtomicBoolean(false); + private static final Map<Class<?>, Map<String, SecurityEntityFactory<?>>> SECURITY_ENTITY_FACTORIES = new HashMap<>(); + + private static final AtomicReference<SecurityProviderChoice> DEFAULT_PROVIDER_HOLDER = new AtomicReference<>(); - private static String defaultProvider; - private static Boolean registerBouncyCastle; - private static boolean registrationDone; private static Boolean hasEcc; - private static Boolean eddsaSupported; private SecurityUtils() { throw new UnsupportedOperationException("No instance"); } /** + * @param name The provider's name - never {@code null}/empty + * @return {@code true} if the provider is marked as disabled a-priori + * @see #setAPrioriDisabledProvider(String, boolean) + */ + public static boolean isAPrioriDisabledProvider(String name) { + ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified"); + synchronized (APRIORI_DISABLED_PROVIDERS) { + return APRIORI_DISABLED_PROVIDERS.contains(name); + } + } + + /** + * Marks a provider's registrar as "a-priori" <U>programatically</U> + * so that when its {@link SecurityProviderRegistrar#isEnabled()} is eventually + * consulted it will return {@code false} regardless of the configured value for + * the specific provider registrar instance. <B>Note:</B> has no effect if the + * provider has already been registered. + * + * @param name The provider's name - never {@code null}/empty + * @param disabled {@code true} whether to disable it a-priori + * @see #isAPrioriDisabledProvider(String) + */ + public static void setAPrioriDisabledProvider(String name, boolean disabled) { + ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified"); + synchronized (APRIORI_DISABLED_PROVIDERS) { + if (disabled) { + APRIORI_DISABLED_PROVIDERS.add(name); + } else { + APRIORI_DISABLED_PROVIDERS.remove(name); + } + } + } + + /** + * @return A <U>copy</U> if the current a-priori disabled providers names + */ + public static Set<String> getAPrioriDisabledProviders() { + synchronized (APRIORI_DISABLED_PROVIDERS) { + return new TreeSet<>(APRIORI_DISABLED_PROVIDERS); + } + } + + /** * @return {@code true} if Elliptic Curve Cryptography is supported * @see #ECC_SUPPORTED_PROP */ - public static boolean hasEcc() { + public static boolean isECCSupported() { if (hasEcc == null) { String propValue = System.getProperty(ECC_SUPPORTED_PROP); if (GenericUtils.isEmpty(propValue)) { @@ -253,121 +326,144 @@ public final class SecurityUtils { } } - public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) { - SecurityUtils.registerBouncyCastle = registerBouncyCastle; - registrationDone = false; - } + public static SecurityProviderChoice getDefaultProviderChoice() { + SecurityProviderChoice choice; + synchronized (DEFAULT_PROVIDER_HOLDER) { + choice = DEFAULT_PROVIDER_HOLDER.get(); + if (choice != null) { + return choice; + } - public static synchronized String getDefaultProvider() { - return defaultProvider; + String name = System.getProperty(PROP_DEFAULT_SECURITY_PROVIDER); + choice = (GenericUtils.isEmpty(name) || "none".equalsIgnoreCase(name)) + ? SecurityProviderChoice.EMPTY + : SecurityProviderChoice.toSecurityProviderChoice(name); + DEFAULT_PROVIDER_HOLDER.set(choice); + } + + return choice; } - public static synchronized void setDefaultProvider(String provider) { - defaultProvider = provider; - registrationDone = false; + public static void setDefaultProviderChoice(SecurityProviderChoice choice) { + DEFAULT_PROVIDER_HOLDER.set(choice); } /** * @return A <U>copy</U> of the currently registered security providers */ - public static synchronized Set<String> getRegisteredProviders() { - register(); - + public static Set<String> getRegisteredProviders() { // returns a COPY of the providers in order to avoid modifications synchronized (REGISTERED_PROVIDERS) { return new TreeSet<>(REGISTERED_PROVIDERS.keySet()); } } - public static synchronized boolean isBouncyCastleRegistered() { + public static boolean isBouncyCastleRegistered() { register(); - return isBouncyCastleListed(); - } - - private static boolean isBouncyCastleListed() { return isProviderRegistered(BOUNCY_CASTLE); } - private static boolean isEDDSAListed() { - return isProviderRegistered(EDDSA); + public static boolean isProviderRegistered(String provider) { + return getRegisteredProvider(provider) != null; } - private static boolean isProviderRegistered(String provider) { - Objects.requireNonNull(provider, "No provider name specified"); + public static SecurityProviderRegistrar getRegisteredProvider(String provider) { + ValidateUtils.checkNotNullAndNotEmpty(provider, "No provider name specified"); synchronized (REGISTERED_PROVIDERS) { - return REGISTERED_PROVIDERS.containsKey(provider); + return REGISTERED_PROVIDERS.get(provider); } } - @SuppressWarnings("synthetic-access") + public static boolean isRegistrationCompleted() { + return REGISTRATION_STATE_HOLDER.get(); + } + private static void register() { - if (!registrationDone) { - if (registerBouncyCastle == null) { - String propValue = System.getProperty(REGISTER_BOUNCY_CASTLE_PROP); - if (!GenericUtils.isEmpty(propValue)) { - Logger logger = LoggerFactory.getLogger(SecurityUtils.class); - logger.info("Override BouncyCastle registration value: " + propValue); - registerBouncyCastle = Boolean.valueOf(propValue); - } + synchronized (REGISTRATION_STATE_HOLDER) { + if (REGISTRATION_STATE_HOLDER.get()) { + return; } - if ((defaultProvider == null) && (!isBouncyCastleListed()) && ((registerBouncyCastle == null) || registerBouncyCastle)) { - // Use an inner class to avoid a strong dependency from SshServer on BouncyCastle - try { - new BouncyCastleRegistration().call(); - defaultProvider = BOUNCY_CASTLE; - } catch (Throwable t) { - Logger logger = LoggerFactory.getLogger(SecurityUtils.class); - if (registerBouncyCastle == null) { - logger.info("BouncyCastle not registered, using the default JCE provider"); - } else { - logger.error("Failed {} to register BouncyCastle as a JCE provider: {}", t.getClass().getSimpleName(), t.getMessage()); - throw new RuntimeException("Failed to register BouncyCastle as a JCE provider", t); + String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS, + GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ',')); + boolean bouncyCastleRegistered = false; + if ((GenericUtils.length(regsList) > 0) && (!"none".equalsIgnoreCase(regsList))) { + String[] classes = GenericUtils.split(regsList, ','); + Logger logger = LoggerFactory.getLogger(SecurityUtils.class); + ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(SecurityUtils.class); + for (String registrarClass : classes) { + SecurityProviderRegistrar r; + try { + r = ThreadUtils.createDefaultInstance(cl, SecurityProviderRegistrar.class, registrarClass); + } catch (ReflectiveOperationException t) { + Throwable e = GenericUtils.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 RuntimeException(e); + } + } + + String name = r.getName(); + SecurityProviderRegistrar registeredInstance = registerSecurityProvider(r); + if (registeredInstance == null) { + if (logger.isDebugEnabled()) { + logger.debug("register({}) not registered - enabled={}, supported={}", + name, r.isEnabled(), r.isSupported()); + } + continue; // provider not registered - e.g., disabled, not supported + } + + if (BOUNCY_CASTLE.equalsIgnoreCase(name)) { + bouncyCastleRegistered = true; } } } - if ((!isEDDSAListed()) && isEDDSACurveSupported()) { - try { - new EdDSARegistration().call(); - } catch (Throwable t) { - Logger logger = LoggerFactory.getLogger(SecurityUtils.class); - logger.error("Failed {} to register " + EDDSA + " as a JCE provider: {}", t.getClass().getSimpleName(), t.getMessage()); - throw new RuntimeException("Failed to register " + EDDSA + " as a JCE provider", t); - } + SecurityProviderChoice choice = getDefaultProviderChoice(); + if (((choice == null) || (choice == SecurityProviderChoice.EMPTY)) && bouncyCastleRegistered) { + setDefaultProviderChoice(SecurityProviderChoice.toSecurityProviderChoice(BOUNCY_CASTLE)); } - registrationDone = true; + REGISTRATION_STATE_HOLDER.set(true); } } - ///////////////// Bouncycastle specific implementations ////////////////// - - private static class BouncyCastleRegistration implements Callable<Void> { - @SuppressWarnings("synthetic-access") - @Override - public Void call() throws Exception { - // no need for a logger specific to this class since this is a one-time call - Logger logger = LoggerFactory.getLogger(SecurityUtils.class); - Provider p = java.security.Security.getProvider(BOUNCY_CASTLE); - if (p == null) { - logger.info("Trying to register BouncyCastle as a JCE provider"); - p = new BouncyCastleProvider(); - java.security.Security.addProvider(p); - MessageDigest.getInstance("MD5", BOUNCY_CASTLE); - KeyAgreement.getInstance("DH", BOUNCY_CASTLE); - logger.info("Registration succeeded"); - } else { - logger.info("BouncyCastle already registered as a JCE provider"); - } + /** + * @param registrar The registrar instance to register + * @return The registered instance - may be different than required + * if already registered. Returns {@code null} if not already registered + * and not enabled or not supported registrar. + */ + public static SecurityProviderRegistrar registerSecurityProvider(SecurityProviderRegistrar registrar) { + Objects.requireNonNull(registrar, "No registrar instance to register"); + String name = registrar.getName(); + SecurityProviderRegistrar registeredInstance = getRegisteredProvider(name); + if ((registeredInstance == null) && registrar.isEnabled() && registrar.isSupported()) { + try { + SecurityProviderRegistrar.registerSecurityProvider(registrar); + synchronized (REGISTERED_PROVIDERS) { + REGISTERED_PROVIDERS.put(name, registrar); + } - synchronized (REGISTERED_PROVIDERS) { - REGISTERED_PROVIDERS.put(BOUNCY_CASTLE, p); + return registrar; + } catch (Throwable t) { + Logger logger = LoggerFactory.getLogger(SecurityUtils.class); + logger.error("Failed {} to register {} as a JCE provider: {}", + t.getClass().getSimpleName(), name, t.getMessage()); + throw new RuntimeException("Failed to register " + name + " as a JCE provider", t); } - return null; } + + return registeredInstance; } + ///////////////// Bouncycastle specific implementations ////////////////// + /* -------------------------------------------------------------------- */ /** @@ -429,51 +525,11 @@ public final class SecurityUtils { /** * @return {@code true} if EDDSA curves (e.g., {@code ed25519}) are supported */ - public static synchronized boolean isEDDSACurveSupported() { - if (eddsaSupported == null) { - String propValue = System.getProperty(EDDSA_SUPPORTED_PROP); - if (GenericUtils.isEmpty(propValue) || "true".equals(propValue)) { - ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(SecurityUtils.class); - eddsaSupported = ReflectionUtils.isClassAvailable(cl, "net.i2p.crypto.eddsa.EdDSAKey"); - } else { - eddsaSupported = Boolean.FALSE; - Logger logger = LoggerFactory.getLogger(SecurityUtils.class); - logger.info("Override EDDSA support value: " + propValue); - } - } - - return eddsaSupported; - } - - /* -------------------------------------------------------------------- */ - - private static class EdDSARegistration implements Callable<Void> { - EdDSARegistration() { - super(); - } - - @SuppressWarnings("synthetic-access") - @Override - public Void call() throws Exception { - // no need for a logger specific to this class since this is a one-time call - Logger logger = LoggerFactory.getLogger(SecurityUtils.class); - Provider p = java.security.Security.getProvider(EDDSA); - if (p == null) { - logger.info("Trying to register " + EDDSA + " as a JCE provider"); - p = new EdDSASecurityProvider(); - java.security.Security.addProvider(p); - KeyFactory.getInstance(EDDSA, EDDSA); - logger.info("Registration succeeded"); - } else { - logger.info(EDDSA + " already registered as a JCE provider"); - } - - synchronized (REGISTERED_PROVIDERS) { - REGISTERED_PROVIDERS.put(EDDSA, p); - } + public static boolean isEDDSACurveSupported() { + register(); - return null; - } + SecurityProviderRegistrar r = getRegisteredProvider(EDDSA); + return (r != null) && r.isEnabled() && r.isSupported(); } /* -------------------------------------------------------------------- */ @@ -604,103 +660,99 @@ public final class SecurityUtils { } } - public static synchronized KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException { - register(); + //////////////////////////// Security entities factories ///////////////////////////// - String providerName = getDefaultProvider(); - if (isEDDSACurveSupported() && EdDSASecurityProvider.isEDDSAKeyFactoryAlgorithm(algorithm)) { - providerName = EDDSA; + @SuppressWarnings("unchecked") + public static <T> SecurityEntityFactory<T> resolveSecurityEntityFactory( + Class<T> entityType, String algorithm, Predicate<? super SecurityProviderRegistrar> entitySelector) { + Map<String, SecurityEntityFactory<?>> factoriesMap; + synchronized (SECURITY_ENTITY_FACTORIES) { + factoriesMap = + SECURITY_ENTITY_FACTORIES.computeIfAbsent( + entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); } - if (GenericUtils.isEmpty(providerName)) { - return KeyFactory.getInstance(algorithm); - } else { - return KeyFactory.getInstance(algorithm, providerName); + String effectiveName = SecurityProviderRegistrar.getEffectiveSecurityEntityName(entityType, algorithm); + SecurityEntityFactory<?> factoryEntry; + synchronized (factoriesMap) { + factoryEntry = + factoriesMap.computeIfAbsent( + effectiveName, k -> createSecurityEntityFactory(entityType, entitySelector)); } - } - - public static synchronized Cipher getCipher(String transformation) throws GeneralSecurityException { - register(); - String providerName = getDefaultProvider(); - if (GenericUtils.isEmpty(providerName)) { - return Cipher.getInstance(transformation); - } else { - return Cipher.getInstance(transformation, providerName); - } + return (SecurityEntityFactory<T>) factoryEntry; } - public static synchronized MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException { + public static <T> SecurityEntityFactory<T> createSecurityEntityFactory( + Class<T> entityType, Predicate<? super SecurityProviderRegistrar> entitySelector) { register(); - String providerName = getDefaultProvider(); - if (GenericUtils.isEmpty(providerName)) { - return MessageDigest.getInstance(algorithm); - } else { - return MessageDigest.getInstance(algorithm, providerName); - } - } - - public static synchronized KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException { - register(); - - String providerName = getDefaultProvider(); - if (isEDDSACurveSupported() && EdDSASecurityProvider.isEDDSAKeyPairGeneratorAlgorithm(algorithm)) { - providerName = EDDSA; + SecurityProviderRegistrar registrar; + synchronized (REGISTERED_PROVIDERS) { + registrar = + SecurityProviderRegistrar.findSecurityProviderRegistrarBySecurityEntity( + entitySelector, REGISTERED_PROVIDERS.values()); } - if (GenericUtils.isEmpty(providerName)) { - return KeyPairGenerator.getInstance(algorithm); - } else { - return KeyPairGenerator.getInstance(algorithm, providerName); + try { + return SecurityEntityFactory.toFactory(entityType, registrar, getDefaultProviderChoice()); + } catch (ReflectiveOperationException t) { + Throwable e = GenericUtils.peelException(t); + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else if (e instanceof Error) { + throw (Error) e; + } else { + throw new RuntimeException(e); + } } } - public static synchronized KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException { - register(); - - String providerName = getDefaultProvider(); - if (GenericUtils.isEmpty(providerName)) { - return KeyAgreement.getInstance(algorithm); - } else { - return KeyAgreement.getInstance(algorithm, providerName); - } + public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException { + SecurityEntityFactory<KeyFactory> factory = + resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm)); + return factory.getInstance(algorithm); } - public static synchronized Mac getMac(String algorithm) throws GeneralSecurityException { - register(); + public static Cipher getCipher(String transformation) throws GeneralSecurityException { + SecurityEntityFactory<Cipher> factory = + resolveSecurityEntityFactory(Cipher.class, transformation, r -> r.isCipherSupported(transformation)); + return factory.getInstance(transformation); + } - String providerName = getDefaultProvider(); - if (GenericUtils.isEmpty(providerName)) { - return Mac.getInstance(algorithm); - } else { - return Mac.getInstance(algorithm, providerName); - } + public static MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException { + SecurityEntityFactory<MessageDigest> factory = + resolveSecurityEntityFactory(MessageDigest.class, algorithm, r -> r.isMessageDigestSupported(algorithm)); + return factory.getInstance(algorithm); } - public static synchronized Signature getSignature(String algorithm) throws GeneralSecurityException { - register(); + public static KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException { + SecurityEntityFactory<KeyPairGenerator> factory = + resolveSecurityEntityFactory(KeyPairGenerator.class, algorithm, r -> r.isKeyPairGeneratorSupported(algorithm)); + return factory.getInstance(algorithm); + } - String providerName = getDefaultProvider(); - if (isEDDSACurveSupported() && EdDSASecurityProvider.isEDDSASignatureAlgorithm(algorithm)) { - providerName = EDDSA; - } + public static KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException { + SecurityEntityFactory<KeyAgreement> factory = + resolveSecurityEntityFactory(KeyAgreement.class, algorithm, r -> r.isKeyAgreementSupported(algorithm)); + return factory.getInstance(algorithm); + } - if (GenericUtils.isEmpty(providerName)) { - return Signature.getInstance(algorithm); - } else { - return Signature.getInstance(algorithm, providerName); - } + public static Mac getMac(String algorithm) throws GeneralSecurityException { + SecurityEntityFactory<Mac> factory = + resolveSecurityEntityFactory(Mac.class, algorithm, r -> r.isMacSupported(algorithm)); + return factory.getInstance(algorithm); } - public static synchronized CertificateFactory getCertificateFactory(String type) throws GeneralSecurityException { - register(); + public static Signature getSignature(String algorithm) throws GeneralSecurityException { + SecurityEntityFactory<Signature> factory = + resolveSecurityEntityFactory(Signature.class, algorithm, r -> r.isSignatureSupported(algorithm)); + return factory.getInstance(algorithm); + } - String providerName = getDefaultProvider(); - if (GenericUtils.isEmpty(providerName)) { - return CertificateFactory.getInstance(type); - } else { - return CertificateFactory.getInstance(type, providerName); - } + public static CertificateFactory getCertificateFactory(String type) throws GeneralSecurityException { + SecurityEntityFactory<CertificateFactory> factory = + resolveSecurityEntityFactory(CertificateFactory.class, type, r -> r.isCertificateFactorySupported(type)); + return factory.getInstance(type); } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java index 467720f..4c8722a 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java @@ -26,6 +26,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.KeyPair; +import java.security.NoSuchProviderException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -35,6 +36,7 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.common.util.security.SecurityProviderRegistrar; import org.apache.sshd.common.util.security.SecurityUtils; import org.bouncycastle.openssl.PEMDecryptorProvider; import org.bouncycastle.openssl.PEMEncryptedKeyPair; @@ -96,8 +98,17 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { Object o = r.readObject(); + SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE); + if (registrar == null) { + throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available"); + } + JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter(); - pemConverter.setProvider(SecurityUtils.BOUNCY_CASTLE); + if (registrar.isNamedProviderUsed()) { + pemConverter.setProvider(registrar.getName()); + } else { + pemConverter.setProvider(registrar.getSecurityProvider()); + } if (o instanceof PEMEncryptedKeyPair) { ValidateUtils.checkNotNull(provider, "No password provider for resource=%s", resourceKey); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java new file mode 100644 index 0000000..87744a1 --- /dev/null +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java @@ -0,0 +1,116 @@ +/* + * 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.bouncycastle; + +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Signature; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.sshd.common.PropertyResolverUtils; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.ReflectionUtils; +import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.common.util.threads.ThreadUtils; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class BouncyCastleSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar { + // We want to use reflection API so as not to require BouncyCastle to be present in the classpath + public static final String PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider"; + // Do not define a static registrar instance to minimize class loading issues + private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null); + + public BouncyCastleSecurityProviderRegistrar() { + super(SecurityUtils.BOUNCY_CASTLE); + } + + @Override + public boolean isEnabled() { + if (!super.isEnabled()) { + return false; + } + + // For backward compatibility + return PropertyResolverUtils.getBooleanProperty(this, SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP, true); + } + + @Override + public Provider getSecurityProvider() { + try { + return getOrCreateProvider(PROVIDER_CLASS); + } catch (ReflectiveOperationException t) { + Throwable e = GenericUtils.peelException(t); + log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}", + getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage()); + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + + throw new RuntimeException(e); + } + } + + @Override + public String getDefaultSecurityEntitySupportValue(Class<?> entityType) { + return ALL_OPTIONS_VALUE; + } + + @Override + public boolean isSecurityEntitySupported(Class<?> entityType, String name) { + if (!isSupported()) { + return false; + } + + // Some known values it does not support + if (KeyPairGenerator.class.isAssignableFrom(entityType) + || KeyFactory.class.isAssignableFrom(entityType)) { + if (Objects.compare(name, SecurityUtils.EDDSA, String.CASE_INSENSITIVE_ORDER) == 0) { + return false; + } + } else if (Signature.class.isAssignableFrom(entityType)) { + if (Objects.compare(name, SecurityUtils.CURVE_ED25519_SHA512, String.CASE_INSENSITIVE_ORDER) == 0) { + return false; + } + } + + return super.isSecurityEntitySupported(entityType, name); + } + + @Override + public boolean isSupported() { + Boolean supported; + synchronized (supportHolder) { + supported = supportHolder.get(); + if (supported != null) { + return supported.booleanValue(); + } + + ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass()); + supported = ReflectionUtils.isClassAvailable(cl, PROVIDER_CLASS); + supportHolder.set(supported); + } + + return supported.booleanValue(); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java new file mode 100644 index 0000000..b463d27 --- /dev/null +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java @@ -0,0 +1,105 @@ +/* + * 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.eddsa; + +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Signature; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.sshd.common.PropertyResolverUtils; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.ReflectionUtils; +import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.common.util.threads.ThreadUtils; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegistrar { + public static final String PROVIDER_CLASS = "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProvider"; + // Do not define a static registrar instance to minimize class loading issues + private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null); + + public EdDSASecurityProviderRegistrar() { + super(SecurityUtils.EDDSA); + } + + @Override + public boolean isEnabled() { + if (!super.isEnabled()) { + return false; + } + + // For backward compatibility + return PropertyResolverUtils.getBooleanProperty(this, SecurityUtils.EDDSA_SUPPORTED_PROP, true); + } + + @Override + public Provider getSecurityProvider() { + try { + return getOrCreateProvider(PROVIDER_CLASS); + } catch (ReflectiveOperationException t) { + Throwable e = GenericUtils.peelException(t); + log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}", + getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage()); + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + + throw new RuntimeException(e); + } + } + + @Override + public boolean isSecurityEntitySupported(Class<?> entityType, String name) { + if (!isSupported()) { + return false; + } + + if (KeyPairGenerator.class.isAssignableFrom(entityType) + || KeyFactory.class.isAssignableFrom(entityType)) { + return Objects.compare(name, getName(), String.CASE_INSENSITIVE_ORDER) == 0; + } else if (Signature.class.isAssignableFrom(entityType)) { + return Objects.compare(SecurityUtils.CURVE_ED25519_SHA512, name, String.CASE_INSENSITIVE_ORDER) == 0; + } else { + return false; + } + } + + @Override + public boolean isSupported() { + Boolean supported; + synchronized (supportHolder) { + supported = supportHolder.get(); + if (supported != null) { + return supported.booleanValue(); + } + + ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass()); + supported = ReflectionUtils.isClassAvailable(cl, "net.i2p.crypto.eddsa.EdDSAKey"); + supportHolder.set(supported); + } + + return supported.booleanValue(); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java index de16f41..0c003ba 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java @@ -96,6 +96,18 @@ public final class ThreadUtils { return resolveDefaultClassLoader(anchor == null ? null : anchor.getClass()); } + public static <T> T createDefaultInstance(Class<?> anchor, Class<T> targetType, String className) + throws ReflectiveOperationException { + return createDefaultInstance(resolveDefaultClassLoader(anchor), targetType, className); + } + + public static <T> T createDefaultInstance(ClassLoader cl, Class<T> targetType, String className) + throws ReflectiveOperationException { + Class<?> instanceType = cl.loadClass(className); + Object instance = instanceType.newInstance(); + return targetType.cast(instance); + } + /** * <P>Attempts to find the most suitable {@link ClassLoader} as follows:</P> * <UL> http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java index a08fb30..68531ed 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java @@ -46,13 +46,13 @@ public interface ServerFactoryManager /** * Key used to retrieve any extra lines to be sent during * initial protocol handshake <U>before</U> the identification. - * The configured string value should use {@link #SERVER_EXTRA_IDENT_LINES_SEPARATOR} + * The configured string value should use {@value #SERVER_EXTRA_IDENT_LINES_SEPARATOR} * character to denote line breaks */ String SERVER_EXTRA_IDENTIFICATION_LINES = "server-extra-identification-lines"; /** - * Separator used in the {@link #SERVER_EXTRA_IDENTIFICATION_LINES} configuration + * Separator used in the {@value #SERVER_EXTRA_IDENTIFICATION_LINES} configuration * string to indicate new line break */ char SERVER_EXTRA_IDENT_LINES_SEPARATOR = '|'; @@ -75,7 +75,7 @@ public interface ServerFactoryManager String COMMAND_EXIT_TIMEOUT = "command-exit-timeout"; /** - * Default {@link #COMMAND_EXIT_TIMEOUT} if not set + * Default {@value #COMMAND_EXIT_TIMEOUT} if not set */ long DEFAULT_COMMAND_EXIT_TIMEOUT = TimeUnit.SECONDS.toMillis(5L); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java index 8175520..120f0c0 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java @@ -187,6 +187,7 @@ public class SftpSubsystem * @see #DEFAULT_SUPPORTED_CLIENT_EXTENSIONS */ public static final String CLIENT_EXTENSIONS_PROP = "sftp-client-extensions"; + /** * The default reported supported client extensions */ http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java b/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java index c4eb337..512d21e 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java @@ -32,7 +32,7 @@ import org.apache.sshd.common.util.net.SshdSocketAddress; public interface X11ForwardSupport extends Closeable, IoHandler { /** * Configuration value on the {@link FactoryManager} to control the - * channel open timeout. If not specified then {@link #DEFAULT_CHANNEL_OPEN_TIMEOUT} + * channel open timeout. If not specified then {@value #DEFAULT_CHANNEL_OPEN_TIMEOUT} * value is used */ String CHANNEL_OPEN_TIMEOUT_PROP = "x11-fwd-open-timeout"; @@ -40,7 +40,7 @@ public interface X11ForwardSupport extends Closeable, IoHandler { /** * Configuration value to control from which X11 display number to start - * looking for a free value. If not specified, then {@link #DEFAULT_X11_DISPLAY_OFFSET} + * looking for a free value. If not specified, then {@value #DEFAULT_X11_DISPLAY_OFFSET} * is used */ String X11_DISPLAY_OFFSET = "x11-fwd-display-offset"; @@ -48,7 +48,7 @@ public interface X11ForwardSupport extends Closeable, IoHandler { /** * Configuration value to control up to which (but not including) X11 display number - * to look or a free value. If not specified, then {@link #DEFAULT_X11_MAX_DISPLAYS} + * to look or a free value. If not specified, then {@value #DEFAULT_X11_MAX_DISPLAYS} * is used */ String X11_MAX_DISPLAYS = "x11-fwd-max-display"; @@ -56,7 +56,7 @@ public interface X11ForwardSupport extends Closeable, IoHandler { /** * Configuration value to control the base port number for the X11 display - * number socket binding. If not specified then {@link #DEFAULT_X11_BASE_PORT} + * number socket binding. If not specified then {@value #DEFAULT_X11_BASE_PORT} * value is used */ String X11_BASE_PORT = "x11-fwd-base-port"; @@ -64,7 +64,7 @@ public interface X11ForwardSupport extends Closeable, IoHandler { /** * Configuration value to control the host used to bind to for the X11 display - * when looking for a free port. If not specified, then {@link #DEFAULT_X11_BIND_HOST} + * when looking for a free port. If not specified, then {@value #DEFAULT_X11_BIND_HOST} * is used */ String X11_BIND_HOST = "x11-fwd-bind-host"; http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java index b85fb1e..6d580b8 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java @@ -97,7 +97,7 @@ public abstract class AuthorizedKeysTestSupport extends BaseTestSupport { public static List<String> loadSupportedKeys(BufferedReader rdr) throws IOException { List<String> keyLines = new ArrayList<>(); - boolean eccSupported = SecurityUtils.hasEcc(); + boolean eccSupported = SecurityUtils.isECCSupported(); for (String l = rdr.readLine(); l != null; l = rdr.readLine()) { l = GenericUtils.trimToEmpty(l); // filter out empty and comment lines http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java index eec81c7..2fa299a 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java @@ -24,64 +24,38 @@ import java.lang.reflect.Modifier; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.util.List; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.util.test.BaseTestSupport; +import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; +import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests public class BuiltinIdentitiesTest extends BaseTestSupport { - public BuiltinIdentitiesTest() { - super(); - } - - @Test - public void testFromName() { - for (BuiltinIdentities expected : BuiltinIdentities.VALUES) { - String name = expected.getName(); - for (int index = 0; index < name.length(); index++) { - assertSame(name, expected, BuiltinIdentities.fromName(name)); - name = shuffleCase(name); - } - } - } + private final BuiltinIdentities expected; - @Test - public void testFromAlgorithm() { - for (BuiltinIdentities expected : BuiltinIdentities.VALUES) { - String algorithm = expected.getAlgorithm(); - for (int index = 0; index < algorithm.length(); index++) { - assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm)); - algorithm = shuffleCase(algorithm); - } - } + public BuiltinIdentitiesTest(BuiltinIdentities expected) { + this.expected = expected; } - @Test - public void testFromKey() throws GeneralSecurityException { - for (BuiltinIdentities expected : BuiltinIdentities.VALUES) { - String name = expected.getName(); - if (!expected.isSupported()) { - System.out.println("Skip unsupported built-in identity: " + name); - continue; - } - - KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm()); - KeyPair kp = gen.generateKeyPair(); - outputDebugMessage("Checking built-in identity: %s", name); - assertSame(name + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp)); - assertSame(name + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic())); - assertSame(name + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate())); - } + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + return parameterize(BuiltinIdentities.VALUES); } - @Test - public void testAllConstantsCovered() throws Exception { + @BeforeClass // Dirty hack around the parameterized run + public static void testAllConstantsCovered() throws Exception { Field[] fields = BuiltinIdentities.Constants.class.getFields(); for (Field f : fields) { int mods = f.getModifiers(); @@ -104,4 +78,33 @@ public class BuiltinIdentitiesTest extends BaseTestSupport { assertNotNull("No match found for field " + name + "=" + value, id); } } + + @Test + public void testFromName() { + String name = expected.getName(); + for (int index = 0, count = name.length(); index < count; index++) { + assertSame(name, expected, BuiltinIdentities.fromName(name)); + name = shuffleCase(name); + } + } + + @Test + public void testFromAlgorithm() { + String algorithm = expected.getAlgorithm(); + for (int index = 0, count = algorithm.length(); index < count; index++) { + assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm)); + algorithm = shuffleCase(algorithm); + } + } + + @Test + public void testFromKey() throws GeneralSecurityException { + Assume.assumeTrue("Unsupported built-in identity", expected.isSupported()); + KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm()); + KeyPair kp = gen.generateKeyPair(); + outputDebugMessage("Checking built-in identity: %s", expected); + assertSame(expected + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp)); + assertSame(expected + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic())); + assertSame(expected + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate())); + } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java index 6185de2..b8b46d8 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java @@ -59,7 +59,7 @@ public class KeyUtilsCloneTest extends BaseTestSupport { List<Object[]> list = new ArrayList<>(); addTests(list, KeyPairProvider.SSH_DSS, DSS_SIZES); addTests(list, KeyPairProvider.SSH_RSA, RSA_SIZES); - if (SecurityUtils.hasEcc()) { + if (SecurityUtils.isECCSupported()) { for (ECCurves curve : ECCurves.VALUES) { if (!curve.isSupported()) { continue; http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java index deb29ec..9c21f33 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java @@ -93,7 +93,7 @@ public class SignatureFactoriesTest extends BaseTestSupport implements OptionalF addTests(list, KeyPairProvider.SSH_DSS, BuiltinSignatures.dsa, DSS_SIZES, DSSPublicKeyEntryDecoder.INSTANCE); addTests(list, KeyPairProvider.SSH_RSA, BuiltinSignatures.rsa, RSA_SIZES, RSAPublicKeyDecoder.INSTANCE); - if (SecurityUtils.hasEcc()) { + if (SecurityUtils.isECCSupported()) { for (ECCurves curve : ECCurves.VALUES) { BuiltinSignatures factory = BuiltinSignatures.fromFactoryName(curve.getKeyType()); addTests(list, curve.getName(), factory, http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java index 1fa87ad..a3dec7a 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java @@ -23,9 +23,7 @@ import java.io.IOException; import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.KeyPair; -import java.security.KeyPairGenerator; import java.security.PrivateKey; -import java.security.Provider; import java.security.PublicKey; import java.security.interfaces.DSAPrivateKey; import java.security.interfaces.DSAPublicKey; @@ -45,9 +43,12 @@ import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader; import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider; import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider; import org.apache.sshd.common.keyprovider.FileKeyPairProvider; +import org.apache.sshd.common.util.security.SecurityProviderRegistrar; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.util.test.BaseTestSupport; +import org.junit.AfterClass; import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; @@ -57,6 +58,11 @@ import org.junit.runners.MethodSorters; */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SecurityUtilsTest extends BaseTestSupport { + public static final String BC_NAMED_USAGE_PROP = + SecurityProviderRegistrar.CONFIG_PROP_BASE + + "." + SecurityUtils.BOUNCY_CASTLE + + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY; + private static final String DEFAULT_PASSWORD = "super secret passphrase"; private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = file -> DEFAULT_PASSWORD; @@ -64,6 +70,17 @@ public class SecurityUtilsTest extends BaseTestSupport { super(); } + // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances + @BeforeClass + public static void useNamedBouncyCastleProvider() { + System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString()); + } + + @AfterClass + public static void unsetBouncyCastleProviderUsagePreference() { + System.clearProperty(BC_NAMED_USAGE_PROP); + } + @Test public void testLoadEncryptedDESPrivateKey() throws Exception { testLoadEncryptedRSAPrivateKey("DES-EDE3"); @@ -107,7 +124,7 @@ public class SecurityUtilsTest extends BaseTestSupport { @Test public void testLoadUnencryptedECPrivateKey() throws Exception { - Assume.assumeTrue("EC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("EC not supported", SecurityUtils.isECCSupported()); for (ECCurves c : ECCurves.VALUES) { if (!c.isSupported()) { System.out.println("Skip unsupported curve: " + c.getName()); @@ -180,26 +197,6 @@ public class SecurityUtilsTest extends BaseTestSupport { } @Test - public void testBouncyCastleRegistrationSettings() { - Assume.assumeTrue("Bouncycastle not registered", SecurityUtils.isBouncyCastleRegistered()); - assertTrue("DH Group Exchange not supported", SecurityUtils.isDHGroupExchangeSupported()); - assertEquals("Mismatched max. DH group exchange key size", SecurityUtils.MAX_DHGEX_KEY_SIZE, SecurityUtils.getMaxDHGroupExchangeKeySize()); - assertTrue("ECC not supported", SecurityUtils.hasEcc()); - } - - @Test - public void testBouncyCastleRegistrationProperty() throws GeneralSecurityException { - String propValue = System.getProperty(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP); - Assume.assumeFalse(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP + " property not set", GenericUtils.isEmpty(propValue)); - Assume.assumeFalse(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP + " property is " + propValue, Boolean.parseBoolean(propValue)); - assertFalse("Unexpected registration of provider", SecurityUtils.isBouncyCastleRegistered()); - - KeyPairGenerator kpg = SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM); - Provider provider = kpg.getProvider(); - assertNotEquals("Unexpected used provider", SecurityUtils.BOUNCY_CASTLE, provider.getName()); - } - - @Test public void testSetMaxDHGroupExchangeKeySizeByProperty() { try { for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) { http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java new file mode 100644 index 0000000..a256552 --- /dev/null +++ b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java @@ -0,0 +1,76 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.crypto.Cipher; + +import org.apache.sshd.common.cipher.BuiltinCiphers; +import org.apache.sshd.common.cipher.CipherInformation; +import org.apache.sshd.util.test.BaseTestSupport; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +public class SecurityProviderRegistrarCipherNameTest extends BaseTestSupport { + private final CipherInformation cipherInfo; + + public SecurityProviderRegistrarCipherNameTest(CipherInformation cipherInfo) { + this.cipherInfo = cipherInfo; + } + + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + return Collections.unmodifiableList( + new ArrayList<Object[]>(BuiltinCiphers.VALUES.size()) { + // Not serializing it + private static final long serialVersionUID = 1L; + + { + for (CipherInformation cipherInfo : BuiltinCiphers.VALUES) { + String algorithm = cipherInfo.getAlgorithm(); + String xform = cipherInfo.getTransformation(); + if (!xform.startsWith(algorithm)) { + continue; + } + + add(new Object[]{cipherInfo}); + } + } + }); + } + + @Test + public void testGetEffectiveSecurityEntityName() { + String expected = cipherInfo.getAlgorithm(); + String actual = SecurityProviderRegistrar.getEffectiveSecurityEntityName(Cipher.class, cipherInfo.getTransformation()); + assertEquals("Mismatched pure cipher name", expected, actual); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java new file mode 100644 index 0000000..cf955dc --- /dev/null +++ b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java @@ -0,0 +1,59 @@ +/* + * 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.Arrays; +import java.util.Collection; + +import org.apache.sshd.util.test.BaseTestSupport; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public abstract class SecurityProviderRegistrarTestSupport extends BaseTestSupport { + protected SecurityProviderRegistrarTestSupport() { + super(); + } + + public static Provider testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar) { + return testGetSecurityProviderCaching(prefix, registrar, registrar.getSecurityProvider()); + } + + public static <P extends Provider> P testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar, P expected) { + for (int index = 1; index <= Byte.SIZE; index++) { + Provider actual = registrar.getSecurityProvider(); + assertSame(prefix + ": Mismatched provider instance at invocation #" + index, expected, actual); + } + + return expected; + } + + public static void assertSecurityEntitySupportState( + String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Class<?> ... entities) { + assertSecurityEntitySupportState(prefix, registrar, expected, name, Arrays.asList(entities)); + } + + public static void assertSecurityEntitySupportState( + String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Collection<Class<?>> entities) { + for (Class<?> entity : entities) { + assertEquals(prefix + "[" + entity.getSimpleName() + "]", expected, registrar.isSecurityEntitySupported(entity, name)); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java new file mode 100644 index 0000000..9e12fab --- /dev/null +++ b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java @@ -0,0 +1,83 @@ +/* + * 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.eddsa; + +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Signature; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; + +import org.apache.sshd.common.util.security.SecurityProviderRegistrar; +import org.apache.sshd.common.util.security.SecurityProviderRegistrarTestSupport; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class EdDSASecurityProviderRegistrarTest extends SecurityProviderRegistrarTestSupport { + private static SecurityProviderRegistrar registrarInstance; + + public EdDSASecurityProviderRegistrarTest() { + super(); + } + + @BeforeClass + public static void checkEDDSASupported() { + Assume.assumeTrue(SecurityUtils.isEDDSACurveSupported()); + registrarInstance = new EdDSASecurityProviderRegistrar(); + } + + @Test + public void testSupportedSecurityEntities() { + assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true, registrarInstance.getName(), + KeyPairGenerator.class, KeyFactory.class); + assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true, + SecurityUtils.CURVE_ED25519_SHA512, Signature.class); + + Collection<Class<?>> supported = new HashSet<>(Arrays.asList(KeyPairGenerator.class, KeyFactory.class, Signature.class)); + for (Class<?> entity : SecurityProviderRegistrar.SECURITY_ENTITIES) { + if (supported.contains(entity)) { + continue; + } + assertFalse("Unexpected support for " + entity.getSimpleName(), registrarInstance.isSecurityEntitySupported(entity, registrarInstance.getName())); + } + } + + @Test + public void testGetSecurityProvider() { + Provider expected = registrarInstance.getSecurityProvider(); + assertNotNull("No provider created", expected); + assertEquals("Mismatched provider name", registrarInstance.getName(), expected.getName()); + assertObjectInstanceOf("Mismatched provider type", EdDSASecurityProvider.class, expected); + } + + @Test + public void testGetSecurityProviderCaching() { + testGetSecurityProviderCaching(getCurrentTestName(), registrarInstance); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java index 6f35a61..0619cab 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java @@ -62,7 +62,7 @@ public class PEMGeneratorHostKeyProviderTest extends BaseTestSupport { @Test public void testECnistp256() throws IOException { Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered()); - Assume.assumeTrue("ECC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported()); Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported()); testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1")); } @@ -70,7 +70,7 @@ public class PEMGeneratorHostKeyProviderTest extends BaseTestSupport { @Test public void testECnistp384() throws IOException { Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered()); - Assume.assumeTrue("ECC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported()); Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported()); testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384")); } @@ -78,7 +78,7 @@ public class PEMGeneratorHostKeyProviderTest extends BaseTestSupport { @Test public void testECnistp521() throws IOException { Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered()); - Assume.assumeTrue("ECC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported()); Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported()); testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521")); } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ab72900f/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java index 2b8db21..4075f4e 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java @@ -60,7 +60,7 @@ public class SimpleGeneratorHostKeyProviderTest extends BaseTestSupport { @Test public void testECnistp256() throws IOException { Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered()); - Assume.assumeTrue("ECC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported()); Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported()); testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1")); } @@ -68,7 +68,7 @@ public class SimpleGeneratorHostKeyProviderTest extends BaseTestSupport { @Test public void testECnistp384() throws IOException { Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered()); - Assume.assumeTrue("ECC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported()); Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported()); testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384")); } @@ -76,7 +76,7 @@ public class SimpleGeneratorHostKeyProviderTest extends BaseTestSupport { @Test public void testECnistp521() throws IOException { Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered()); - Assume.assumeTrue("ECC not supported", SecurityUtils.hasEcc()); + Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported()); Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported()); testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521")); }