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 0598b834a78d3f800e8227548a0e001dfb883d49 Author: Thomas Wolf <[email protected]> AuthorDate: Fri Sep 19 21:37:34 2025 +0200 Also enable X25519 and X448 in SunECSecurityProviderRegistrar These algorithms are available since Java 11. If present, use them from SunEC even if Bouncy Castle is also present. Also enable it by default. Fix the two ed25519 PublicKeyEntryDecoders to not call KeyPairGenerator.initialize(int): the SunEC edDSA key pair generator throws an exception if called with value 256; it accepts 255 and 448.[1] [1] https://github.com/openjdk/jdk/blob/b03b6f54c5/src/java.base/share/classes/sun/security/ec/ed/EdDSAParameters.java#L275 --- sshd-common/pom.xml | 9 ++- .../security/SunECSecurityProviderRegistrar.java | 79 +++++++++++++++++----- .../eddsa/generic/Ed25519PublicKeyDecoder.java | 6 ++ .../OpenSSHEd25519PrivateKeyEntryDecoder.java | 6 ++ sshd-core/pom.xml | 46 ++++++++----- sshd-test/pom.xml | 57 +++++++++------- 6 files changed, 146 insertions(+), 57 deletions(-) diff --git a/sshd-common/pom.xml b/sshd-common/pom.xml index 5d0fef547..b16433e1b 100644 --- a/sshd-common/pom.xml +++ b/sshd-common/pom.xml @@ -124,17 +124,21 @@ <configuration> <redirectTestOutputToFile>true</redirectTestOutputToFile> <reportsDirectory>${project.build.directory}/surefire-reports-common</reportsDirectory> + <systemPropertyVariables> + <org.apache.sshd.security.provider.SunECWrapper.enabled>false</org.apache.sshd.security.provider.SunECWrapper.enabled> + </systemPropertyVariables> </configuration> </execution> <execution> - <id>no-net-i2p</id> + <id>bc</id> <goals> <goal>test</goal> </goals> <configuration> <redirectTestOutputToFile>true</redirectTestOutputToFile> - <reportsDirectory>${project.build.directory}/surefire-reports-no-net-i2p</reportsDirectory> + <reportsDirectory>${project.build.directory}/surefire-reports-bc</reportsDirectory> <systemPropertyVariables> + <org.apache.sshd.security.provider.SunECWrapper.enabled>false</org.apache.sshd.security.provider.SunECWrapper.enabled> <org.apache.sshd.security.provider.EdDSA.enabled>false</org.apache.sshd.security.provider.EdDSA.enabled> </systemPropertyVariables> </configuration> @@ -200,4 +204,5 @@ </plugin> </plugins> </build> + </project> 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 index f0fea7c32..702c29db7 100644 --- 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 @@ -37,37 +37,84 @@ import org.apache.sshd.common.util.GenericUtils; * 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}. + * This registrar is <em>enabled</em> by default. It can be disabled via a system property + * {@code org.apache.sshd.security.provider.SunECWrapper.enabled=false}. * </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. + * The registrar can be configured as usual. By default are enabled: + * </p> + * <dl> + * <dt>"Ed25519"</dt> + * <dd>{@code KeyFactory}, {@code KeyPairGenerator}, and {@code Signature}, if "Ed25519" is supported by SunEC</dd> + * <dt>"X25519"</dt> + * <dd>{@code KeyAgreement}, {@code KeyFactory}, and {@code KeyPairGenerator}, if "X25519" is supported by SunEC</dd> + * <dt>"X448"</dt> + * <dd>{@code KeyAgreement}, {@code KeyFactory}, and {@code KeyPairGenerator}, if "X448" is supported by SunEC</dd> + * </dl> + * <p> + * Everything else is disabled. * </p> * * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> */ public class SunECSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar { + private static final String X25519 = "X25519"; + + private static final String X448 = "X448"; + 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; - } + Provider provider = getSecurityProvider(); + + boolean haveEd25519 = have(SecurityUtils.ED25519, provider); + boolean haveX25519 = have("X25519", provider); + boolean haveX448 = have("X448", provider); + + String keyAgreement = null; + String generator = null; + String factory = null; + String signature = null; if (haveEd25519) { - defaultProperties.put(baseName + ".KeyPairGenerator", "Ed25519"); - defaultProperties.put(baseName + ".KeyFactory", "Ed25519"); - defaultProperties.put(baseName + ".Signature", "Ed25519"); + generator = SecurityUtils.ED25519; + factory = SecurityUtils.ED25519; + signature = SecurityUtils.ED25519; + } + if (haveX25519) { + keyAgreement = X25519; + generator = generator == null ? X25519 : (generator + ',' + X25519); + factory = factory == null ? X25519 : (factory + ',' + X25519); + } + if (haveX448) { + keyAgreement = keyAgreement == null ? X448 : (keyAgreement + ',' + X448); + generator = generator == null ? X448 : (generator + ',' + X448); + factory = factory == null ? X448 : (factory + ',' + X448); } + String baseName = getBasePropertyName(); + if (keyAgreement != null) { + defaultProperties.put(baseName + ".KeyAgreement", keyAgreement); + } + if (generator != null) { + defaultProperties.put(baseName + ".KeyPairGenerator", generator); + } + if (factory != null) { + defaultProperties.put(baseName + ".KeyFactory", factory); + } + if (signature != null) { + defaultProperties.put(baseName + ".Signature", signature); + } + } + } + + private static boolean have(String algorithm, Provider provider) { + try { + KeyFactory factory = KeyFactory.getInstance(algorithm, provider); + return factory != null; + } catch (NoSuchAlgorithmException e) { + return false; } } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/Ed25519PublicKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/Ed25519PublicKeyDecoder.java index 712defeac..e5f1941a8 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/Ed25519PublicKeyDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/Ed25519PublicKeyDecoder.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.KeyFactory; +import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.util.Collections; @@ -55,6 +56,11 @@ public final class Ed25519PublicKeyDecoder extends AbstractPublicKeyEntryDecoder return SecurityUtils.getKeyPairGenerator(SecurityUtils.ED25519); } + @Override + public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException { + return getKeyPairGenerator().generateKeyPair(); + } + @Override public String encodePublicKey(OutputStream s, PublicKey key) throws IOException { Objects.requireNonNull(key, "No public key provided"); diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/OpenSSHEd25519PrivateKeyEntryDecoder.java index aafd8a717..3634ab053 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/OpenSSHEd25519PrivateKeyEntryDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/OpenSSHEd25519PrivateKeyEntryDecoder.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyFactory; +import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -157,6 +158,11 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntr return SecurityUtils.getKeyPairGenerator(SecurityUtils.ED25519); } + @Override + public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException { + return getKeyPairGenerator().generateKeyPair(); + } + @Override public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException { return SecurityUtils.getKeyFactory(SecurityUtils.ED25519); diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml index c315235cf..3f3dfa97f 100644 --- a/sshd-core/pom.xml +++ b/sshd-core/pom.xml @@ -189,28 +189,42 @@ <configuration> <redirectTestOutputToFile>true</redirectTestOutputToFile> <reportsDirectory>${project.build.directory}/surefire-reports-nio2</reportsDirectory> + <systemPropertyVariables> + <org.apache.sshd.security.provider.SunECWrapper.enabled>false</org.apache.sshd.security.provider.SunECWrapper.enabled> + </systemPropertyVariables> </configuration> </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <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.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> + <excludes> + <!-- These tests fail inexplicably without Bouncycastle --> + <exclude>**/*LoadTest.java</exclude> + <exclude>**/SinglePublicKeyAuthTest.java</exclude> + <exclude>**/ClientTest.java</exclude> + </excludes> + </configuration> + <executions> <execution> <id>jce</id> <goals> - <goal>test</goal> + <goal>integration-test</goal> + <goal>verify</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> - <!-- These tests fail inexplicably without Bouncycastle --> - <exclude>**/*LoadTest.java</exclude> - <exclude>**/SinglePublicKeyAuthTest.java</exclude> - <exclude>**/ClientTest.java</exclude> - </excludes> - </configuration> </execution> </executions> </plugin> diff --git a/sshd-test/pom.xml b/sshd-test/pom.xml index 24ae7e6fb..3ac83a5e1 100644 --- a/sshd-test/pom.xml +++ b/sshd-test/pom.xml @@ -132,33 +132,44 @@ </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> <profiles> + <profile> + <id>multirelease</id> + <activation> + <jdk>[11,)</jdk> + </activation> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-mr</reportsDirectory> + <systemPropertyVariables> + <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> + </profile> + <profile> <id>test-mina</id> <activation>
