All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sshd.common.signature.SignatureFactory Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * 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.PublicKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.sshd.common.BuiltinFactory;
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.keyprovider.KeyPairProvider;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.MapEntryUtils;
import org.apache.sshd.common.util.security.SecurityUtils;

/**
 * @author Apache MINA SSHD Project
 */
public interface SignatureFactory extends BuiltinFactory {
    /**
     * ECC signature types in ascending order of preference (i.e., most preferred 1st)
     */
    List ECC_SIGNATURE_TYPE_PREFERENCES = Collections.unmodifiableList(
            Arrays.asList(
                    KeyPairProvider.ECDSA_SHA2_NISTP521,
                    KeyPairProvider.ECDSA_SHA2_NISTP384,
                    KeyPairProvider.ECDSA_SHA2_NISTP256));

    /**
     * RSA signature types in ascending order of preference (i.e., most preferred 1st)
     */
    List RSA_SIGNATURE_TYPE_PREFERENCES = Collections.unmodifiableList(
            Arrays.asList(
                    KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS,
                    KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS,
                    KeyPairProvider.SSH_RSA));

    /**
     * @param  provided  The provided signature key types
     * @param  factories The available signature factories
     * @return           A {@link List} of the matching available factories names that are also listed as provided ones
     *                   - in the same order of preference as they appear in the available listing. May be empty
     *                   if no provided signature key types, or no available ones or no match found.
     * @see              #resolveSignatureFactoryNamesProposal(Iterable, Collection)
     */
    static List resolveSignatureFactoriesProposal(
            Iterable provided, Collection> factories) {
        return resolveSignatureFactoryNamesProposal(provided, NamedResource.getNameList(factories));
    }

    /**
     * @param  provided  The provided signature key types
     * @param  available The available signature factories names
     * @return           A {@link List} of the matching available factories names that are also listed as provided ones
     *                   - in the same order of preference as they appear in the available listing. May be empty
     *                   if no provided signature key types, or no available ones or no match found.
     */
    static List resolveSignatureFactoryNamesProposal(
            Iterable provided, Collection available) {
        if ((provided == null) || GenericUtils.isEmpty(available)) {
            return Collections.emptyList();
        }

        Set providedKeys = new HashSet<>();
        for (String providedType : provided) {
            Collection equivTypes = KeyUtils.getAllEquivalentKeyTypes(providedType);
            providedKeys.addAll(equivTypes);
        }

        if (GenericUtils.isEmpty(providedKeys)) {
            return Collections.emptyList();
        }

        // We want to preserve the original available order as it indicates the preference
        List supported = new ArrayList<>(available);
        for (int index = 0; index < supported.size(); index++) {
            String kt = supported.get(index);
            if (!providedKeys.contains(kt)) {
                supported.remove(index);
                index--; // compensate for auto-increment
            }
        }

        return supported;
    }

    // returns -1 or > size() if append to end
    static int resolvePreferredSignaturePosition(
            List> factories, NamedFactory factory) {
        if (GenericUtils.isEmpty(factories)) {
            return -1; // just add it to the end
        }

        String name = factory.getName();
        if (KeyPairProvider.SSH_RSA.equalsIgnoreCase(name)) {
            return -1;
        }

        int pos = RSA_SIGNATURE_TYPE_PREFERENCES.indexOf(name);
        if (pos >= 0) {
            Map posMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
            for (int index = 0, count = factories.size(); index < count; index++) {
                NamedFactory f = factories.get(index);
                String keyType = f.getName();
                String canonicalName = KeyUtils.getCanonicalKeyType(keyType);
                if (!KeyPairProvider.SSH_RSA.equalsIgnoreCase(canonicalName)) {
                    continue; // debug breakpoint
                }

                posMap.put(keyType, index);
            }

            return resolvePreferredSignaturePosition(RSA_SIGNATURE_TYPE_PREFERENCES, pos, posMap);
        }

        pos = ECC_SIGNATURE_TYPE_PREFERENCES.indexOf(name);
        if (pos >= 0) {
            Map posMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
            for (int index = 0, count = factories.size(); index < count; index++) {
                NamedFactory f = factories.get(index);
                String keyType = f.getName();
                if (!ECC_SIGNATURE_TYPE_PREFERENCES.contains(keyType)) {
                    continue; // debug breakpoint
                }

                posMap.put(keyType, index);
            }

            return resolvePreferredSignaturePosition(ECC_SIGNATURE_TYPE_PREFERENCES, pos, posMap);
        }

        return -1; // no special preference - stick it as last
    }

    static int resolvePreferredSignaturePosition(
            List preferredOrder, int prefValue, Map posMap) {
        if (GenericUtils.isEmpty(preferredOrder) || (prefValue < 0) || MapEntryUtils.isEmpty(posMap)) {
            return -1;
        }

        int posValue = -1;
        for (Map.Entry pe : posMap.entrySet()) {
            String name = pe.getKey();
            int order = preferredOrder.indexOf(name);
            if (order < 0) {
                continue; // should not happen, but tolerate
            }

            Integer curIndex = pe.getValue();
            int resIndex;
            if (order < prefValue) {
                resIndex = curIndex.intValue() + 1;
            } else if (order > prefValue) {
                resIndex = curIndex.intValue(); // by using same index we insert in front of it in effect
            } else {
                continue; // should not happen, but tolerate
            }

            // Preferred factories should be as close as possible to the beginning of the list
            if ((posValue < 0) || (resIndex < posValue)) {
                posValue = resIndex;
            }
        }

        return posValue;
    }

    static NamedFactory resolveSignatureFactory(
            String keyType, Collection> factories) {
        if (GenericUtils.isEmpty(keyType) || GenericUtils.isEmpty(factories)) {
            return null;
        }

        Collection 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);
        }
    }

    /**
     * @param  pubKey                  The intended {@link PublicKey} - ignored if {@code null}
     * @param  algo                    The intended signature algorithm - if {@code null}/empty and multiple signatures
     *                                 available for the key type then a default will be used. Otherwise, it is
     *                                 validated to make sure it matches the public key type
     * @return                         The {@link Signature} factory or {@code null} if no match found
     * @throws InvalidKeySpecException If specified algorithm does not match the selected public key
     */
    static NamedFactory resolveSignatureFactoryByPublicKey(PublicKey pubKey, String algo)
            throws InvalidKeySpecException {
        if (pubKey == null) {
            return null;
        }

        NamedFactory factory = null;
        if (pubKey instanceof DSAPublicKey) {
            factory = BuiltinSignatures.dsa;
        } else if (pubKey instanceof ECPublicKey) {
            ECPublicKey ecKey = (ECPublicKey) pubKey;
            factory = BuiltinSignatures.getFactoryByCurveSize(ecKey.getParams());
        } else if (pubKey instanceof RSAPublicKey) {
            // SSHD-1104 take into account key aliases
            if (GenericUtils.isEmpty(algo)) {
                factory = BuiltinSignatures.rsa;
            } else if (algo.contains("rsa")) {
                factory = BuiltinSignatures.fromFactoryName(algo);
            }
        } else if (SecurityUtils.EDDSA.equalsIgnoreCase(pubKey.getAlgorithm())) {
            factory = BuiltinSignatures.ed25519;
        }

        if (GenericUtils.isEmpty(algo) || (factory == null)) {
            return factory;
        }

        String name = factory.getName();
        if (!algo.equalsIgnoreCase(name)) {
            throw new InvalidKeySpecException("Mismatched factory name (" + name + ")"
                                              + " for algorithm=" + algo + " when using key type"
                                              + KeyUtils.getKeyType(pubKey));
        }

        return factory;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy