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 f09406ea76af38242cd549864d020394e9faae3a Author: Thomas Wolf <[email protected]> AuthorDate: Tue Sep 16 19:41:22 2025 +0200 Add SunECSecurityProviderRegistrar Disabled by default it can be used to force the use of JCE algorithms even if Bouncy Castle is present. Users are responsible themselves for enabling consistent sets of algorithms. Mix-and-match between SunEC and BC will not work (for instance EC keys from SunEC with EC signatures from BC). By default the registrar has ed25519 enabled, if supported by the Java version it's running on. So if this registrar is enabled and Java has ed25519, it will be used instead of Bouncy Castle ed25519. --- sshd-common/pom.xml | 122 +++++++----------- .../sshd/common/util/security/SecurityUtils.java | 1 + .../security/SunECSecurityProviderRegistrar.java | 140 ++++++++++++++++++++ sshd-core/pom.xml | 96 +++++--------- .../FileHostKeyCertificateProviderTest.java | 4 +- .../java/org/apache/sshd/server/ServerTest.java | 4 +- sshd-mina/pom.xml | 143 ++++++++++----------- sshd-test/pom.xml | 23 ++++ 8 files changed, 313 insertions(+), 220 deletions(-) diff --git a/sshd-common/pom.xml b/sshd-common/pom.xml index 3582c74f6..6ae6e6dbe 100644 --- a/sshd-common/pom.xml +++ b/sshd-common/pom.xml @@ -72,80 +72,6 @@ </dependency> </dependencies> - <profiles> - <profile> - <id>no-net-i2p</id> - <activation> - <property> - <name>test.no-net-i2p</name> - <value>!disable</value> - </property> - </activation> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <executions> - <execution> - <id>no-net-i2p</id> - <goals> - <goal>test</goal> - </goals> - <configuration> - <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-no-net-i2p</reportsDirectory> - <systemPropertyVariables> - <org.apache.sshd.security.provider.EdDSA.enabled>false</org.apache.sshd.security.provider.EdDSA.enabled> - </systemPropertyVariables> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>jce</id> - <activation> - <property> - <name>test.jce</name> - <value>!disable</value> - </property> - </activation> - - <build> - <plugins> - <plugin> - <!-- MR JAR needs to run tests from the built JAR, so use failsafe instead of surefire --> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-failsafe-plugin</artifactId> - <executions> - <execution> - <id>jce</id> - <goals> - <goal>integration-test</goal> - </goals> - <configuration> - <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-jce</reportsDirectory> - <systemPropertyVariables> - <org.apache.sshd.security.provider.EdDSA.enabled>false</org.apache.sshd.security.provider.EdDSA.enabled> - <org.apache.sshd.security.provider.BC.enabled>false</org.apache.sshd.security.provider.BC.enabled> - </systemPropertyVariables> - <includes> - <include>**/*Test.java</include> - </includes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> - <build> <resources> <resource> @@ -189,10 +115,56 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>default-test</id> + <goals> + <goal>test</goal> + </goals> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-common</reportsDirectory> + </configuration> + </execution> + <execution> + <id>no-net-i2p</id> + <goals> + <goal>test</goal> + </goals> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-no-net-i2p</reportsDirectory> + <systemPropertyVariables> + <org.apache.sshd.security.provider.EdDSA.enabled>false</org.apache.sshd.security.provider.EdDSA.enabled> + </systemPropertyVariables> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <!-- MR JAR needs to run tests from the built JAR, so use failsafe instead of surefire --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> <configuration> <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-common</reportsDirectory> + <reportsDirectory>${project.build.directory}/surefire-reports-jce</reportsDirectory> + <systemPropertyVariables> + <org.apache.sshd.security.provider.EdDSA.enabled>false</org.apache.sshd.security.provider.EdDSA.enabled> + <org.apache.sshd.security.provider.BC.enabled>false</org.apache.sshd.security.provider.BC.enabled> + </systemPropertyVariables> + <includes> + <include>**/*Test.java</include> + </includes> </configuration> + <executions> + <execution> + <id>jce</id> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> 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 2bf9881a3..b3f621ae9 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 @@ -137,6 +137,7 @@ public final class SecurityUtils { public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS = Collections.unmodifiableList( Arrays.asList( "org.apache.sshd.common.util.security.SunJCESecurityProviderRegistrar", + "org.apache.sshd.common.util.security.SunECSecurityProviderRegistrar", "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar", "org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar")); diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SunECSecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SunECSecurityProviderRegistrar.java new file mode 100644 index 000000000..f0fea7c32 --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SunECSecurityProviderRegistrar.java @@ -0,0 +1,140 @@ +/* + * 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.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.HashMap; +import java.util.Map; + +import org.apache.sshd.common.util.GenericUtils; + +/** + * This is registrar ensures that even if other registrars are active, we still use the Java built-in security provider + * at least for some security entities. + * <p> + * This registrar can be used to enforce using some security entities from the JDK's SunEC provider even if other + * registrars also provide the same entity. Care should be taken to use consistent configurations. For instance, to use + * EC keys from this provider instead of Bouncy Castle, all of "KeyPairGenerator", "KeyFactory", and "Signature" should + * be set correctly. Mixing SunEC keys with Bouncy Castle signatures won't work. + * </p> + * <p> + * This registrar is <em>disabled</em> by default. It can be enabled via a system property + * {@code org.apache.sshd.security.provider.SunECWrapper.enabled=true}. + * </p> + * <p> + * The registrar can be configured as usual. By default it has only the "Ed25519" {@code KeyFactory}, + * {@code KeyPairGenerator}, and {@code Signature} enabled; everything else is disabled. + * </p> + * + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public class SunECSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar { + + private final Map<String, String> defaultProperties = new HashMap<>(); + + public SunECSecurityProviderRegistrar() { + super("SunECWrapper"); + String baseName = getBasePropertyName(); + defaultProperties.put(baseName + ".enabled", "false"); + if (isSupported()) { + boolean haveEd25519; + try { + KeyFactory factory = KeyFactory.getInstance(SecurityUtils.ED25519, getSecurityProvider()); + haveEd25519 = factory != null; + } catch (NoSuchAlgorithmException e) { + haveEd25519 = false; + } + if (haveEd25519) { + defaultProperties.put(baseName + ".KeyPairGenerator", "Ed25519"); + defaultProperties.put(baseName + ".KeyFactory", "Ed25519"); + defaultProperties.put(baseName + ".Signature", "Ed25519"); + } + } + } + + @Override + public boolean isEnabled() { + if (SecurityUtils.isFipsMode() || !super.isEnabled()) { + return false; + } + return isSupported(); + } + + @Override + public String getProviderName() { + return "SunEC"; + } + + @Override + public String getDefaultSecurityEntitySupportValue(Class<?> entityType) { + return ""; + } + + @Override + public Boolean getBoolean(String name) { + Boolean configured = super.getBoolean(name); + if (configured == null) { + String value = defaultProperties.get(name); + if (value != null) { + configured = Boolean.valueOf(value); + } + } + return configured; + } + + @Override + public boolean getBooleanProperty(String name, boolean def) { + Boolean configured = getBoolean(name); + if (configured == null) { + return def; + } + return configured.booleanValue(); + } + + @Override + public String getString(String name) { + String configured = super.getString(name); + if (GenericUtils.isEmpty(configured)) { + String byDefault = defaultProperties.get(name); + if (byDefault != null) { + return byDefault; + } + } + return configured; + } + + @Override + public boolean isNamedProviderUsed() { + return false; + } + + @Override + public Provider getSecurityProvider() { + return Security.getProvider(getProviderName()); + } + + @Override + public boolean isSupported() { + return getSecurityProvider() != null; + } + +} diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml index 3b65b0dc9..c315235cf 100644 --- a/sshd-core/pom.xml +++ b/sshd-core/pom.xml @@ -180,71 +180,41 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-nio2</reportsDirectory> - </configuration> - </plugin> - </plugins> - </build> - - <profiles> - <profile> - <id>test-jce</id> - <activation> - <property> - <name>test.jce</name> - <value>!disable</value> - </property> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <executions> - <execution> - <id>jce</id> - <goals> - <goal>test</goal> - </goals> - <configuration> - <reportsDirectory>${project.build.directory}/surefire-reports-jce</reportsDirectory> - <systemPropertyVariables> - <!-- Enable using deprecated ssh-rsa signature keys with JSch 0.2.x --> - <jsch.server_host_key>ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa</jsch.server_host_key> - <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> - </systemPropertyVariables> - <excludes> - <!-- These tests fail inexplicably without Bouncycastle --> - <exclude>**/*LoadTest.java</exclude> - <exclude>**/SinglePublicKeyAuthTest.java</exclude> - <exclude>**/ClientTest.java</exclude> - </excludes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>load-test</id> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>default-test</id> + <goals> + <goal>test</goal> + </goals> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-nio2</reportsDirectory> + </configuration> + </execution> + <execution> + <id>jce</id> + <goals> + <goal>test</goal> + </goals> <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-jce</reportsDirectory> + <systemPropertyVariables> + <!-- Enable using deprecated ssh-rsa signature keys with JSch 0.2.x --> + <jsch.server_host_key>ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa</jsch.server_host_key> + <org.apache.sshd.security.provider.BC.enabled>false</org.apache.sshd.security.provider.BC.enabled> + </systemPropertyVariables> <excludes> - <exclude>foo</exclude> + <!-- These tests fail inexplicably without Bouncycastle --> + <exclude>**/*LoadTest.java</exclude> + <exclude>**/SinglePublicKeyAuthTest.java</exclude> + <exclude>**/ClientTest.java</exclude> </excludes> </configuration> - </plugin> - </plugins> - </build> - </profile> - </profiles> + </execution> + </executions> + </plugin> + </plugins> + </build> + </project> diff --git a/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProviderTest.java index 7e8718d96..10aa4150d 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProviderTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProviderTest.java @@ -33,8 +33,8 @@ class FileHostKeyCertificateProviderTest extends JUnitTestSupport { FileHostKeyCertificateProvider provider = new FileHostKeyCertificateProvider( getTestResourcesFolder().resolve("dummy_user-cert" + PublicKeyEntry.PUBKEY_FILE_SUFFIX)); Exception e = assertThrows(Exception.class, () -> provider.loadCertificates(null)); - assertTrue(e.getMessage().contains("line 1"), "Expected error in line 1"); + assertTrue(e.getMessage().contains("line 1"), "Expected error in line 1: " + e.toString()); assertTrue(e.getMessage().contains("host") || e.getMessage().contains("user"), - "Unexpected exception message: " + e.getMessage()); + "Unexpected exception message: " + e.toString()); } } diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java index 08be180bb..a6764b6b3 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java @@ -348,11 +348,11 @@ class ServerTest extends BaseTestSupport { try (Channel channel = GenericUtils.head(channels)) { final long maxTimeoutValue = idleTimeoutValue + disconnectTimeoutValue + TimeUnit.SECONDS.toMillis(3L); - final long maxWaitNanos = TimeUnit.MILLISECONDS.toNanos(maxTimeoutValue); + final long maxWaitNanos = TimeUnit.MILLISECONDS.toNanos(maxTimeoutValue + 200); RemoteWindow wRemote = channel.getRemoteWindow(); for (long totalNanoTime = 0L; wRemote.getSize() > 0;) { long nanoStart = System.nanoTime(); - Thread.sleep(1L); + Thread.sleep(100); long nanoEnd = System.nanoTime(); long nanoDuration = nanoEnd - nanoStart; diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml index a0d1b1601..839e76fa0 100644 --- a/sshd-mina/pom.xml +++ b/sshd-mina/pom.xml @@ -111,26 +111,70 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-mina</reportsDirectory> - <forkCount>1</forkCount> - <reuseForks>false</reuseForks> - <excludes> - <!-- These tests use NIO2 explicitly --> - <exclude>**/Nio2ServiceTest.java</exclude> - <!-- reading files from classpath doesn't work correctly w/ reusable test jar --> - <exclude>**/OpenSSHCertificateTest.java</exclude> - <!-- A MinaServiceFactory cannot be instantiated with a mock CloseableExecutorService. --> - <exclude>**/DefaultIoServiceFactoryFactoryTest.java</exclude> - </excludes> - <!-- No need to re-run core tests that do not involve session creation --> - <excludedGroups>NoIoTestCase</excludedGroups> - <!-- Tests are located in the sshd-core reusable test jar --> - <dependenciesToScan> - <dependency>org.apache.sshd:sshd-core</dependency> - </dependenciesToScan> - </configuration> + <executions> + <execution> + <id>default-test</id> + <goals> + <goal>test</goal> + </goals> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-mina</reportsDirectory> + <forkCount>1</forkCount> + <reuseForks>false</reuseForks> + <excludes> + <!-- These tests use NIO2 explicitly --> + <exclude>**/Nio2ServiceTest.java</exclude> + <!-- reading files from classpath doesn't work correctly w/ reusable test jar --> + <exclude>**/OpenSSHCertificateTest.java</exclude> + <!-- A MinaServiceFactory cannot be instantiated with a mock CloseableExecutorService. --> + <exclude>**/DefaultIoServiceFactoryFactoryTest.java</exclude> + </excludes> + <!-- No need to re-run core tests that do not involve session creation --> + <excludedGroups>NoIoTestCase</excludedGroups> + <!-- Tests are located in the sshd-core reusable test jar --> + <dependenciesToScan> + <dependency>org.apache.sshd:sshd-core</dependency> + </dependenciesToScan> + </configuration> + </execution> + <execution> + <id>test-mina-2.2.X</id> + <goals> + <goal>test</goal> + </goals> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-mina-22</reportsDirectory> + <forkCount>1</forkCount> + <reuseForks>false</reuseForks> + <excludes> + <!-- These tests use NIO2 explicitly --> + <exclude>**/Nio2ServiceTest.java</exclude> + <!-- reading files from classpath doesn't work correctly w/ reusable test jar --> + <exclude>**/OpenSSHCertificateTest.java</exclude> + <!-- A MinaServiceFactory cannot be instantiated with a mock CloseableExecutorService. --> + <exclude>**/DefaultIoServiceFactoryFactoryTest.java</exclude> + </excludes> + <!-- No need to re-run core tests that do not involve session creation --> + <excludedGroups>NoIoTestCase</excludedGroups> + <!-- Tests are located in the sshd-core reusable test jar --> + <dependenciesToScan> + <dependency>org.apache.sshd:sshd-core</dependency> + </dependenciesToScan> + <classpathDependencyExcludes> + <classpathDependencyExclude>org.apache.mina:mina-core</classpathDependencyExclude> + </classpathDependencyExcludes> + <additionalClasspathDependencies> + <additionalClasspathDependency> + <groupId>org.apache.mina</groupId> + <artifactId>mina-core</artifactId> + <version>2.2.4</version> + </additionalClasspathDependency> + </additionalClasspathDependencies> + </configuration> + </execution> + </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -141,62 +185,5 @@ </plugin> </plugins> </build> - - <profiles> - <profile> - <id>test-mina-2.2.X</id> - <activation> - <property> - <name>test.mina22</name> - <value>!disable</value> - </property> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <executions> - <execution> - <id>test-mina-2.2.X</id> - <goals> - <goal>test</goal> - </goals> - <configuration> - <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-mina-22</reportsDirectory> - <forkCount>1</forkCount> - <reuseForks>false</reuseForks> - <excludes> - <!-- These tests use NIO2 explicitly --> - <exclude>**/Nio2ServiceTest.java</exclude> - <!-- reading files from classpath doesn't work correctly w/ reusable test jar --> - <exclude>**/OpenSSHCertificateTest.java</exclude> - <!-- A MinaServiceFactory cannot be instantiated with a mock CloseableExecutorService. --> - <exclude>**/DefaultIoServiceFactoryFactoryTest.java</exclude> - </excludes> - <!-- No need to re-run core tests that do not involve session creation --> - <excludedGroups>NoIoTestCase</excludedGroups> - <!-- Tests are located in the sshd-core reusable test jar --> - <dependenciesToScan> - <dependency>org.apache.sshd:sshd-core</dependency> - </dependenciesToScan> - <classpathDependencyExcludes> - <classpathDependencyExclude>org.apache.mina:mina-core</classpathDependencyExclude> - </classpathDependencyExcludes> - <additionalClasspathDependencies> - <additionalClasspathDependency> - <groupId>org.apache.mina</groupId> - <artifactId>mina-core</artifactId> - <version>2.2.4</version> - </additionalClasspathDependency> - </additionalClasspathDependencies> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> + </project> diff --git a/sshd-test/pom.xml b/sshd-test/pom.xml index 339617ca5..24ae7e6fb 100644 --- a/sshd-test/pom.xml +++ b/sshd-test/pom.xml @@ -132,6 +132,29 @@ </systemPropertyVariables> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-nio2</reportsDirectory> + <systemPropertyVariables> + <org.apache.sshd.security.provider.SunECWrapper.enabled>true</org.apache.sshd.security.provider.SunECWrapper.enabled> + <org.apache.sshd.common.io.IoServiceFactoryFactory>org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory</org.apache.sshd.common.io.IoServiceFactoryFactory> + </systemPropertyVariables> + <includes> + <include>**/*Test.java</include> + </includes> + </configuration> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build>
