src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
child 56706 a82a96b62d22
--- /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;
+    }
+}