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 <[email protected]> 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:[email protected]">Apache MINA SSHD Project</a> + * @author <a href="mailto:[email protected]">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); }
