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 0de75cef20317326501f2ea54593b6e766eaa2f5
Author: Lyor Goldstein <lgoldst...@apache.org>
AuthorDate: Fri Nov 20 09:47:47 2020 +0200

    [SSHD-1104] Take into account possible key type aliases when using public 
key authentication
---
 CHANGES.md                                         |  2 +-
 .../java/org/apache/sshd/common/NamedResource.java | 13 ++++++-
 .../common/signature/SignatureFactoriesHolder.java | 44 ++++++++++++++++++++++
 .../signature/SignatureFactoriesManager.java       | 16 +-------
 .../sshd/common/signature/SignatureFactory.java    | 14 +++++++
 .../sshd/client/auth/pubkey/KeyPairIdentity.java   | 34 ++++++++++-------
 .../sshd/client/auth/pubkey/UserAuthPublicKey.java | 40 ++++++++++++++++----
 7 files changed, 126 insertions(+), 37 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 0c8196a..4375d17 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -61,4 +61,4 @@ or `-key-file` command line option.
 * [SSHD-1070](https://issues.apache.org/jira/browse/SSHD-1070) 
OutOfMemoryError when use async port forwarding
 * [SSHD-1100](https://issues.apache.org/jira/browse/SSHD-1100) Updated used 
moduli for DH group KEX
 * [SSHD-1102](https://issues.apache.org/jira/browse/SSHD-1102) Provide filter 
support for SftpDirectoryStream
-
+* [SSHD-1104](https://issues.apache.org/jira/browse/SSHD-1104) Take into 
account possible key type aliases when using public key authentication
\ No newline at end of file
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java 
b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
index 256ecfd..b124579 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -95,7 +95,7 @@ public interface NamedResource {
      */
     static <R extends NamedResource> R findByName(
             String name, Comparator<? super String> c, Collection<? extends R> 
resources) {
-        return GenericUtils.isEmpty(name)
+        return (GenericUtils.isEmpty(name) || GenericUtils.isEmpty(resources))
                 ? null
                 : GenericUtils.stream(resources)
                         .filter(r -> c.compare(name, r.getName()) == 0)
@@ -103,6 +103,17 @@ public interface NamedResource {
                         .orElse(null);
     }
 
+    static <R extends NamedResource> R findFirstMatchByName(
+            Collection<String> names, Comparator<? super String> c, 
Collection<? extends R> resources) {
+        return (GenericUtils.isEmpty(names) || GenericUtils.isEmpty(resources))
+                ? null
+                : GenericUtils.stream(resources)
+                        .filter(r -> GenericUtils.findFirstMatchingMember(n -> 
c.compare(n, r.getName()) == 0, names) != null)
+                        .findFirst()
+                        .orElse(null);
+
+    }
+
     /**
      * Wraps a name value inside a {@link NamedResource}
      *
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesHolder.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesHolder.java
new file mode 100644
index 0000000..eeb4a5a
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesHolder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.util.List;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface SignatureFactoriesHolder {
+    /**
+     * @return The list of named <code>Signature</code> factories
+     */
+    List<NamedFactory<Signature>> getSignatureFactories();
+
+    default String getSignatureFactoriesNameList() {
+        return NamedResource.getNames(getSignatureFactories());
+    }
+
+    default List<String> getSignatureFactoriesNames() {
+        return NamedResource.getNameList(getSignatureFactories());
+    }
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
index 6ef4352..86b8cbd 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
@@ -25,7 +25,6 @@ import java.util.Collections;
 import java.util.List;
 
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -34,20 +33,7 @@ import org.apache.sshd.common.util.ValidateUtils;
  *
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SignatureFactoriesManager {
-    /**
-     * @return The list of named <code>Signature</code> factories
-     */
-    List<NamedFactory<Signature>> getSignatureFactories();
-
-    default String getSignatureFactoriesNameList() {
-        return NamedResource.getNames(getSignatureFactories());
-    }
-
-    default List<String> getSignatureFactoriesNames() {
-        return NamedResource.getNameList(getSignatureFactories());
-    }
-
+public interface SignatureFactoriesManager extends SignatureFactoriesHolder {
     void setSignatureFactories(List<NamedFactory<Signature>> factories);
 
     default void setSignatureFactoriesNameList(String names) {
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
index 8abfb79..e3dcf4f 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -187,4 +187,18 @@ public interface SignatureFactory extends 
BuiltinFactory<Signature> {
 
         return posValue;
     }
+
+    static NamedFactory<? extends Signature> resolveSignatureFactory(
+            String keyType, Collection<? extends NamedFactory<? extends 
Signature>> factories) {
+        if (GenericUtils.isEmpty(keyType) || GenericUtils.isEmpty(factories)) {
+            return null;
+        }
+
+        Collection<String> aliases = 
KeyUtils.getAllEquivalentKeyTypes(keyType);
+        if (GenericUtils.isEmpty(aliases)) {
+            return NamedResource.findByName(keyType, 
String.CASE_INSENSITIVE_ORDER, factories);
+        } else {
+            return NamedResource.findFirstMatchByName(aliases, 
String.CASE_INSENSITIVE_ORDER, factories);
+        }
+    }
 }
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
index be38e41..65812e1 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
@@ -20,15 +20,17 @@ package org.apache.sshd.client.auth.pubkey;
 
 import java.security.KeyPair;
 import java.security.PublicKey;
-import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesHolder;
 import org.apache.sshd.common.signature.SignatureFactoriesManager;
+import org.apache.sshd.common.signature.SignatureFactory;
 import org.apache.sshd.common.util.ValidateUtils;
 
 /**
@@ -36,14 +38,15 @@ import org.apache.sshd.common.util.ValidateUtils;
  *
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public class KeyPairIdentity implements PublicKeyIdentity {
-    private final KeyPair pair;
-    private final Collection<NamedFactory<Signature>> signatureFactories;
+public class KeyPairIdentity implements PublicKeyIdentity, 
SignatureFactoriesHolder {
+    protected final KeyPair pair;
+    private final List<NamedFactory<Signature>> signatureFactories;
 
     public KeyPairIdentity(SignatureFactoriesManager primary, 
SignatureFactoriesManager secondary, KeyPair pair) {
-        this.signatureFactories = ValidateUtils.checkNotNullAndNotEmpty(
-                SignatureFactoriesManager.resolveSignatureFactories(primary, 
secondary),
-                "No available signature factories");
+        this.signatureFactories = Collections.unmodifiableList(
+                ValidateUtils.checkNotNullAndNotEmpty(
+                        
SignatureFactoriesManager.resolveSignatureFactories(primary, secondary),
+                        "No available signature factories"));
         this.pair = Objects.requireNonNull(pair, "No key pair");
     }
 
@@ -53,12 +56,17 @@ public class KeyPairIdentity implements PublicKeyIdentity {
     }
 
     @Override
+    public List<NamedFactory<Signature>> getSignatureFactories() {
+        return signatureFactories;
+    }
+
+    @Override
     public byte[] sign(SessionContext session, byte[] data) throws Exception {
         String keyType = KeyUtils.getKeyType(getPublicKey());
-        Signature verifier = ValidateUtils.checkNotNull(
-                NamedFactory.create(signatureFactories, keyType),
-                "No signer could be located for key type=%s",
-                keyType);
+        // SSHD-1104 check if the key type is aliased
+        NamedFactory<? extends Signature> factory = 
SignatureFactory.resolveSignatureFactory(keyType, getSignatureFactories());
+        Signature verifier = (factory == null) ? null : factory.create();
+        ValidateUtils.checkNotNull(verifier, "No signer could be located for 
key type=%s", keyType);
         verifier.initSigner(session, pair.getPrivate());
         verifier.update(session, data);
         return verifier.sign(session);
@@ -69,7 +77,7 @@ public class KeyPairIdentity implements PublicKeyIdentity {
         PublicKey pubKey = getPublicKey();
         return getClass().getSimpleName()
                + " type=" + KeyUtils.getKeyType(pubKey)
-               + ", factories=" + NamedResource.getNames(signatureFactories)
+               + ", factories=" + getSignatureFactoriesNameList()
                + ", fingerprint=" + KeyUtils.getFingerPrint(pubKey);
     }
 }
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
index 26d7b65..01143e6 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
@@ -22,6 +22,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
@@ -32,7 +33,9 @@ import org.apache.sshd.common.RuntimeSshException;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesHolder;
 import org.apache.sshd.common.signature.SignatureFactoriesManager;
+import org.apache.sshd.common.signature.SignatureFactory;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -40,7 +43,7 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 
 /**
  * Implements the &quot;publickey&quot; authentication mechanism
- * 
+ *
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
 public class UserAuthPublicKey extends AbstractUserAuth implements 
SignatureFactoriesManager {
@@ -115,7 +118,17 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
             throw new RuntimeSshException(e);
         }
 
-        String algo = KeyUtils.getKeyType(key);
+        String keyType = KeyUtils.getKeyType(key);
+        NamedFactory<? extends Signature> factory;
+        // SSHD-1104 check if the key type is aliased
+        if (current instanceof SignatureFactoriesHolder) {
+            factory = SignatureFactory.resolveSignatureFactory(
+                    keyType, ((SignatureFactoriesHolder) 
current).getSignatureFactories());
+        } else {
+            factory = SignatureFactory.resolveSignatureFactory(keyType, 
getSignatureFactories());
+        }
+
+        String algo = (factory == null) ? keyType : factory.getName();
         String name = getName();
         if (debugEnabled) {
             log.debug("sendAuthDataRequest({})[{}] send 
SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}",
@@ -156,12 +169,25 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
             throw new RuntimeSshException(e);
         }
 
-        String algo = KeyUtils.getKeyType(key);
+        String curKeyType = KeyUtils.getKeyType(key);
         String rspKeyType = buffer.getString();
-        if (!rspKeyType.equals(algo)) {
-            throw new InvalidKeySpecException(
-                    "processAuthDataRequest(" + session + ")[" + service + 
"][" + name + "]"
-                                              + " mismatched key types: 
expected=" + algo + ", actual=" + rspKeyType);
+        Collection<String> aliases = 
KeyUtils.getAllEquivalentKeyTypes(curKeyType);
+        String algo;
+        // SSHD-1104 see if key aliases used
+        if (GenericUtils.isEmpty(aliases)) {
+            algo = curKeyType;
+            if (!rspKeyType.equals(algo)) {
+                throw new InvalidKeySpecException(
+                        "processAuthDataRequest(" + session + ")[" + service + 
"][" + name + "]"
+                                                  + " mismatched key types: 
expected=" + algo + ", actual=" + rspKeyType);
+            }
+        } else {
+            if (GenericUtils.findFirstMatchingMember(n -> 
n.equalsIgnoreCase(rspKeyType), aliases) == null) {
+                throw new InvalidKeySpecException(
+                        "processAuthDataRequest(" + session + ")[" + service + 
"][" + name + "]"
+                                                  + " unsupported key type: 
expected=" + aliases + ", actual=" + rspKeyType);
+            }
+            algo = rspKeyType;
         }
 
         PublicKey rspKey = buffer.getPublicKey();

Reply via email to