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 d04cc45845c301bca9f60487c71f53479e19068d Author: Thomas Wolf <tw...@apache.org> AuthorDate: Thu Apr 24 21:00:32 2025 +0200 An OpenSshCertificate is not a PrivateKey! Remove the interface. Handle this like the SK keys: specify the private key type in the decoder as PrivateKey. Remove the decoder-by-key-class methods and maps; they are not needed and, as the now removed comment pointed out, fragile. --- .../apache/sshd/common/config/keys/KeyUtils.java | 128 ++++----------------- .../common/config/keys/OpenSshCertificate.java | 3 +- .../keys/impl/OpenSSHCertificateDecoder.java | 30 ++--- .../openssh/OpenSSHKeyPairResourceParser.java | 50 +------- 4 files changed, 42 insertions(+), 169 deletions(-) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java index b53a3ea78..357635eeb 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java @@ -54,7 +54,6 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NavigableSet; @@ -147,10 +146,6 @@ public final class KeyUtils { private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - // order matters here, when, for example, SkED25519PublicKeyEntryDecoder is used, it registers the PrivateKey - // type as java.security.PrivateKey, and all PrivateKey types are assignable to this, so it must be consulted last - private static final Map<Class<?>, PublicKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP = new LinkedHashMap<>(); - private static final Map<String, String> KEY_TYPE_ALIASES = NavigableMapBuilder.<String, String> builder(String.CASE_INSENSITIVE_ORDER) .put(RSA_SHA256_KEY_TYPE_ALIAS, KeyPairProvider.SSH_RSA) @@ -355,7 +350,7 @@ public final class KeyUtils { * @return A {@link KeyPair} of the specified type and size * @throws GeneralSecurityException If failed to generate the key pair * @see #getPublicKeyEntryDecoder(String) - * @see PublicKeyEntryDecoder#generateKeyPair(int) + * @see PublicKeyEntryDecoder<?, ?>#generateKeyPair(int) */ public static KeyPair generateKeyPair(String keyType, int keySize) throws GeneralSecurityException { PublicKeyEntryDecoder<?, ?> decoder = getPublicKeyEntryDecoder(keyType); @@ -385,88 +380,33 @@ public final class KeyUtils { } /** - * @param decoder The decoder to register - * @throws IllegalArgumentException if no decoder or not key type or no supported names for the decoder - * @see PublicKeyEntryDecoder#getPublicKeyType() - * @see PublicKeyEntryDecoder#getSupportedKeyTypes() - */ - public static void registerPublicKeyEntryDecoder(PublicKeyEntryDecoder<?, ?> decoder) { - Objects.requireNonNull(decoder, "No decoder specified"); - - Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared"); - Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared"); - synchronized (BY_KEY_CLASS_DECODERS_MAP) { - BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder); - BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder); - } - - registerPublicKeyEntryDecoderKeyTypes(decoder); - } - - /** - * Registers the specified decoder for all the types it {@link PublicKeyEntryDecoder#getSupportedKeyTypes() + * Registers the specified decoder for all the types it {@link PublicKeyEntryDecoder<?, ?>#getSupportedKeyTypes() * supports} * - * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to register - * @see #registerPublicKeyEntryDecoderForKeyType(String, PublicKeyEntryDecoder) + * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder<?, ?> decoder} to register + * @see #registerPublicKeyEntryDecoderForKeyType(String, PublicKeyEntryDecoder<?, ?>) */ - public static void registerPublicKeyEntryDecoderKeyTypes(PublicKeyEntryDecoder<?, ?> decoder) { + public static void registerPublicKeyEntryDecoder(PublicKeyEntryDecoder<?, ?> decoder) { Objects.requireNonNull(decoder, "No decoder specified"); Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedKeyTypes(), "No supported key types"); - for (String n : names) { - PublicKeyEntryDecoder<?, ?> prev = registerPublicKeyEntryDecoderForKeyType(n, decoder); - if (prev != null) { - // noinspection UnnecessaryContinue - continue; // debug breakpoint - } - } - } - - /** - * @param keyType The key (never {@code null}/empty) key type - * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to register - * @return The previously registered decoder for this key type - {@code null} if none - */ - public static PublicKeyEntryDecoder<?, ?> registerPublicKeyEntryDecoderForKeyType( - String keyType, PublicKeyEntryDecoder<?, ?> decoder) { - keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified"); - Objects.requireNonNull(decoder, "No decoder specified"); - synchronized (BY_KEY_TYPE_DECODERS_MAP) { - return BY_KEY_TYPE_DECODERS_MAP.put(keyType, decoder); - } - } - - /** - * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to unregister - * @return The case <U>insensitive</U> {@link NavigableSet} of all the effectively un-registered key types - * out of all the {@link PublicKeyEntryDecoder#getSupportedKeyTypes() supported} ones. - * @see #unregisterPublicKeyEntryDecoderKeyTypes(PublicKeyEntryDecoder) - */ - public static NavigableSet<String> unregisterPublicKeyEntryDecoder(PublicKeyEntryDecoder<?, ?> decoder) { - Objects.requireNonNull(decoder, "No decoder specified"); - - Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared"); - Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared"); - synchronized (BY_KEY_CLASS_DECODERS_MAP) { - BY_KEY_CLASS_DECODERS_MAP.remove(pubType); - BY_KEY_CLASS_DECODERS_MAP.remove(prvType); + for (String keyType : names) { + BY_KEY_TYPE_DECODERS_MAP.put(keyType, decoder); + } } - - return unregisterPublicKeyEntryDecoderKeyTypes(decoder); } /** * Unregisters the specified decoder for all the types it supports * - * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to unregister + * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder<?, ?> decoder} to unregister * @return The case <U>insensitive</U> {@link NavigableSet} of all the effectively un-registered key types - * out of all the {@link PublicKeyEntryDecoder#getSupportedKeyTypes() supported} ones. + * out of all the {@link PublicKeyEntryDecoder<?, ?>#getSupportedKeyTypes() supported} ones. * @see #unregisterPublicKeyEntryDecoderForKeyType(String) */ - public static NavigableSet<String> unregisterPublicKeyEntryDecoderKeyTypes(PublicKeyEntryDecoder<?, ?> decoder) { + public static NavigableSet<String> unregisterPublicKeyEntryDecoder(PublicKeyEntryDecoder<?, ?> decoder) { Objects.requireNonNull(decoder, "No decoder specified"); Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty( @@ -495,8 +435,8 @@ public final class KeyUtils { * Unregister the decoder registered for the specified key type * * @param keyType The key (never {@code null}/empty) key type - * @return The unregistered {@link PublicKeyEntryDecoder} - {@code null} if none registered for this key - * type + * @return The unregistered {@link PublicKeyEntryDecoder<?, ?>} - {@code null} if none registered for this + * key type */ public static PublicKeyEntryDecoder<?, ?> unregisterPublicKeyEntryDecoderForKeyType(String keyType) { keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified"); @@ -509,7 +449,7 @@ public final class KeyUtils { /** * @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss} - ignored if * {@code null}/empty - * @return The registered {@link PublicKeyEntryDecoder} or {code null} if not found + * @return The registered {@link PublicKeyEntryDecoder<?, ?>} or {code null} if not found */ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(String keyType) { if (GenericUtils.isEmpty(keyType)) { @@ -523,8 +463,8 @@ public final class KeyUtils { /** * @param kp The {@link KeyPair} to examine - ignored if {@code null} - * @return The matching {@link PublicKeyEntryDecoder} provided <U>both</U> the public and private keys have the - * same decoder - {@code null} if no match found + * @return The matching {@link PublicKeyEntryDecoder<?, ?>} provided <U>both</U> the public and private keys have + * the same decoder - {@code null} if no match found * @see #getPublicKeyEntryDecoder(Key) */ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(KeyPair kp) { @@ -543,45 +483,17 @@ public final class KeyUtils { /** * @param key The {@link Key} (public or private) - ignored if {@code null} - * @return The registered {@link PublicKeyEntryDecoder} for this key or {code null} if no match found + * @return The registered {@link PublicKeyEntryDecoder<?, ?>} for this key or {code null} if no match found * @see #getPublicKeyEntryDecoder(Class) */ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(Key key) { if (key == null) { return null; } else { - return getPublicKeyEntryDecoder(key.getClass()); + return getPublicKeyEntryDecoder(KeyUtils.getKeyType(key)); } } - /** - * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key} compatible type - * @return The registered {@link PublicKeyEntryDecoder} or {code null} if no match found - */ - public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(Class<?> keyType) { - if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) { - return null; - } - - synchronized (BY_KEY_TYPE_DECODERS_MAP) { - PublicKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType); - if (decoder != null) { - return decoder; - } - - // in case it is a derived class - for (PublicKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) { - Class<?> pubType = dec.getPublicKeyType(); - Class<?> prvType = dec.getPrivateKeyType(); - if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) { - return dec; - } - } - } - - return null; - } - /** * @return The default {@link DigestFactory} by the {@link #getFingerPrint(PublicKey)} and * {@link #getFingerPrint(String)} methods @@ -837,7 +749,7 @@ public final class KeyUtils { /** * @param kp a key pair - ignored if {@code null}. If the private key is non-{@code null} then it is used to * determine the type, otherwise the public one is used. - * @return the key type or {@code null} if cannot determine it + * @return the SSH key type or {@code null} if cannot determine it * @see #getKeyType(Key) */ public static String getKeyType(KeyPair kp) { @@ -854,7 +766,7 @@ public final class KeyUtils { /** * @param key a public or private key - * @return the key type or {@code null} if cannot determine it + * @return the SSH key type or {@code null} if cannot determine it */ public static String getKeyType(Key key) { if (key == null) { diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java index f9f0cef30..ab3e230a1 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java @@ -18,7 +18,6 @@ */ package org.apache.sshd.common.config.keys; -import java.security.PrivateKey; import java.security.PublicKey; import java.time.Instant; import java.util.Arrays; @@ -37,7 +36,7 @@ import org.apache.sshd.common.util.ValidateUtils; * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> * @see <a href= "https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD">PROTOCOL.certkeys</a> */ -public interface OpenSshCertificate extends SshPublicKey, PrivateKey { +public interface OpenSshCertificate extends SshPublicKey { /** * {@link OpenSshCertificate}s have a type indicating whether the certificate if for a host key (certifying a host diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java index b6fa7a9f5..68822e7ce 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java @@ -26,7 +26,9 @@ 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.PrivateKey; import java.util.Arrays; import java.util.Collections; import java.util.Map; @@ -43,11 +45,11 @@ import org.apache.sshd.common.util.io.IoUtils; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public class OpenSSHCertificateDecoder extends AbstractPublicKeyEntryDecoder<OpenSshCertificate, OpenSshCertificate> { +public class OpenSSHCertificateDecoder extends AbstractPublicKeyEntryDecoder<OpenSshCertificate, PrivateKey> { public static final OpenSSHCertificateDecoder INSTANCE = new OpenSSHCertificateDecoder(); public OpenSSHCertificateDecoder() { - super(OpenSshCertificate.class, OpenSshCertificate.class, + super(OpenSshCertificate.class, PrivateKey.class, Collections.unmodifiableList(Arrays.asList( KeyUtils.RSA_SHA256_CERT_TYPE_ALIAS, KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS, @@ -72,9 +74,7 @@ public class OpenSSHCertificateDecoder extends AbstractPublicKeyEntryDecoder<Ope buffer.putString(keyType); buffer.putRawBytes(IoUtils.toByteArray(keyData)); buffer.getString(); // Skip the key type just prepended - OpenSshCertificate cert = OpenSSHCertPublicKeyParser.INSTANCE.getRawPublicKey(keyType, buffer); - - return cert; + return OpenSSHCertPublicKeyParser.INSTANCE.getRawPublicKey(keyType, buffer); } @Override @@ -82,8 +82,7 @@ public class OpenSSHCertificateDecoder extends AbstractPublicKeyEntryDecoder<Ope Objects.requireNonNull(key, "No public key provided"); ByteArrayBuffer buffer = new ByteArrayBuffer(); - // use Buffer.putRawPublicKey so the certificate type is prepended - buffer.putRawPublicKey(key); + buffer.putRawPublicKey(key); // prepends the certificate type s.write(buffer.getCompactData()); return key.getKeyType(); @@ -102,17 +101,22 @@ public class OpenSSHCertificateDecoder extends AbstractPublicKeyEntryDecoder<Ope } @Override - public OpenSshCertificate clonePrivateKey(OpenSshCertificate key) throws GeneralSecurityException { - return clonePublicKey(key); + public PrivateKey clonePrivateKey(PrivateKey key) { + throw new UnsupportedOperationException("Private key operations are not supported for certificates."); + } + + @Override + public KeyFactory getKeyFactoryInstance() { + throw new UnsupportedOperationException("Private key operations are not supported for certificates."); } @Override - public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException { - return null; + public KeyPair generateKeyPair(int keySize) { + throw new UnsupportedOperationException("Private key operations are not supported for certificates."); } @Override - public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException { - return null; + public KeyPairGenerator getKeyPairGenerator() { + throw new UnsupportedOperationException("Private key operations are not supported for certificates."); } } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java index 3fd1c1d5a..a28989375 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java @@ -36,7 +36,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -83,8 +82,6 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser private static final Map<String, PrivateKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - private static final Map<Class<?>, PrivateKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP = new HashMap<>(); - static { registerPrivateKeyEntryDecoder(OpenSSHRSAPrivateKeyDecoder.INSTANCE); registerPrivateKeyEntryDecoder(OpenSSHDSSPrivateKeyEntryDecoder.INSTANCE); @@ -279,7 +276,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser if (traceEnabled) { log.trace("extractKeyPairs({}) add private key #{}: {} {}", - resourceKey, keyIndex, prvType, prvData.getValue()); + resourceKey, keyIndex, prvType, prvData == null ? "" : prvData.getValue()); } keyPairs.add(new KeyPair(pubKey, prvKey)); } @@ -345,22 +342,11 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser public static void registerPrivateKeyEntryDecoder(PrivateKeyEntryDecoder<?, ?> decoder) { Objects.requireNonNull(decoder, "No decoder specified"); - Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared"); - Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared"); - synchronized (BY_KEY_CLASS_DECODERS_MAP) { - BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder); - BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder); - } - Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedKeyTypes(), "No supported key type"); synchronized (BY_KEY_TYPE_DECODERS_MAP) { - for (String n : names) { - PrivateKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder); - if (prev != null) { - // noinspection UnnecessaryContinue - continue; // debug breakpoint - } + for (String keyType : names) { + BY_KEY_TYPE_DECODERS_MAP.put(keyType, decoder); } } } @@ -409,35 +395,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser if (key == null) { return null; } else { - return getPrivateKeyEntryDecoder(key.getClass()); - } - } - - /** - * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key} compatible type - * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if no match found - */ - public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Class<?> keyType) { - if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) { - return null; - } - - synchronized (BY_KEY_TYPE_DECODERS_MAP) { - PrivateKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType); - if (decoder != null) { - return decoder; - } - - // in case it is a derived class - for (PrivateKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) { - Class<?> pubType = dec.getPublicKeyType(); - Class<?> prvType = dec.getPrivateKeyType(); - if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) { - return dec; - } - } + return getPrivateKeyEntryDecoder(KeyUtils.getKeyType(key)); } - - return null; } }