You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by tw...@apache.org on 2025/10/09 06:03:51 UTC
(mina-sshd) 01/01: Merge branch 'master' into 3.0.0
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 1152a651a04391c9bb83ecf4e07a339b6ad81169
Merge: c975a971e 8f0c296a2
Author: Thomas Wolf <tw...@apache.org>
AuthorDate: Wed Oct 8 23:41:02 2025 +0200
Merge branch 'master' into 3.0.0
docs/server-setup.md | 2 +-
docs/standards.md | 2 +-
pom.xml | 4 +-
.../org/apache/sshd/common/cipher/ECCurves.java | 124 ++++++++-------------
.../util/security/SecurityEntityFactory.java | 20 ++++
.../sshd/common/util/security/SecurityUtils.java | 8 ++
.../common/util/security/SecurityUtilsTest.java | 16 +++
7 files changed, 92 insertions(+), 84 deletions(-)
diff --cc pom.xml
index 729168748,d15fb01a0..b8d3567e8
--- a/pom.xml
+++ b/pom.xml
@@@ -110,10 -115,10 +110,10 @@@
<grpc.version>1.75.0</grpc.version> <!-- Used only in tests -->
<maven.archiver.version>3.6.4</maven.archiver.version>
- <plexus.archiver.version>4.10.1</plexus.archiver.version>
+ <plexus.archiver.version>4.10.2</plexus.archiver.version>
<!-- See https://pmd.github.io/ for available latest version -->
<pmd.version>7.17.0</pmd.version>
-
+ <japicmp.version>0.23.1</japicmp.version>
<sshd.tests.timeout.factor>1.0</sshd.tests.timeout.factor>
<sshd.tests.rerun.count>2</sshd.tests.rerun.count>
diff --cc sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index 6db662ed7,5967b079d..a53bd36fd
--- a/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@@ -162,13 -100,14 +100,15 @@@ public enum ECCurves implements KeyType
private final String keyType;
private final String oidString;
private final List<Integer> oidValue;
- private final ECParameterSpec params;
- private final int keySize;
- private ECParameterSpec params;
- private volatile int keySize = -1;
private final int numOctets;
private final DigestFactory digestFactory;
- ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
++ private ECParameterSpec params;
++ private volatile int keySize = -1;
++
+ ECCurves(String name, String secName, int[] oid, int numOctets, DigestFactory digestFactory) {
this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
+ this.secName = ValidateUtils.checkNotNullAndNotEmpty(secName, "No SEC curve name");
this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
diff --cc sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
index 569abb345,badd54166..00a17f1aa
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
@@@ -19,227 -19,175 +19,247 @@@
package org.apache.sshd.common.util.security;
-import java.lang.reflect.Method;
++import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.Provider;
-import java.util.Objects;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
-import org.apache.sshd.common.util.ExceptionUtils;
import org.apache.sshd.common.util.ValidateUtils;
/**
- * @param <T> Type of security entity being generated by this factory
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface SecurityEntityFactory<T> {
- Class<T> getEntityType();
-
- T getInstance(String algorithm) throws GeneralSecurityException;
-
- /**
- * Uses reflection in order to wrap the {@code getInstance} method(s) as a security entity factory.
- *
- * @param <F> Type of entity being generated by the factor
- * @param entityType The entity type class
- * @param registrar The {@code SecurityProviderRegistrar} to use - if {@code null} then default
- * provider is used (if specified).
- * @param defaultProvider Default provider choice to use if no registrar provided. If
- * {@code null}/empty then JCE default is used
- * @return The {@link SecurityEntityFactory} for the entity
- * @throws ReflectiveOperationException If failed to create the factory
- * @see #toDefaultFactory(Class)
- * @see #toNamedProviderFactory(Class, String)
- * @see #toProviderInstanceFactory(Class, Provider)
- * @see SecurityProviderChoice#isNamedProviderUsed()
- * @see SecurityProviderChoice#getSecurityProvider()
- */
- static <F> SecurityEntityFactory<F> toFactory(
- Class<F> entityType, SecurityProviderChoice registrar, SecurityProviderChoice defaultProvider)
- throws ReflectiveOperationException {
- if (registrar == null) {
- if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
- return toDefaultFactory(entityType);
- } else if (defaultProvider.isNamedProviderUsed()) {
- return toNamedProviderFactory(entityType, defaultProvider.getProviderName());
- } else {
- return toProviderInstanceFactory(entityType, defaultProvider.getSecurityProvider());
- }
- } else if (registrar.isNamedProviderUsed()) {
- return toNamedProviderFactory(entityType, registrar.getProviderName());
- } else {
- return toProviderInstanceFactory(entityType, registrar.getSecurityProvider());
- }
+public interface SecurityEntityFactory {
+
++ default AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
++ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
++ }
++
+ default CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default Cipher createCipher(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default KeyAgreement createKeyAgreement(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default KeyFactory createKeyFactory(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default KeyPairGenerator createKeyPairGenerator(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default Mac createMac(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default MessageDigest createMessageDigest(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default Signature createSignature(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default SecureRandom createSecureRandom(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
+ }
+
+ default KEM createKEM(String algorithm) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
}
- static <F> SecurityEntityFactory<F> toDefaultFactory(Class<F> entityType)
- throws ReflectiveOperationException {
- Method m = entityType.getDeclaredMethod("getInstance", String.class);
- return new SecurityEntityFactory<F>() {
- private final String s = SecurityEntityFactory.class.getSimpleName()
- + "[" + entityType.getSimpleName() + "]"
- + "[default]";
-
- @Override
- public Class<F> getEntityType() {
- return entityType;
- }
-
- @Override
- public F getInstance(String algorithm) throws GeneralSecurityException {
- try {
- Object value = m.invoke(null, algorithm);
- return entityType.cast(value);
- } catch (ReflectiveOperationException t) {
- Throwable e = ExceptionUtils.peelException(t);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException) e;
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new GeneralSecurityException(e);
- }
- }
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
+ class Named implements SecurityEntityFactory {
+
+ private final String name;
+
+ public Named(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "Security provider name must not be empty");
+ }
+
++ @Override
++ public AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
++ return AlgorithmParameters.getInstance(algorithm, name);
++ }
++
+ @Override
+ public CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
+ return CertificateFactory.getInstance(algorithm, name);
+ }
+
+ @Override
+ public Cipher createCipher(String algorithm) throws GeneralSecurityException {
+ return Cipher.getInstance(algorithm, name);
+ }
+
+ @Override
+ public KeyAgreement createKeyAgreement(String algorithm) throws GeneralSecurityException {
+ return KeyAgreement.getInstance(algorithm, name);
+ }
+
+ @Override
+ public KeyFactory createKeyFactory(String algorithm) throws GeneralSecurityException {
+ return KeyFactory.getInstance(algorithm, name);
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGenerator(String algorithm) throws GeneralSecurityException {
+ return KeyPairGenerator.getInstance(algorithm, name);
+ }
+
+ @Override
+ public Mac createMac(String algorithm) throws GeneralSecurityException {
+ return Mac.getInstance(algorithm, name);
+ }
+
+ @Override
+ public MessageDigest createMessageDigest(String algorithm) throws GeneralSecurityException {
+ return MessageDigest.getInstance(algorithm, name);
+ }
+
+ @Override
+ public Signature createSignature(String algorithm) throws GeneralSecurityException {
+ return Signature.getInstance(algorithm, name);
+ }
+
+ @Override
+ public SecureRandom createSecureRandom(String algorithm) throws GeneralSecurityException {
+ return SecureRandom.getInstance(algorithm, name);
+ }
}
- static <F> SecurityEntityFactory<F> toNamedProviderFactory(Class<F> entityType, String name)
- throws ReflectiveOperationException {
- ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
- Method m = entityType.getDeclaredMethod("getInstance", String.class, String.class);
- return new SecurityEntityFactory<F>() {
- private final String s = SecurityEntityFactory.class.getSimpleName()
- + "[" + entityType.getSimpleName() + "]"
- + "[" + name + "]";
-
- @Override
- public Class<F> getEntityType() {
- return entityType;
- }
-
- @Override
- public F getInstance(String algorithm) throws GeneralSecurityException {
- try {
- Object value = m.invoke(null, algorithm, name);
- return entityType.cast(value);
- } catch (ReflectiveOperationException t) {
- Throwable e = ExceptionUtils.peelException(t);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException) e;
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new GeneralSecurityException(e);
- }
- }
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
+ class ByProvider implements SecurityEntityFactory {
+
+ private final Provider provider;
+
+ public ByProvider(Provider name) {
+ this.provider = ValidateUtils.checkNotNull(name, "Security provider must not be null");
+ }
+
++ @Override
++ public AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
++ return AlgorithmParameters.getInstance(algorithm, provider);
++ }
++
+ @Override
+ public CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
+ return CertificateFactory.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public Cipher createCipher(String algorithm) throws GeneralSecurityException {
+ return Cipher.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public KeyAgreement createKeyAgreement(String algorithm) throws GeneralSecurityException {
+ return KeyAgreement.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public KeyFactory createKeyFactory(String algorithm) throws GeneralSecurityException {
+ return KeyFactory.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGenerator(String algorithm) throws GeneralSecurityException {
+ return KeyPairGenerator.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public Mac createMac(String algorithm) throws GeneralSecurityException {
+ return Mac.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public MessageDigest createMessageDigest(String algorithm) throws GeneralSecurityException {
+ return MessageDigest.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public Signature createSignature(String algorithm) throws GeneralSecurityException {
+ return Signature.getInstance(algorithm, provider);
+ }
+
+ @Override
+ public SecureRandom createSecureRandom(String algorithm) throws GeneralSecurityException {
+ return SecureRandom.getInstance(algorithm, provider);
+ }
}
- static <F> SecurityEntityFactory<F> toProviderInstanceFactory(Class<F> entityType, Provider provider)
- throws ReflectiveOperationException {
- Objects.requireNonNull(provider, "No provider instance");
- Method m = entityType.getDeclaredMethod("getInstance", String.class, Provider.class);
- return new SecurityEntityFactory<F>() {
- private final String s = SecurityEntityFactory.class.getSimpleName()
- + "[" + entityType.getSimpleName() + "]"
- + "[" + Provider.class.getSimpleName() + "]"
- + "[" + provider.getName() + "]";
-
- @Override
- public Class<F> getEntityType() {
- return entityType;
- }
-
- @Override
- public F getInstance(String algorithm) throws GeneralSecurityException {
- try {
- Object value = m.invoke(null, algorithm, provider);
- return entityType.cast(value);
- } catch (ReflectiveOperationException t) {
- Throwable e = ExceptionUtils.peelException(t);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException) e;
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new GeneralSecurityException(e);
- }
- }
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
+ enum Default implements SecurityEntityFactory {
+
+ INSTANCE;
+
++ @Override
++ public AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
++ return AlgorithmParameters.getInstance(algorithm);
++ }
++
+ @Override
+ public CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
+ return CertificateFactory.getInstance(algorithm);
+ }
+
+ @Override
+ public Cipher createCipher(String algorithm) throws GeneralSecurityException {
+ return Cipher.getInstance(algorithm);
+ }
+
+ @Override
+ public KeyAgreement createKeyAgreement(String algorithm) throws GeneralSecurityException {
+ return KeyAgreement.getInstance(algorithm);
+ }
+
+ @Override
+ public KeyFactory createKeyFactory(String algorithm) throws GeneralSecurityException {
+ return KeyFactory.getInstance(algorithm);
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGenerator(String algorithm) throws GeneralSecurityException {
+ return KeyPairGenerator.getInstance(algorithm);
+ }
+
+ @Override
+ public Mac createMac(String algorithm) throws GeneralSecurityException {
+ return Mac.getInstance(algorithm);
+ }
+
+ @Override
+ public MessageDigest createMessageDigest(String algorithm) throws GeneralSecurityException {
+ return MessageDigest.getInstance(algorithm);
+ }
+
+ @Override
+ public Signature createSignature(String algorithm) throws GeneralSecurityException {
+ return Signature.getInstance(algorithm);
+ }
+
+ @Override
+ public SecureRandom createSecureRandom(String algorithm) throws GeneralSecurityException {
+ return SecureRandom.getInstance(algorithm);
+ }
+
+ @Override
+ public KEM createKEM(String algorithm) throws GeneralSecurityException {
+ return JceKEM.INSTANCE.get(algorithm);
+ }
}
}
diff --cc sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
index a2bbe89c2,525b66d01..2686b1ad5
--- 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
@@@ -22,8 -22,10 +22,9 @@@ import java.io.IOException
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Path;
+ import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
-import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@@ -697,25 -833,31 +698,32 @@@ public final class SecurityUtils
entitySelector, REGISTERED_PROVIDERS.values());
}
- try {
- return SecurityEntityFactory.toFactory(entityType, registrar, getDefaultProviderChoice());
- } catch (ReflectiveOperationException t) {
- Throwable e = ExceptionUtils.peelException(t);
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new IllegalArgumentException(e);
+ return getSecurityEntityProvider(registrar, getDefaultProviderChoice());
+ }
+
+ public static SecurityEntityFactory getSecurityEntityProvider(
+ SecurityProviderRegistrar registrar,
+ SecurityProviderChoice defaultProvider) {
+ if (registrar == null) {
+ if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
+ return SecurityEntityFactory.Default.INSTANCE;
}
+ return defaultProvider.getFactory();
}
+ return registrar.getFactory();
}
+ public static AlgorithmParameters getAlgorithmParameters(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<AlgorithmParameters> factory
++ SecurityEntityFactory factory
+ = resolveSecurityEntityFactory(AlgorithmParameters.class, algorithm,
+ r -> r.isSecurityEntitySupported(AlgorithmParameters.class, algorithm));
- return factory.getInstance(algorithm);
++ return factory.createAlgorithmParameters(algorithm);
+ }
+
public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<KeyFactory> factory
- = resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm));
- return factory.getInstance(algorithm);
+ SecurityEntityFactory factory = resolveSecurityEntityFactory(KeyFactory.class, algorithm,
+ r -> r.isKeyFactorySupported(algorithm));
+ return factory.createKeyFactory(algorithm);
}
public static Cipher getCipher(String transformation) throws GeneralSecurityException {
diff --cc sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
index e7b3454ce,a67168a61..60f818cbc
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
@@@ -23,6 -23,6 +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.PublicKey;
import java.security.interfaces.DSAPrivateKey;
@@@ -32,6 -32,6 +33,7 @@@ import java.security.interfaces.ECPubli
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
++import java.util.Base64;
import java.util.Collection;
import java.util.List;
@@@ -45,7 -45,8 +47,8 @@@ import org.apache.sshd.common.keyprovid
import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.apache.sshd.common.util.GenericUtils;
++import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.io.resource.PathResource;
-import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.MethodOrderer.MethodName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@@ -118,6 -125,6 +121,19 @@@ class SecurityUtilsTest extends Securit
}
}
++ @Test
++ void x509EncodingEc() throws Exception {
++ KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
++ generator.initialize(521);
++ KeyPair kp = generator.generateKeyPair();
++ String enc = Base64.getEncoder().encodeToString(kp.getPublic().getEncoded());
++ ByteArrayBuffer buf = new ByteArrayBuffer();
++ buf.putPublicKey(kp.getPublic());
++ PublicKey readBack = buf.getPublicKey();
++ assertTrue(KeyUtils.compareKeys(kp.getPublic(), readBack), "Public keys should be equal");
++ assertEquals(enc, Base64.getEncoder().encodeToString(readBack.getEncoded()), readBack.getClass().getCanonicalName());
++ }
++
private KeyPair testLoadECPrivateKey(String name) throws Exception {
return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
}