This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 059ae39f91b0310f63218139e6d7bfd595d9c06d Author: Lyor Goldstein <[email protected]> AuthorDate: Sun Feb 17 15:13:54 2019 +0200 [SSHD-895] Added RSA SHA-256 and SHA-512 signature support --- .../sshd/common/config/keys/IdentityUtils.java | 3 +- .../apache/sshd/common/config/keys/KeyUtils.java | 105 ++++++++++++++++++++- .../config/keys/impl/RSAPublicKeyDecoder.java | 30 +++--- .../sshd/common/signature/BuiltinSignatures.java | 36 ++++++- .../apache/sshd/common/signature/SignatureRSA.java | 22 +++-- .../sshd/common/signature/SignatureRSASHA1.java | 31 ++++++ .../sshd/common/signature/SignatureRSASHA256.java | 31 ++++++ .../sshd/common/signature/SignatureRSASHA512.java | 31 ++++++ .../keys/LazyClientIdentityIteratorTest.java | 3 +- .../sshd/common/config/keys/KeyUtilsTest.java | 8 ++ .../common/signature/RSASignatureVariantsTest.java | 67 +++++++++++++ ...atureRSATest.java => SignatureRSASHA1Test.java} | 15 +-- .../signature/SignatureVariantTestSupport.java | 80 ++++++++++++++++ .../org/apache/sshd/common/signature/id_dsa | 12 +++ .../org/apache/sshd/common/signature/id_ecdsa | 5 + .../org/apache/sshd/common/signature/id_rsa | 27 ++++++ 16 files changed, 472 insertions(+), 34 deletions(-) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java index 6b15070..178c914 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java @@ -77,7 +77,8 @@ public final class IdentityUtils { return null; } else { return GenericUtils.trimToEmpty(prefix) - + type.toLowerCase() + GenericUtils.trimToEmpty(suffix); + + type.toLowerCase() + + GenericUtils.trimToEmpty(suffix); } } 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 f788a4c..7ced362 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 @@ -48,12 +48,14 @@ import java.security.spec.ECParameterSpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.NavigableSet; import java.util.Objects; @@ -73,6 +75,7 @@ import org.apache.sshd.common.digest.DigestFactory; import org.apache.sshd.common.digest.DigestUtils; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.MapEntryUtils.NavigableMapBuilder; import org.apache.sshd.common.util.OsUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; @@ -130,6 +133,10 @@ public final class KeyUtils { */ public static final DigestFactory DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256; + /** @see <A HREF="">https://tools.ietf.org/html/rfc8332#section-3</A> */ + public static final String RSA_SHA256_KEY_TYPE_ALIAS = "rsa-sha2-256"; + public static final String RSA_SHA512_KEY_TYPE_ALIAS = "rsa-sha2-512"; + private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER = new AtomicReference<>(); private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP = @@ -138,6 +145,12 @@ public final class KeyUtils { private static final Map<Class<?>, PublicKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP = new HashMap<>(); + 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) + .put(RSA_SHA512_KEY_TYPE_ALIAS, KeyPairProvider.SSH_RSA) + .build(); + static { registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE); registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE); @@ -614,7 +627,7 @@ public final class KeyUtils { /** * @param f The {@link Factory} to create the {@link Digest} to use * @param s The {@link String} to digest - ignored if {@code null}/empty, - * otherwise its UTF-8 representation is used as input for the fingerprint + * otherwise its UTF-8 representation is used as input for the fingerprint * @return The fingerprint - {@code null} if {@code null}/empty input. * <B>Note:</B> if exception encountered then returns the exception's simple class name * @see #getFingerPrint(Digest, String, Charset) @@ -627,7 +640,7 @@ public final class KeyUtils { * @param f The {@link Factory} to create the {@link Digest} to use * @param s The {@link String} to digest - ignored if {@code null}/empty * @param charset The {@link Charset} to use in order to convert the - * string to its byte representation to use as input for the fingerprint + * string to its byte representation to use as input for the fingerprint * @return The fingerprint - {@code null} if {@code null}/empty input * <B>Note:</B> if exception encountered then returns the exception's simple class name * @see DigestUtils#getFingerPrint(Digest, String, Charset) @@ -652,7 +665,7 @@ public final class KeyUtils { * @param d The {@link Digest} to use to calculate the fingerprint * @param s The string to digest - ignored if {@code null}/empty * @param charset The {@link Charset} to use in order to convert the - * string to its byte representation to use as input for the fingerprint + * string to its byte representation to use as input for the fingerprint * @return The fingerprint - {@code null} if {@code null}/empty input. * <B>Note:</B> if exception encountered then returns the exception's simple class name * @see DigestUtils#getFingerPrint(Digest, String, Charset) @@ -742,8 +755,8 @@ 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. + * 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 * @see #getKeyType(Key) */ @@ -787,6 +800,88 @@ public final class KeyUtils { } /** + * @param keyType The available key-type - ignored if {@code null}/empty + * @return The canonical key type - same as input if no alias registered + * for the provided key type + * @see #RSA_SHA256_KEY_TYPE_ALIAS + * @see #RSA_SHA512_KEY_TYPE_ALIAS + */ + public static String getCanonicalKeyType(String keyType) { + if (GenericUtils.isEmpty(keyType)) { + return keyType; + } + + String canonicalName; + synchronized (KEY_TYPE_ALIASES) { + canonicalName = KEY_TYPE_ALIASES.get(keyType); + } + + if (GenericUtils.isEmpty(canonicalName)) { + return keyType; + } + + return canonicalName; + } + + /** + * @return A case insensitive {@link NavigableSet} of the currently registered + * key type "aliases". + * @see #getCanonicalKeyType(String) + */ + public static NavigableSet<String> getRegisteredKeyTypeAliases() { + synchronized (KEY_TYPE_ALIASES) { + return KEY_TYPE_ALIASES.isEmpty() + ? Collections.emptyNavigableSet() + : GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, KEY_TYPE_ALIASES.keySet()); + } + } + + /** + * Registers a collection of aliases to a canonical key type + * + * @param keyType The (never {@code null}/empty) canonical name + * @param aliases The (never {@code null}/empty) aliases + * @return A {@link List} of the replaced aliases - empty + * if no previous aliases for the canonical name + */ + public static List<String> registerCanonicalKeyTypes(String keyType, Collection<String> aliases) { + ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type value"); + ValidateUtils.checkNotNullAndNotEmpty(aliases, "No aliases provided"); + + List<String> replaced = Collections.emptyList(); + synchronized (KEY_TYPE_ALIASES) { + for (String a : aliases) { + ValidateUtils.checkNotNullAndNotEmpty(a, "Null/empty alias registration for %s", keyType); + String prev = KEY_TYPE_ALIASES.put(a, keyType); + if (GenericUtils.isEmpty(prev)) { + continue; + } + + if (replaced.isEmpty()) { + replaced = new ArrayList<>(); + } + replaced.add(prev); + } + } + + return replaced; + } + + /** + * @param alias The alias to unregister (ignored if {@code null}/empty) + * @return The associated canonical key type - {@code null} if alias not registered + */ + public static String unregisterCanonicalKeyTypeAlias(String alias) { + if (GenericUtils.isEmpty(alias)) { + return alias; + } + + synchronized (KEY_TYPE_ALIASES) { + return KEY_TYPE_ALIASES.remove(alias); + } + } + + /** * Determines the key size in bits * * @param key The {@link Key} to examine - ignored if {@code null} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java index 52cabce..9c2730d 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java @@ -33,6 +33,7 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; +import java.util.Arrays; import java.util.Collections; import java.util.Objects; @@ -49,13 +50,20 @@ public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublic public static final RSAPublicKeyDecoder INSTANCE = new RSAPublicKeyDecoder(); public RSAPublicKeyDecoder() { - super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA))); + super(RSAPublicKey.class, RSAPrivateKey.class, + Collections.unmodifiableList( + Arrays.asList(KeyPairProvider.SSH_RSA, + // Not really required, but allow it + KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS, + KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS))); } @Override public RSAPublicKey decodePublicKey(SessionContext session, String keyType, InputStream keyData) throws IOException, GeneralSecurityException { - if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly + // Not really required, but allow it + String canonicalName = KeyUtils.getCanonicalKeyType(keyType); + if (!KeyPairProvider.SSH_RSA.equals(canonicalName)) { // just in case we were invoked directly throw new InvalidKeySpecException("Unexpected key type: " + keyType); } @@ -96,15 +104,15 @@ public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublic RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key; return generatePrivateKey( - new RSAPrivateCrtKeySpec( - rsaPrv.getModulus(), - rsaPrv.getPublicExponent(), - rsaPrv.getPrivateExponent(), - rsaPrv.getPrimeP(), - rsaPrv.getPrimeQ(), - rsaPrv.getPrimeExponentP(), - rsaPrv.getPrimeExponentQ(), - rsaPrv.getCrtCoefficient())); + new RSAPrivateCrtKeySpec( + rsaPrv.getModulus(), + rsaPrv.getPublicExponent(), + rsaPrv.getPrivateExponent(), + rsaPrv.getPrimeP(), + rsaPrv.getPrimeQ(), + rsaPrv.getPrimeExponentP(), + rsaPrv.getPrimeExponentQ(), + rsaPrv.getCrtCoefficient())); } @Override diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java index e9cd416..a0856b0 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java @@ -31,11 +31,13 @@ import java.util.NavigableSet; import java.util.Objects; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicReference; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.cipher.ECCurves; import org.apache.sshd.common.config.NamedFactoriesListParseResult; +import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; @@ -56,7 +58,39 @@ public enum BuiltinSignatures implements SignatureFactory { rsa(KeyPairProvider.SSH_RSA) { @Override public Signature create() { - return new SignatureRSA(); + return new SignatureRSASHA1(); + } + }, + rsaSHA256(KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS) { + @Override + public Signature create() { + return new SignatureRSASHA256(); + } + }, + rsaSHA512(KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS) { + private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(); + + @Override + public Signature create() { + return new SignatureRSASHA512(); + } + + @Override + public boolean isSupported() { + Boolean supported = supportHolder.get(); + if (supported == null) { + try { + java.security.Signature sig = + SecurityUtils.getSignature(SignatureRSASHA512.ALGORITHM); + supported = sig != null; + } catch (Exception e) { + supported = Boolean.FALSE; + } + + supportHolder.set(supported); + } + + return supported; } }, nistp256(KeyPairProvider.ECDSA_SHA2_NISTP256) { diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java index ad1d37e..23cc46c 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java @@ -23,6 +23,7 @@ import java.security.PublicKey; import java.security.interfaces.RSAKey; import java.util.Map; +import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.util.ValidateUtils; @@ -32,15 +33,9 @@ import org.apache.sshd.common.util.ValidateUtils; * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A> */ -public class SignatureRSA extends AbstractSignature { - public static final String DEFAULT_ALGORITHM = "SHA1withRSA"; - +public abstract class SignatureRSA extends AbstractSignature { private int verifierSignatureSize = -1; - public SignatureRSA() { - super(DEFAULT_ALGORITHM); - } - protected SignatureRSA(String algorithm) { super(algorithm); } @@ -71,7 +66,18 @@ public class SignatureRSA extends AbstractSignature { Map.Entry<String, byte[]> encoding = extractEncodedSignature(data); if (encoding != null) { String keyType = encoding.getKey(); - ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(keyType), "Mismatched key type: %s", keyType); + /* + * According to https://tools.ietf.org/html/rfc8332#section-3.2: + * + * OpenSSH 7.2 (but not 7.2p2) incorrectly encodes the algorithm in the + * signature as "ssh-rsa" when the algorithm in SSH_MSG_USERAUTH_REQUEST + * is "rsa-sha2-256" or "rsa-sha2-512". In this case, the signature + * does actually use either SHA-256 or SHA-512. A server MAY, but is + * not required to, accept this variant or another variant that + * corresponds to a good-faith implementation and is considered safe to accept. + */ + String canonicalName = KeyUtils.getCanonicalKeyType(keyType); + ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(canonicalName), "Mismatched key type: %s", keyType); data = encoding.getValue(); } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA1.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA1.java new file mode 100644 index 0000000..a20278f --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA1.java @@ -0,0 +1,31 @@ +/* + * 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.signature; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public class SignatureRSASHA1 extends SignatureRSA { + public static final String ALGORITHM = "SHA1withRSA"; + + public SignatureRSASHA1() { + super(ALGORITHM); + } +} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA256.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA256.java new file mode 100644 index 0000000..fae2354 --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA256.java @@ -0,0 +1,31 @@ +/* + * 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.signature; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public class SignatureRSASHA256 extends SignatureRSA { + public static final String ALGORITHM = "SHA256withRSA"; + + public SignatureRSASHA256() { + super(ALGORITHM); + } +} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA512.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA512.java new file mode 100644 index 0000000..eff5b0f --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSASHA512.java @@ -0,0 +1,31 @@ +/* + * 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.signature; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public class SignatureRSASHA512 extends SignatureRSA { + public static final String ALGORITHM = "SHA512withRSA"; + + public SignatureRSASHA512() { + super(ALGORITHM); + } +} diff --git a/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java similarity index 97% rename from sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java rename to sshd-common/src/test/java/org/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java index 8e072c1..86a3b96 100644 --- a/sshd-common/src/test/java/rg/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/LazyClientIdentityIteratorTest.java @@ -17,7 +17,7 @@ * under the License. */ -package rg.apache.sshd.client.config.keys; +package org.apache.sshd.client.config.keys; import java.security.KeyPair; import java.security.PrivateKey; @@ -27,7 +27,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -import org.apache.sshd.client.config.keys.ClientIdentityProvider; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.util.test.JUnitTestSupport; import org.junit.FixMethodOrder; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java index 00b273f..b62057d 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java @@ -34,6 +34,7 @@ import org.apache.sshd.common.digest.BaseDigest; import org.apache.sshd.common.digest.BuiltinDigests; import org.apache.sshd.common.digest.Digest; import org.apache.sshd.common.digest.DigestFactory; +import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.OsUtils; import org.apache.sshd.common.util.io.IoUtils; @@ -154,4 +155,11 @@ public class KeyUtilsTest extends JUnitTestSupport { assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file)); } } + + @Test // see SSHD-895 + public void testRSAKeyTypeAliases() { + for (String alias : new String[] {KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS, KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS}) { + assertEquals("Mismatched canonical name for " + alias, KeyPairProvider.SSH_RSA, KeyUtils.getCanonicalKeyType(alias)); + } + } } diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/RSASignatureVariantsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/RSASignatureVariantsTest.java new file mode 100644 index 0000000..806be61 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/RSASignatureVariantsTest.java @@ -0,0 +1,67 @@ +/* + * 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.signature; + +import java.security.KeyPair; +import java.util.Arrays; +import java.util.List; + +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; + +/** + * NOTE: some tests are inherited from parent + * + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) +public class RSASignatureVariantsTest extends SignatureVariantTestSupport { + private static KeyPair kp; + + public RSASignatureVariantsTest(SignatureFactory factory) { + super(factory, kp); + } + + @BeforeClass + public static void initializeSigningKeyPair() throws Exception { + kp = initializeSigningKeyPair(KeyUtils.RSA_ALGORITHM); + } + + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + return parameterize( + Arrays.asList( + BuiltinSignatures.rsa, + BuiltinSignatures.rsaSHA256, + BuiltinSignatures.rsaSHA512)); + } +} diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSASHA1Test.java similarity index 80% rename from sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java rename to sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSASHA1Test.java index 5671ac3..38d9168 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSASHA1Test.java @@ -31,26 +31,29 @@ import org.apache.sshd.common.Factory; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runners.MethodSorters; /** * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class SignatureRSATest extends JUnitTestSupport { +@Category({ NoIoTestCase.class }) +public class SignatureRSASHA1Test extends JUnitTestSupport { private static final Base64.Decoder B64_DECODER = Base64.getDecoder(); @SuppressWarnings("checkstyle:linelength") private static final byte[] TEST_MSG = - B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t [...] + B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSx [...] @SuppressWarnings("checkstyle:linelength") private static final byte[] TEST_SIGNATURE = - B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+"); + B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+"); private static PublicKey testKey; - public SignatureRSATest() { + public SignatureRSASHA1Test() { super(); } @@ -68,7 +71,7 @@ public class SignatureRSATest extends JUnitTestSupport { testLeadingZeroes(new Factory<SignatureRSA>() { @Override public SignatureRSA create() { - return new SignatureRSA() { + return new SignatureRSASHA1() { @Override protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException { assertFalse("Signature not initialized for verification", forSigning); @@ -87,7 +90,7 @@ public class SignatureRSATest extends JUnitTestSupport { @Test // see SSHD-642 public void testLeadingZeroesJCE() throws Throwable { - testLeadingZeroes(() -> new SignatureRSA() { + testLeadingZeroes(() -> new SignatureRSASHA1() { @Override protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException { assertFalse("Signature not initialized for verification", forSigning); diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureVariantTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureVariantTestSupport.java new file mode 100644 index 0000000..ede85e8 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureVariantTestSupport.java @@ -0,0 +1,80 @@ +/* + * 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.signature; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.Objects; + +import org.apache.sshd.client.config.keys.ClientIdentity; +import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.junit.Assume; +import org.junit.Test; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public abstract class SignatureVariantTestSupport extends JUnitTestSupport { + protected final SignatureFactory factory; + protected final KeyPair kp; + + protected SignatureVariantTestSupport(SignatureFactory factory, KeyPair kp) { + this.factory = Objects.requireNonNull(factory, "No factory provided"); + this.kp = Objects.requireNonNull(kp, "No key pair provided"); + Assume.assumeTrue("Unsupported factory: " + factory, factory.isSupported()); + } + + protected static KeyPair initializeSigningKeyPair(String algorithm) throws IOException, GeneralSecurityException { + String resourceKey = ClientIdentity.getIdentityFileName(algorithm); + URL urlKeyPair = SignatureVariantTestSupport.class.getResource(resourceKey); + assertNotNull("Missing key-pair resource: " + resourceKey, urlKeyPair); + try (InputStream stream = urlKeyPair.openStream()) { + Iterable<KeyPair> ids = + SecurityUtils.loadKeyPairIdentities(null, NamedResource.ofName(resourceKey), stream, null); + return GenericUtils.head(ids); + } + } + + @Test + public void testSignature() throws Exception { + byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8); + Signature signer = factory.create(); + signer.initSigner(kp.getPrivate()); + signer.update(data); + + byte[] signature = signer.sign(); + Signature verifier = factory.create(); + verifier.initVerifier(kp.getPublic()); + verifier.update(data); + verifier.verify(signature); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + factory + "]"; + } +} diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_dsa b/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_dsa new file mode 100644 index 0000000..1d3ef24 --- /dev/null +++ b/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX +oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf +2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr +YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl +BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo +NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew +CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab +YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa +XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG +nmVCAbzo9YsbdJWBnxMnBA== +-----END DSA PRIVATE KEY----- \ No newline at end of file diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_ecdsa b/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_ecdsa new file mode 100644 index 0000000..31b1268 --- /dev/null +++ b/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_ecdsa @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49 +AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF +DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA== +-----END EC PRIVATE KEY----- diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_rsa b/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_rsa new file mode 100644 index 0000000..afc6aa8 --- /dev/null +++ b/sshd-common/src/test/resources/org/apache/sshd/common/signature/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c +tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB +gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE +aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl +5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh +V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj +xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH +prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY +9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7 +eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny +1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a +7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP +r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f +1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp +wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR +c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz +Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS +bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC +raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo +8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0 +evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa +OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm +fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX +AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3 +FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg== +-----END RSA PRIVATE KEY-----
