/* * Copyright (c) 2015, 2019, 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.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import sun.security.ssl.NamedGroup.NamedGroupType; import sun.security.ssl.X509Authentication.X509Possession; import sun.security.util.KeyUtil; import sun.security.util.SignatureUtil; enum SignatureScheme { // EdDSA algorithms ED25519 (0x0807, "ed25519", "ed25519", "ed25519", ProtocolVersion.PROTOCOLS_OF_13), ED448 (0x0808, "ed448", "ed448", "ed448", ProtocolVersion.PROTOCOLS_OF_13), // 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), // 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", ProtocolVersion.PROTOCOLS_TO_13), 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 AlgorithmParameterSpec signAlgParameter; 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 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 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", }; static 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); final private AlgorithmParameterSpec parameterSpec; final boolean isAvailable; SigAlgParamSpec(String hash, int saltLength) { // See RFC 8017 PSSParameterSpec pssParamSpec = new PSSParameterSpec(hash, "MGF1", new MGF1ParameterSpec(hash), saltLength, 1); boolean mediator = true; try { Signature signer = Signature.getInstance("RSASSA-PSS"); signer.setParameter(pssParamSpec); } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException exp) { 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; } AlgorithmParameterSpec getParameterSpec() { return parameterSpec; } } // performance optimization private static final Set SIGNATURE_PRIMITIVE_SET = Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); private SignatureScheme(int id, String name, String algorithm, String keyAlgorithm, ProtocolVersion[] supportedProtocols) { this(id, name, algorithm, keyAlgorithm, -1, supportedProtocols); } private SignatureScheme(int id, String name, String algorithm, String keyAlgorithm, int minimalKeySize, ProtocolVersion[] supportedProtocols) { this(id, name, algorithm, keyAlgorithm, null, minimalKeySize, supportedProtocols); } private 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); } private SignatureScheme(int id, String name, String algorithm, String keyAlgorithm, NamedGroup namedGroup, ProtocolVersion[] supportedProtocols) { this(id, name, algorithm, keyAlgorithm, null, namedGroup, -1, supportedProtocols, supportedProtocols); } private SignatureScheme(int id, String name, String algorithm, String keyAlgorithm, SigAlgParamSpec signAlgParamSpec, NamedGroup namedGroup, int minimalKeySize, ProtocolVersion[] supportedProtocols, ProtocolVersion[] handshakeSupportedProtocols) { this.id = id; this.name = name; this.algorithm = algorithm; this.keyAlgorithm = keyAlgorithm; this.signAlgParameter = signAlgParamSpec != null ? signAlgParamSpec.parameterSpec : null; this.namedGroup = namedGroup; this.minimalKeySize = minimalKeySize; this.supportedProtocols = Arrays.asList(supportedProtocols); this.handshakeSupportedProtocols = Arrays.asList(handshakeSupportedProtocols); boolean mediator = true; if (signAlgParamSpec != null) { mediator = signAlgParamSpec.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; } // Return the size of a SignatureScheme structure in TLS record static int sizeInRecord() { return 2; } // Get local supported algorithm collection complying to algorithm // constraints. static List getSupportedAlgorithms( AlgorithmConstraints constraints, List activeProtocols) { List supported = new LinkedList<>(); for (SignatureScheme ss: SignatureScheme.values()) { if (!ss.isAvailable) { continue; } boolean isMatch = false; for (ProtocolVersion pv : activeProtocols) { if (ss.supportedProtocols.contains(pv)) { isMatch = true; break; } } if (isMatch) { if (constraints.permits( SIGNATURE_PRIMITIVE_SET, ss.algorithm, null)) { 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 getSupportedAlgorithms( AlgorithmConstraints constraints, ProtocolVersion protocolVersion, int[] algorithmIds) { List 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 (ss.isAvailable && ss.supportedProtocols.contains(protocolVersion) && constraints.permits(SIGNATURE_PRIMITIVE_SET, ss.algorithm, null)) { supported.add(ss); } else { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unsupported signature scheme: " + ss.name); } } } return supported; } static SignatureScheme getPreferableAlgorithm( List schemes, SignatureScheme certScheme, ProtocolVersion version) { for (SignatureScheme ss : schemes) { if (ss.isAvailable && ss.handshakeSupportedProtocols.contains(version) && certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) { return ss; } } return null; } static SignatureScheme getPreferableAlgorithm( List 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 (ss.isAvailable && (keySize >= ss.minimalKeySize) && ss.handshakeSupportedProtocols.contains(version) && keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) { if ((ss.namedGroup != null) && (ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE)) { ECParameterSpec params = x509Possession.getECParameterSpec(); if (params != null && ss.namedGroup == NamedGroup.valueOf(params)) { return ss; } } else { return ss; } } } return null; } static String[] getAlgorithmNames(Collection schemes) { if (schemes != null) { ArrayList names = new ArrayList<>(schemes.size()); for (SignatureScheme scheme : schemes) { names.add(scheme.algorithm); } return names.toArray(new String[0]); } return new String[0]; } Signature getSignature(Key key) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { if (!isAvailable) { return null; } Signature signer = Signature.getInstance(algorithm); if (key instanceof PublicKey) { SignatureUtil.initVerifyWithParam(signer, (PublicKey)key, signAlgParameter); } else { SignatureUtil.initSignWithParam(signer, (PrivateKey)key, signAlgParameter, null); } return signer; } }