/*
 * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.security.ssl;

import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.KeyUtil;
import sun.security.util.SignatureUtil;

enum SignatureScheme {
    // ECDSA algorithms
    ECDSA_SECP256R1_SHA256  (0x0403, "ecdsa_secp256r1_sha256",
                                    "SHA256withECDSA",
                                    "EC",
                                    NamedGroup.SECP256_R1,
                                    ProtocolVersion.PROTOCOLS_TO_13),
    ECDSA_SECP384R1_SHA384  (0x0503, "ecdsa_secp384r1_sha384",
                                    "SHA384withECDSA",
                                    "EC",
                                    NamedGroup.SECP384_R1,
                                    ProtocolVersion.PROTOCOLS_TO_13),
    ECDSA_SECP521R1_SHA512  (0x0603, "ecdsa_secp521r1_sha512",
                                    "SHA512withECDSA",
                                    "EC",
                                    NamedGroup.SECP521_R1,
                                    ProtocolVersion.PROTOCOLS_TO_13),

    // EdDSA algorithms
    ED25519                 (0x0807, "ed25519", "Ed25519",
                                    "EdDSA",
                                    ProtocolVersion.PROTOCOLS_12_13),
    ED448                   (0x0808, "ed448", "Ed448",
                                    "EdDSA",
                                    ProtocolVersion.PROTOCOLS_12_13),

    // RSASSA-PSS algorithms with public key OID rsaEncryption
    //
    // The minimalKeySize is calculated as (See RFC 8017 for details):
    //     hash length + salt length + 16
    RSA_PSS_RSAE_SHA256     (0x0804, "rsa_pss_rsae_sha256",
                                    "RSASSA-PSS", "RSA",
                                    SigAlgParamSpec.RSA_PSS_SHA256, 528,
                                    ProtocolVersion.PROTOCOLS_12_13),
    RSA_PSS_RSAE_SHA384     (0x0805, "rsa_pss_rsae_sha384",
                                    "RSASSA-PSS", "RSA",
                                    SigAlgParamSpec.RSA_PSS_SHA384, 784,
                                    ProtocolVersion.PROTOCOLS_12_13),
    RSA_PSS_RSAE_SHA512     (0x0806, "rsa_pss_rsae_sha512",
                                    "RSASSA-PSS", "RSA",
                                    SigAlgParamSpec.RSA_PSS_SHA512, 1040,
                                    ProtocolVersion.PROTOCOLS_12_13),

    // RSASSA-PSS algorithms with public key OID RSASSA-PSS
    //
    // The minimalKeySize is calculated as (See RFC 8017 for details):
    //     hash length + salt length + 16
    RSA_PSS_PSS_SHA256      (0x0809, "rsa_pss_pss_sha256",
                                    "RSASSA-PSS", "RSASSA-PSS",
                                    SigAlgParamSpec.RSA_PSS_SHA256, 528,
                                    ProtocolVersion.PROTOCOLS_12_13),
    RSA_PSS_PSS_SHA384      (0x080A, "rsa_pss_pss_sha384",
                                    "RSASSA-PSS", "RSASSA-PSS",
                                    SigAlgParamSpec.RSA_PSS_SHA384, 784,
                                    ProtocolVersion.PROTOCOLS_12_13),
    RSA_PSS_PSS_SHA512      (0x080B, "rsa_pss_pss_sha512",
                                    "RSASSA-PSS", "RSASSA-PSS",
                                    SigAlgParamSpec.RSA_PSS_SHA512, 1040,
                                    ProtocolVersion.PROTOCOLS_12_13),

    // RSASSA-PKCS1-v1_5 algorithms
    RSA_PKCS1_SHA256        (0x0401, "rsa_pkcs1_sha256", "SHA256withRSA",
                                    "RSA", null, null, 511,
                                    ProtocolVersion.PROTOCOLS_TO_13,
                                    ProtocolVersion.PROTOCOLS_TO_12),
    RSA_PKCS1_SHA384        (0x0501, "rsa_pkcs1_sha384", "SHA384withRSA",
                                    "RSA", null, null, 768,
                                    ProtocolVersion.PROTOCOLS_TO_13,
                                    ProtocolVersion.PROTOCOLS_TO_12),
    RSA_PKCS1_SHA512        (0x0601, "rsa_pkcs1_sha512", "SHA512withRSA",
                                    "RSA", null, null, 768,
                                    ProtocolVersion.PROTOCOLS_TO_13,
                                    ProtocolVersion.PROTOCOLS_TO_12),

    // Legacy algorithms
    DSA_SHA256              (0x0402, "dsa_sha256", "SHA256withDSA",
                                    "DSA",
                                    ProtocolVersion.PROTOCOLS_TO_12),
    ECDSA_SHA224            (0x0303, "ecdsa_sha224", "SHA224withECDSA",
                                    "EC",
                                    ProtocolVersion.PROTOCOLS_TO_12),
    RSA_SHA224              (0x0301, "rsa_sha224", "SHA224withRSA",
                                    "RSA", 511,
                                    ProtocolVersion.PROTOCOLS_TO_12),
    DSA_SHA224              (0x0302, "dsa_sha224", "SHA224withDSA",
                                    "DSA",
                                    ProtocolVersion.PROTOCOLS_TO_12),
    ECDSA_SHA1              (0x0203, "ecdsa_sha1", "SHA1withECDSA",
                                    "EC", null, null, -1,
                                    ProtocolVersion.PROTOCOLS_TO_13,
                                    ProtocolVersion.PROTOCOLS_TO_12),
    RSA_PKCS1_SHA1          (0x0201, "rsa_pkcs1_sha1", "SHA1withRSA",
                                    "RSA", null, null, 511,
                                    ProtocolVersion.PROTOCOLS_TO_13,
                                    ProtocolVersion.PROTOCOLS_TO_12),
    DSA_SHA1                (0x0202, "dsa_sha1", "SHA1withDSA",
                                    "DSA",
                                    ProtocolVersion.PROTOCOLS_TO_12),
    RSA_MD5                 (0x0101, "rsa_md5", "MD5withRSA",
                                    "RSA", 511,
                                    ProtocolVersion.PROTOCOLS_TO_12);

    final int id;                       // hash + signature
    final String name;                  // literal name
    private final String algorithm;     // signature algorithm
    final String keyAlgorithm;          // signature key algorithm
    private final SigAlgParamSpec signAlgParams;    // signature parameters
    private final NamedGroup namedGroup;    // associated named group

    // The minimal required key size in bits.
    //
    // Only need to check RSA algorithm at present. RSA keys of 512 bits
    // have been shown to be practically breakable, it does not make much
    // sense to use the strong hash algorithm for keys whose key size less
    // than 512 bits.  So it is not necessary to calculate the minimal
    // required key size exactly for a hash algorithm.
    //
    // Note that some provider may use 511 bits for 512-bit strength RSA keys.
    final int minimalKeySize;
    final List<ProtocolVersion> supportedProtocols;

    // Some signature schemes are supported in different versions for handshake
    // messages and certificates. This field holds the supported protocols
    // for handshake messages.
    final List<ProtocolVersion> handshakeSupportedProtocols;
    final boolean isAvailable;

    private static final String[] hashAlgorithms = new String[] {
            "none",         "md5",      "sha1",     "sha224",
            "sha256",       "sha384",   "sha512"
        };

    private static final String[] signatureAlgorithms = new String[] {
            "anonymous",    "rsa",      "dsa",      "ecdsa",
        };

    enum SigAlgParamSpec {   // support RSASSA-PSS only now
        RSA_PSS_SHA256 ("SHA-256", 32),
        RSA_PSS_SHA384 ("SHA-384", 48),
        RSA_PSS_SHA512 ("SHA-512", 64);

        private final AlgorithmParameterSpec parameterSpec;
        private final AlgorithmParameters parameters;
        private final boolean isAvailable;

        SigAlgParamSpec(String hash, int saltLength) {
            // See RFC 8017
            PSSParameterSpec pssParamSpec =
                    new PSSParameterSpec(hash, "MGF1",
                            new MGF1ParameterSpec(hash), saltLength, 1);
            AlgorithmParameters pssParams = null;

            boolean mediator = true;
            try {
                Signature signer = Signature.getInstance("RSASSA-PSS");
                signer.setParameter(pssParamSpec);
                pssParams = signer.getParameters();
            } catch (InvalidAlgorithmParameterException |
                    NoSuchAlgorithmException | RuntimeException exp) {
                // Signature.getParameters() may throw RuntimeException.
                mediator = false;
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                    SSLLogger.warning(
                        "RSASSA-PSS signature with " + hash +
                        " is not supported by the underlying providers", exp);
                }
            }

            this.isAvailable = mediator;
            this.parameterSpec = mediator ? pssParamSpec : null;
            this.parameters = mediator ? pssParams : null;
        }
    }

    // Handshake signature scope.
    static final Set<SSLScope> HANDSHAKE_SCOPE =
            Set.of(SSLScope.HANDSHAKE_SIGNATURE);

    // Certificate signature scope.
    static final Set<SSLScope> CERTIFICATE_SCOPE =
            Set.of(SSLScope.CERTIFICATE_SIGNATURE);

    // Non-TLS specific SIGNATURE CryptoPrimitive.
    private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
            Set.of(CryptoPrimitive.SIGNATURE);

    SignatureScheme(int id, String name,
            String algorithm, String keyAlgorithm,
            ProtocolVersion[] supportedProtocols) {
        this(id, name, algorithm, keyAlgorithm, -1, supportedProtocols);
    }

    SignatureScheme(int id, String name,
            String algorithm, String keyAlgorithm,
            int minimalKeySize,
            ProtocolVersion[] supportedProtocols) {
        this(id, name, algorithm, keyAlgorithm,
                null, minimalKeySize, supportedProtocols);
    }

    SignatureScheme(int id, String name,
            String algorithm, String keyAlgorithm,
            SigAlgParamSpec signAlgParamSpec, int minimalKeySize,
            ProtocolVersion[] supportedProtocols) {
        this(id, name, algorithm, keyAlgorithm,
                signAlgParamSpec, null, minimalKeySize,
                supportedProtocols, supportedProtocols);
    }

    SignatureScheme(int id, String name,
            String algorithm, String keyAlgorithm,
            NamedGroup namedGroup,
            ProtocolVersion[] supportedProtocols) {
        this(id, name, algorithm, keyAlgorithm,
                null, namedGroup, -1,
                supportedProtocols, supportedProtocols);
    }

    SignatureScheme(int id, String name,
            String algorithm, String keyAlgorithm,
            SigAlgParamSpec signAlgParams,
            NamedGroup namedGroup, int minimalKeySize,
            ProtocolVersion[] supportedProtocols,
            ProtocolVersion[] handshakeSupportedProtocols) {
        this.id = id;
        this.name = name;
        this.algorithm = algorithm;
        this.keyAlgorithm = keyAlgorithm;
        this.signAlgParams = signAlgParams;
        this.namedGroup = namedGroup;
        this.minimalKeySize = minimalKeySize;
        this.supportedProtocols = Arrays.asList(supportedProtocols);
        this.handshakeSupportedProtocols =
                Arrays.asList(handshakeSupportedProtocols);

        boolean mediator = true;

        // An EC provider, for example the SunEC provider, may support
        // AlgorithmParameters but not KeyPairGenerator or Signature.
        //
        // Note: Please be careful if removing this block!
        if ("EC".equals(keyAlgorithm)) {
            mediator = JsseJce.isEcAvailable();
        }

        // Check the specific algorithm and parameters.
        if (mediator) {
            if (signAlgParams != null) {
                mediator = signAlgParams.isAvailable;
            } else {
                try {
                    Signature.getInstance(algorithm);
                } catch (Exception e) {
                    mediator = false;
                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                        SSLLogger.warning(
                            "Signature algorithm, " + algorithm +
                            ", is not supported by the underlying providers");
                    }
                }
            }
        }

        if (mediator && ((id >> 8) & 0xFF) == 0x03) {   // SHA224
            // There are some problems to use SHA224 on Windows.
            if (Security.getProvider("SunMSCAPI") != null) {
                mediator = false;
            }
        }

        this.isAvailable = mediator;
    }

    static SignatureScheme valueOf(int id) {
        for (SignatureScheme ss: SignatureScheme.values()) {
            if (ss.id == id) {
                return ss;
            }
        }

        return null;
    }

    static String nameOf(int id) {
        for (SignatureScheme ss: SignatureScheme.values()) {
            if (ss.id == id) {
                return ss.name;
            }
        }

        // Use TLS 1.2 style name for unknown signature scheme.
        int hashId = ((id >> 8) & 0xFF);
        int signId = (id & 0xFF);
        String hashName = (hashId >= hashAlgorithms.length) ?
            "UNDEFINED-HASH(" + hashId + ")" : hashAlgorithms[hashId];
        String signName = (signId >= signatureAlgorithms.length) ?
            "UNDEFINED-SIGNATURE(" + signId + ")" :
            signatureAlgorithms[signId];

        return signName + "_" + hashName;
    }

    // Note: the signatureSchemeName is not case-sensitive.
    static SignatureScheme nameOf(String signatureSchemeName) {
        for (SignatureScheme ss: SignatureScheme.values()) {
            if (ss.name.equalsIgnoreCase(signatureSchemeName)) {
                return ss;
            }
        }

        return null;
    }

    // Return the size of a SignatureScheme structure in TLS record
    static int sizeInRecord() {
        return 2;
    }

    private boolean isPermitted(
            SSLAlgorithmConstraints constraints, Set<SSLScope> scopes) {
        return constraints.permits(this.name, scopes)
                && constraints.permits(this.keyAlgorithm, scopes)
                && constraints.permits(this.algorithm, scopes)
                && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.name, null)
                && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.keyAlgorithm, null)
                && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.algorithm,
                (signAlgParams != null ? signAlgParams.parameters : null))
                && (namedGroup == null || namedGroup.isPermitted(constraints));
    }

    // Helper method to update all locally supported signature schemes for
    // a given HandshakeContext.
    static void updateHandshakeLocalSupportedAlgs(HandshakeContext hc) {
        // To improve performance we only update when necessary.
        // No need to do anything if we already computed the local supported
        // algorithms and either there is no negotiated protocol yet or the
        // only active protocol ends up to be the negotiated protocol.
        if (hc.localSupportedSignAlgs != null
                && hc.localSupportedCertSignAlgs != null
                && (hc.negotiatedProtocol == null
                || hc.activeProtocols.size() == 1)) {
            return;
        }

        List<ProtocolVersion> protocols = hc.negotiatedProtocol != null ?
                List.of(hc.negotiatedProtocol) :
                hc.activeProtocols;

        hc.localSupportedSignAlgs = getSupportedAlgorithms(
                hc.sslConfig,
                hc.algorithmConstraints,
                protocols,
                HANDSHAKE_SCOPE);

        hc.localSupportedCertSignAlgs = getSupportedAlgorithms(
                hc.sslConfig,
                hc.algorithmConstraints,
                protocols,
                CERTIFICATE_SCOPE);
    }

    // Get local supported algorithm collection complying to algorithm
    // constraints and SSL scopes.
    private static List<SignatureScheme> getSupportedAlgorithms(
            SSLConfiguration config,
            SSLAlgorithmConstraints constraints,
            List<ProtocolVersion> activeProtocols,
            Set<SSLScope> scopes) {
        List<SignatureScheme> supported = new LinkedList<>();

        List<SignatureScheme> schemesToCheck =
                config.signatureSchemes == null ?
                    Arrays.asList(SignatureScheme.values()) :
                    namesOfAvailable(config.signatureSchemes);

        for (SignatureScheme ss: schemesToCheck) {
            if (!ss.isAvailable) {
                if (SSLLogger.isOn &&
                        SSLLogger.isOn("ssl,handshake,verbose")) {
                    SSLLogger.finest(
                        "Ignore unsupported signature scheme: " + ss.name);
                }
                continue;
            }

            boolean isMatch = false;
            for (ProtocolVersion pv : activeProtocols) {
                if (ss.isSupportedProtocol(pv, scopes)) {
                    isMatch = true;
                    break;
                }
            }

            if (isMatch) {
                if (ss.isPermitted(constraints, scopes)) {
                    supported.add(ss);
                } else if (SSLLogger.isOn &&
                        SSLLogger.isOn("ssl,handshake,verbose")) {
                    SSLLogger.finest(
                        "Ignore disabled signature scheme: " + ss.name);
                }
            } else if (SSLLogger.isOn &&
                    SSLLogger.isOn("ssl,handshake,verbose")) {
                SSLLogger.finest(
                    "Ignore inactive signature scheme: " + ss.name);
            }
        }

        return supported;
    }

    static List<SignatureScheme> getSupportedAlgorithms(
            SSLConfiguration config,
            SSLAlgorithmConstraints constraints,
            ProtocolVersion protocolVersion,
            int[] algorithmIds,
            Set<SSLScope> scopes) {
        List<SignatureScheme> supported = new LinkedList<>();
        for (int ssid : algorithmIds) {
            SignatureScheme ss = SignatureScheme.valueOf(ssid);
            if (ss == null) {
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                    SSLLogger.warning(
                            "Unsupported signature scheme: " +
                            SignatureScheme.nameOf(ssid));
                }
            } else if ((config.signatureSchemes == null
                        || Utilities.contains(config.signatureSchemes, ss.name))
                    && ss.isAllowed(constraints, protocolVersion, scopes)) {
                supported.add(ss);
            } else {
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                    SSLLogger.warning(
                            "Unsupported signature scheme: " + ss.name);
                }
            }
        }

        return supported;
    }

    static SignatureScheme getPreferableAlgorithm(
            SSLAlgorithmConstraints constraints,
            List<SignatureScheme> schemes,
            String keyAlgorithm,
            ProtocolVersion version) {

        for (SignatureScheme ss : schemes) {
            if (keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)
                    && ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) {
                return ss;
            }
        }

        return null;
    }

    static Map.Entry<SignatureScheme, Signature> getSignerOfPreferableAlgorithm(
            SSLConfiguration sslConfig,
            SSLAlgorithmConstraints constraints,
            List<SignatureScheme> schemes,
            X509Possession x509Possession,
            ProtocolVersion version) {

        PrivateKey signingKey = x509Possession.popPrivateKey;
        String keyAlgorithm = signingKey.getAlgorithm();
        int keySize;
        // Only need to check RSA algorithm at present.
        if (keyAlgorithm.equalsIgnoreCase("RSA") ||
                keyAlgorithm.equalsIgnoreCase("RSASSA-PSS")) {
            keySize = KeyUtil.getKeySize(signingKey);
        } else {
            keySize = Integer.MAX_VALUE;
        }
        for (SignatureScheme ss : schemes) {
            if (keySize >= ss.minimalKeySize &&
                    keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) &&
                    ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) {
                if ((ss.namedGroup != null) && (ss.namedGroup.spec ==
                        NamedGroupSpec.NAMED_GROUP_ECDHE)) {
                    ECParameterSpec params =
                            x509Possession.getECParameterSpec();
                    if (params != null &&
                            ss.namedGroup == NamedGroup.valueOf(params)) {
                        Signature signer = ss.getSigner(signingKey);
                        if (signer != null) {
                            return new SimpleImmutableEntry<>(ss, signer);
                        }
                    }

                    if (SSLLogger.isOn &&
                            SSLLogger.isOn("ssl,handshake,verbose")) {
                        SSLLogger.finest(
                            "Ignore the signature algorithm (" + ss +
                            "), unsupported EC parameter spec: " + params);
                    }
                } else if ("EC".equals(ss.keyAlgorithm)) {
                    // Must be a legacy signature algorithm, which does not
                    // specify the associated named groups.  The connection
                    // cannot be established if the peer cannot recognize
                    // the named group used for the signature.  RFC 8446
                    // does not define countermeasures for the corner cases.
                    // In order to mitigate the impact, we choose to check
                    // against the local supported named groups.  The risk
                    // should be minimal as applications should not use
                    // unsupported named groups for its certificates.
                    ECParameterSpec params =
                            x509Possession.getECParameterSpec();
                    if (params != null) {
                        NamedGroup keyGroup = NamedGroup.valueOf(params);
                        if (keyGroup != null &&
                                NamedGroup.isEnabled(sslConfig, keyGroup)) {
                            Signature signer = ss.getSigner(signingKey);
                            if (signer != null) {
                                return new SimpleImmutableEntry<>(ss, signer);
                            }
                        }
                    }

                    if (SSLLogger.isOn &&
                            SSLLogger.isOn("ssl,handshake,verbose")) {
                        SSLLogger.finest(
                            "Ignore the legacy signature algorithm (" + ss +
                            "), unsupported EC parameter spec: " + params);
                    }
                } else {
                    Signature signer = ss.getSigner(signingKey);
                    if (signer != null) {
                        return new SimpleImmutableEntry<>(ss, signer);
                    }
                }
            }
        }

        return null;
    }

    // Returns true if this signature scheme is supported for the given
    // protocol version and SSL scopes.
    private boolean isSupportedProtocol(
            ProtocolVersion version, Set<SSLScope> scopes) {
        if (scopes != null && scopes.equals(HANDSHAKE_SCOPE)) {
            return this.handshakeSupportedProtocols.contains(version);
        } else {
            return this.supportedProtocols.contains(version);
        }
    }

    // Returns true if this signature scheme is available, supported and
    // permitted for the given constraints, protocol version and SSL scopes.
    private boolean isAllowed(SSLAlgorithmConstraints constraints,
            ProtocolVersion version, Set<SSLScope> scopes) {
        return isAvailable
                && isSupportedProtocol(version, scopes)
                && isPermitted(constraints, scopes);
    }

    static String[] getAlgorithmNames(Collection<SignatureScheme> schemes) {
        if (schemes != null) {
            ArrayList<String> names = new ArrayList<>(schemes.size());
            for (SignatureScheme scheme : schemes) {
                names.add(scheme.algorithm);
            }

            return names.toArray(new String[0]);
        }

        return new String[0];
    }

    private static List<SignatureScheme> namesOfAvailable(
                String[] signatureSchemes) {

        if (signatureSchemes == null || signatureSchemes.length == 0) {
            return Collections.emptyList();
        }

        List<SignatureScheme> sss = new ArrayList<>(signatureSchemes.length);
        for (String ss : signatureSchemes) {
            SignatureScheme scheme = SignatureScheme.nameOf(ss);
            if (scheme == null || !scheme.isAvailable) {
                if (SSLLogger.isOn &&
                        SSLLogger.isOn("ssl,handshake,verbose")) {
                    SSLLogger.finest(
                            "Ignore the signature algorithm (" + ss
                          + "), unsupported or unavailable");
                }

                continue;
            }

            sss.add(scheme);
        }

        return sss;
    }

    // This method is used to get the signature instance of this signature
    // scheme for the specific public key.  Unlike getSigner(), the exception
    // is bubbled up.  If the public key does not support this signature
    // scheme, it normally means the TLS handshaking cannot continue and
    // the connection should be terminated.
    Signature getVerifier(PublicKey publicKey) throws NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException {
        if (!isAvailable) {
            return null;
        }

        Signature verifier = Signature.getInstance(algorithm);
        SignatureUtil.initVerifyWithParam(verifier, publicKey,
                (signAlgParams != null ? signAlgParams.parameterSpec : null));

        return verifier;
    }

    // This method is also used to choose preferable signature scheme for the
    // specific private key.  If the private key does not support the signature
    // scheme, {@code null} is returned, and the caller may fail back to next
    // available signature scheme.
    private Signature getSigner(PrivateKey privateKey) {
        if (!isAvailable) {
            return null;
        }

        try {
            Signature signer = Signature.getInstance(algorithm);
            SignatureUtil.initSignWithParam(signer, privateKey,
                (signAlgParams != null ? signAlgParams.parameterSpec : null),
                null);
            return signer;
        } catch (NoSuchAlgorithmException | InvalidKeyException |
                InvalidAlgorithmParameterException nsae) {
            if (SSLLogger.isOn &&
                    SSLLogger.isOn("ssl,handshake,verbose")) {
                SSLLogger.finest(
                    "Ignore unsupported signature algorithm (" +
                    this.name + ")", nsae);
            }
        }

        return null;
    }
}
