--- /dev/null 2018-05-11 10:42:23.849000000 -0700 +++ new/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java 2018-05-11 15:10:33.228189100 -0700 @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2015, 2018, 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.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.util.KeyUtil; + +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", + ProtocolVersion.PROTOCOLS_TO_13), + ECDSA_SECP384R1_SHA384 (0x0503, "ecdsa_secp384r1_sha384", + "SHA384withECDSA", + "EC", + ProtocolVersion.PROTOCOLS_TO_13), + ECDSA_SECP512R1_SHA512 (0x0603, "ecdsa_secp512r1_sha512", + "SHA512withECDSA", + "EC", + ProtocolVersion.PROTOCOLS_TO_13), + + // RSASSA-PSS algorithms with public key OID rsaEncryption + RSA_PSS_RSAE_SHA256 (0x0804, "rsa_pss_rsae_sha256", + "RSASSA-PSS", "RSA", + SigAlgParamSpec.RSA_PSS_SHA256, 512, + ProtocolVersion.PROTOCOLS_OF_13), + RSA_PSS_RSAE_SHA384 (0x0805, "rsa_pss_rsae_sha384", + "RSASSA-PSS", "RSA", + SigAlgParamSpec.RSA_PSS_SHA384, 768, + ProtocolVersion.PROTOCOLS_OF_13), + RSA_PSS_RSAE_SHA512 (0x0806, "rsa_pss_rsae_sha512", + "RSASSA-PSS", "RSA", + SigAlgParamSpec.RSA_PSS_SHA512, 768, + ProtocolVersion.PROTOCOLS_OF_13), + + // RSASSA-PSS algorithms with public key OID RSASSA-PSS + RSA_PSS_PSS_SHA256 (0x0809, "rsa_pss_pss_sha256", + "RSASSA-PSS", "RSASSA-PSS", + SigAlgParamSpec.RSA_PSS_SHA256, 512, + ProtocolVersion.PROTOCOLS_OF_13), + RSA_PSS_PSS_SHA384 (0x080A, "rsa_pss_pss_sha384", + "RSASSA-PSS", "RSASSA-PSS", + SigAlgParamSpec.RSA_PSS_SHA384, 768, + ProtocolVersion.PROTOCOLS_OF_13), + RSA_PSS_PSS_SHA512 (0x080B, "rsa_pss_pss_sha512", + "RSASSA-PSS", "RSASSA-PSS", + SigAlgParamSpec.RSA_PSS_SHA512, 768, + ProtocolVersion.PROTOCOLS_OF_13), + + // RSASSA-PKCS1-v1_5 algorithms + RSA_PKCS1_SHA256 (0x0401, "rsa_pkcs1_sha256", "SHA256withRSA", + "RSA", null, 512, + ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_12), + RSA_PKCS1_SHA384 (0x0501, "rsa_pkcs1_sha384", "SHA384withRSA", + "RSA", null, 768, + ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_12), + RSA_PKCS1_SHA512 (0x0601, "rsa_pkcs1_sha512", "SHA512withRSA", + "RSA", 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", 768, + 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, 512, + 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", 512, + 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; + + // The minial 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 caculate the minial + // required key size exactly for a hash algorithm. + 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", null, saltLength, 1); + + boolean mediator = true; + try { + Signature signer = JsseJce.getSignature("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, minimalKeySize, + supportedProtocols, supportedProtocols); + } + + private SignatureScheme(int id, String name, + String algorithm, String keyAlgorithm, + SigAlgParamSpec signAlgParamSpec, 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.minimalKeySize = minimalKeySize; + this.supportedProtocols = Arrays.asList(supportedProtocols); + this.handshakeSupportedProtocols = Arrays.asList(handshakeSupportedProtocols); + + boolean mediator = true; + if (signAlgParamSpec != null) { + mediator = signAlgParamSpec.isAvailable; + } else { + try { + JsseJce.getSignature(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 sheme: " + ss.name); + } + } else if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Ignore inactive signature sheme: " + 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, + PrivateKey signingKey, + ProtocolVersion version) { + + String keyAlgorithm = signingKey.getAlgorithm(); + int keySize; + // Only need to check RSA algorithm at present. + if (keyAlgorithm.equalsIgnoreCase("rsa")) { + 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)) { + + 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() throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException { + if (!isAvailable) { + return null; + } + + Signature signer = JsseJce.getSignature(algorithm); + if (signAlgParameter != null) { + signer.setParameter(signAlgParameter); + } + + return signer; + } +}