--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java Fri May 11 15:53:12 2018 -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<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",
+ };
+
+ 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<CryptoPrimitive> 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<SignatureScheme> getSupportedAlgorithms(
+ AlgorithmConstraints constraints,
+ List<ProtocolVersion> activeProtocols) {
+ List<SignatureScheme> 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<SignatureScheme> getSupportedAlgorithms(
+ AlgorithmConstraints constraints,
+ ProtocolVersion protocolVersion, int[] algorithmIds) {
+ 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 (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<SignatureScheme> 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<SignatureScheme> 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<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];
+ }
+
+ Signature getSignature() throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException {
+ if (!isAvailable) {
+ return null;
+ }
+
+ Signature signer = JsseJce.getSignature(algorithm);
+ if (signAlgParameter != null) {
+ signer.setParameter(signAlgParameter);
+ }
+
+ return signer;
+ }
+}