# HG changeset patch # User xuelei # Date 1468043778 0 # Node ID 1449ed425710ceb80e8097105422ae48cc8866ba # Parent 672b948cb3557e9ea9d14d5b163427010734c8ca 8148516: Improve the default strength of EC in JDK Reviewed-by: valeriep diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Sat Jul 09 05:56:18 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, 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 @@ -376,36 +376,38 @@ static enum KeyExchange { // key exchange algorithms - K_NULL ("NULL", false), - K_RSA ("RSA", true), - K_RSA_EXPORT ("RSA_EXPORT", true), - K_DH_RSA ("DH_RSA", false), - K_DH_DSS ("DH_DSS", false), - K_DHE_DSS ("DHE_DSS", true), - K_DHE_RSA ("DHE_RSA", true), - K_DH_ANON ("DH_anon", true), + K_NULL ("NULL", false, false), + K_RSA ("RSA", true, false), + K_RSA_EXPORT ("RSA_EXPORT", true, false), + K_DH_RSA ("DH_RSA", false, false), + K_DH_DSS ("DH_DSS", false, false), + K_DHE_DSS ("DHE_DSS", true, false), + K_DHE_RSA ("DHE_RSA", true, false), + K_DH_ANON ("DH_anon", true, false), - K_ECDH_ECDSA ("ECDH_ECDSA", ALLOW_ECC), - K_ECDH_RSA ("ECDH_RSA", ALLOW_ECC), - K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC), - K_ECDHE_RSA ("ECDHE_RSA", ALLOW_ECC), - K_ECDH_ANON ("ECDH_anon", ALLOW_ECC), + K_ECDH_ECDSA ("ECDH_ECDSA", ALLOW_ECC, true), + K_ECDH_RSA ("ECDH_RSA", ALLOW_ECC, true), + K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC, true), + K_ECDHE_RSA ("ECDHE_RSA", ALLOW_ECC, true), + K_ECDH_ANON ("ECDH_anon", ALLOW_ECC, true), // Kerberos cipher suites - K_KRB5 ("KRB5", true), - K_KRB5_EXPORT("KRB5_EXPORT", true), + K_KRB5 ("KRB5", true, false), + K_KRB5_EXPORT("KRB5_EXPORT", true, false), // renegotiation protection request signaling cipher suite - K_SCSV ("SCSV", true); + K_SCSV ("SCSV", true, false); // name of the key exchange algorithm, e.g. DHE_DSS final String name; final boolean allowed; + final boolean isEC; private final boolean alwaysAvailable; - KeyExchange(String name, boolean allowed) { + KeyExchange(String name, boolean allowed, boolean isEC) { this.name = name; this.allowed = allowed; + this.isEC = isEC; this.alwaysAvailable = allowed && (!name.startsWith("EC")) && (!name.startsWith("KRB")); } @@ -415,7 +417,7 @@ return true; } - if (name.startsWith("EC")) { + if (isEC) { return (allowed && JsseJce.isEcAvailable()); } else if (name.startsWith("KRB")) { return (allowed && JsseJce.isKerberosAvailable()); diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java Sat Jul 09 05:56:18 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, 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 @@ -112,20 +112,15 @@ boolean containsEC() { if (containsEC == null) { for (CipherSuite c : cipherSuites) { - switch (c.keyExchange) { - case K_ECDH_ECDSA: - case K_ECDH_RSA: - case K_ECDHE_ECDSA: - case K_ECDHE_RSA: - case K_ECDH_ANON: + if (c.keyExchange.isEC) { containsEC = true; return true; - default: - break; } } + containsEC = false; } + return containsEC; } diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Sat Jul 09 05:56:18 2016 +0000 @@ -877,30 +877,33 @@ String typeName; switch (certRequest.types[i]) { - case CertificateRequest.cct_rsa_sign: - typeName = "RSA"; - break; + case CertificateRequest.cct_rsa_sign: + typeName = "RSA"; + break; - case CertificateRequest.cct_dss_sign: - typeName = "DSA"; - break; + case CertificateRequest.cct_dss_sign: + typeName = "DSA"; + break; + + case CertificateRequest.cct_ecdsa_sign: + // ignore if we do not have EC crypto available + typeName = JsseJce.isEcAvailable() ? "EC" : null; + break; - case CertificateRequest.cct_ecdsa_sign: - // ignore if we do not have EC crypto available - typeName = JsseJce.isEcAvailable() ? "EC" : null; - break; - - // Fixed DH/ECDH client authentication not supported - case CertificateRequest.cct_rsa_fixed_dh: - case CertificateRequest.cct_dss_fixed_dh: - case CertificateRequest.cct_rsa_fixed_ecdh: - case CertificateRequest.cct_ecdsa_fixed_ecdh: - // Any other values (currently not used in TLS) - case CertificateRequest.cct_rsa_ephemeral_dh: - case CertificateRequest.cct_dss_ephemeral_dh: - default: - typeName = null; - break; + // Fixed DH/ECDH client authentication not supported + // + // case CertificateRequest.cct_rsa_fixed_dh: + // case CertificateRequest.cct_dss_fixed_dh: + // case CertificateRequest.cct_rsa_fixed_ecdh: + // case CertificateRequest.cct_ecdsa_fixed_ecdh: + // + // Any other values (currently not used in TLS) + // + // case CertificateRequest.cct_rsa_ephemeral_dh: + // case CertificateRequest.cct_dss_ephemeral_dh: + default: + typeName = null; + break; } if ((typeName != null) && (!keytypesTmp.contains(typeName))) { @@ -928,16 +931,6 @@ X509Certificate[] certs = km.getCertificateChain(alias); if ((certs != null) && (certs.length != 0)) { PublicKey publicKey = certs[0].getPublicKey(); - // for EC, make sure we use a supported named curve - if (publicKey instanceof ECPublicKey) { - ECParameterSpec params = - ((ECPublicKey)publicKey).getParams(); - int index = - EllipticCurvesExtension.getCurveIndex(params); - if (!EllipticCurvesExtension.isSupported(index)) { - publicKey = null; - } - } if (publicKey != null) { m1 = new CertificateMsg(certs); signingKey = km.getPrivateKey(alias); @@ -1499,6 +1492,17 @@ sslContext.getSecureRandom(), maxProtocolVersion, sessionId, cipherSuites, isDTLS); + // add elliptic curves and point format extensions + if (cipherSuites.containsEC()) { + EllipticCurvesExtension ece = + EllipticCurvesExtension.createExtension(algorithmConstraints); + if (ece != null) { + clientHelloMessage.extensions.add(ece); + clientHelloMessage.extensions.add( + EllipticPointFormatsExtension.DEFAULT); + } + } + // add signature_algorithm extension if (maxProtocolVersion.useTLS12PlusSpec()) { // we will always send the signature_algorithm extension diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java Sat Jul 09 05:56:18 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, 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 @@ -56,10 +56,11 @@ } // Called by ServerHandshaker for ephemeral ECDH - ECDHCrypt(String curveName, SecureRandom random) { + ECDHCrypt(int curveId, SecureRandom random) { try { KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC"); - ECGenParameterSpec params = new ECGenParameterSpec(curveName); + ECGenParameterSpec params = + EllipticCurvesExtension.getECGenParamSpec(curveId); kpg.initialize(params, random); KeyPair kp = kpg.generateKeyPair(); privateKey = kp.getPrivate(); diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/EllipticCurvesExtension.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/EllipticCurvesExtension.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/EllipticCurvesExtension.java Sat Jul 09 05:56:18 2016 +0000 @@ -27,58 +27,271 @@ import java.io.IOException; import java.security.spec.ECParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.AlgorithmParameters; +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.AccessController; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import java.util.ArrayList; +import javax.net.ssl.SSLProtocolException; -import javax.net.ssl.SSLProtocolException; +import sun.security.action.GetPropertyAction; final class EllipticCurvesExtension extends HelloExtension { - // the extension value to send in the ClientHello message - static final EllipticCurvesExtension DEFAULT; + private static final int ARBITRARY_PRIME = 0xff01; + private static final int ARBITRARY_CHAR2 = 0xff02; + + // speed up the searching + private static final Map oidToIdMap = new HashMap<>(); + private static final Map idToOidMap = new HashMap<>(); + + // speed up the parameters construction + private static final Map idToParams = new HashMap<>(); + + // the supported elliptic curves + private static final int[] supportedCurveIds; + + // the curves of the extension + private final int[] curveIds; + + // See sun.security.util.CurveDB for the OIDs + private static enum NamedEllipticCurve { + T163_K1(1, "sect163k1", "1.3.132.0.1", true), // NIST K-163 + T163_R1(2, "sect163r1", "1.3.132.0.2", false), + T163_R2(3, "sect163r2", "1.3.132.0.15", true), // NIST B-163 + T193_R1(4, "sect193r1", "1.3.132.0.24", false), + T193_R2(5, "sect193r2", "1.3.132.0.25", false), + T233_K1(6, "sect233k1", "1.3.132.0.26", true), // NIST K-233 + T233_R1(7, "sect233r1", "1.3.132.0.27", true), // NIST B-233 + T239_K1(8, "sect239k1", "1.3.132.0.3", false), + T283_K1(9, "sect283k1", "1.3.132.0.16", true), // NIST K-283 + T283_R1(10, "sect283r1", "1.3.132.0.17", true), // NIST B-283 + T409_K1(11, "sect409k1", "1.3.132.0.36", true), // NIST K-409 + T409_R1(12, "sect409r1", "1.3.132.0.37", true), // NIST B-409 + T571_K1(13, "sect571k1", "1.3.132.0.38", true), // NIST K-571 + T571_R1(14, "sect571r1", "1.3.132.0.39", true), // NIST B-571 - private static final boolean fips; + P160_K1(15, "secp160k1", "1.3.132.0.9", false), + P160_R1(16, "secp160r1", "1.3.132.0.8", false), + P160_R2(17, "secp160r2", "1.3.132.0.30", false), + P192_K1(18, "secp192k1", "1.3.132.0.31", false), + P192_R1(19, "secp192r1", "1.2.840.10045.3.1.1", true), // NIST P-192 + P224_K1(20, "secp224k1", "1.3.132.0.32", false), + P224_R1(21, "secp224r1", "1.3.132.0.33", true), // NIST P-224 + P256_K1(22, "secp256k1", "1.3.132.0.10", false), + P256_R1(23, "secp256r1", "1.2.840.10045.3.1.7", true), // NIST P-256 + P384_R1(24, "secp384r1", "1.3.132.0.34", true), // NIST P-384 + P521_R1(25, "secp521r1", "1.3.132.0.35", true); // NIST P-521 + + int id; + String name; + String oid; + boolean isFips; + + NamedEllipticCurve(int id, String name, String oid, boolean isFips) { + this.id = id; + this.name = name; + this.oid = oid; + this.isFips = isFips; + + if (oidToIdMap.put(oid, id) != null || + idToOidMap.put(id, oid) != null) { + + throw new RuntimeException( + "Duplicate named elliptic curve definition: " + name); + } + } + + static NamedEllipticCurve getCurve(String name, boolean requireFips) { + for (NamedEllipticCurve curve : NamedEllipticCurve.values()) { + if (curve.name.equals(name) && (!requireFips || curve.isFips)) { + return curve; + } + } + + return null; + } + } static { - int[] ids; - fips = SunJSSE.isFIPS(); - if (fips == false) { - ids = new int[] { - // NIST curves first - // prefer NIST P-256, rest in order of increasing key length - 23, 1, 3, 19, 21, 6, 7, 9, 10, 24, 11, 12, 25, 13, 14, - // non-NIST curves - 15, 16, 17, 2, 18, 4, 5, 20, 8, 22, - }; + boolean requireFips = SunJSSE.isFIPS(); + + // hack code to initialize NamedEllipticCurve + NamedEllipticCurve nec = + NamedEllipticCurve.getCurve("secp256r1", false); + + // The value of the System Property defines a list of enabled named + // curves in preference order, separated with comma. For example: + // + // jdk.tls.namedGroups="secp521r1, secp256r1, secp384r1" + // + // If the System Property is not defined or the value is empty, the + // default curves and preferences will be used. + String property = AccessController.doPrivileged( + new GetPropertyAction("jdk.tls.namedGroups")); + if (property != null && property.length() != 0) { + // remove double quote marks from beginning/end of the property + if (property.length() > 1 && property.charAt(0) == '"' && + property.charAt(property.length() - 1) == '"') { + property = property.substring(1, property.length() - 1); + } + } + + ArrayList idList; + if (property != null && property.length() != 0) { // customized curves + String[] curves = property.split(","); + idList = new ArrayList<>(curves.length); + for (String curve : curves) { + curve = curve.trim(); + if (!curve.isEmpty()) { + NamedEllipticCurve namedCurve = + NamedEllipticCurve.getCurve(curve, requireFips); + if (namedCurve != null) { + if (isAvailableCurve(namedCurve.id)) { + idList.add(namedCurve.id); + } + } // ignore unknown curves + } + } + } else { // default curves + int[] ids; + if (requireFips) { + ids = new int[] { + // only NIST curves in FIPS mode + 23, 24, 25, 9, 10, 11, 12, 13, 14, + }; + } else { + ids = new int[] { + // NIST curves first + 23, 24, 25, 9, 10, 11, 12, 13, 14, + // non-NIST curves + 22, + }; + } + + idList = new ArrayList<>(ids.length); + for (int curveId : ids) { + if (isAvailableCurve(curveId)) { + idList.add(curveId); + } + } + } + + if (idList.isEmpty()) { + throw new IllegalArgumentException( + "System property jdk.tls.namedGroups(" + property + ") " + + "contains no supported elliptic curves"); } else { - ids = new int[] { - // same as above, but allow only NIST curves in FIPS mode - 23, 1, 3, 19, 21, 6, 7, 9, 10, 24, 11, 12, 25, 13, 14, - }; + supportedCurveIds = new int[idList.size()]; + int i = 0; + for (Integer id : idList) { + supportedCurveIds[i++] = id; + } } - DEFAULT = new EllipticCurvesExtension(ids); } - private final int[] curveIds; + // check whether the curve is supported by the underlying providers + private static boolean isAvailableCurve(int curveId) { + String oid = idToOidMap.get(curveId); + if (oid != null) { + AlgorithmParameters params = null; + try { + params = JsseJce.getAlgorithmParameters("EC"); + params.init(new ECGenParameterSpec(oid)); + } catch (Exception e) { + return false; + } + + // cache the parameters + idToParams.put(curveId, params); + + return true; + } + + return false; + } private EllipticCurvesExtension(int[] curveIds) { super(ExtensionType.EXT_ELLIPTIC_CURVES); + this.curveIds = curveIds; } EllipticCurvesExtension(HandshakeInStream s, int len) throws IOException { super(ExtensionType.EXT_ELLIPTIC_CURVES); + int k = s.getInt16(); if (((len & 1) != 0) || (k + 2 != len)) { throw new SSLProtocolException("Invalid " + type + " extension"); } + + // Note: unknown curves will be ignored later. curveIds = new int[k >> 1]; for (int i = 0; i < curveIds.length; i++) { curveIds[i] = s.getInt16(); } } + // get the preferred active curve + static int getActiveCurves(AlgorithmConstraints constraints) { + return getPreferredCurve(supportedCurveIds, constraints); + } + + static boolean hasActiveCurves(AlgorithmConstraints constraints) { + return getActiveCurves(constraints) >= 0; + } + + static EllipticCurvesExtension createExtension( + AlgorithmConstraints constraints) { + + ArrayList idList = new ArrayList<>(supportedCurveIds.length); + for (int curveId : supportedCurveIds) { + if (constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + "EC", idToParams.get(curveId))) { + idList.add(curveId); + } + } + + if (!idList.isEmpty()) { + int[] ids = new int[idList.size()]; + int i = 0; + for (Integer id : idList) { + ids[i++] = id; + } + + return new EllipticCurvesExtension(ids); + } + + return null; + } + + // get the preferred activated curve + int getPreferredCurve(AlgorithmConstraints constraints) { + return getPreferredCurve(curveIds, constraints); + } + + // get a preferred activated curve + private static int getPreferredCurve(int[] curves, + AlgorithmConstraints constraints) { + for (int curveId : curves) { + if (constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + "EC", idToParams.get(curveId))) { + return curveId; + } + } + + return -1; + } + boolean contains(int index) { for (int curveId : curveIds) { if (index == curveId) { @@ -88,12 +301,6 @@ return false; } - // Return a reference to the internal curveIds array. - // The caller must NOT modify the contents. - int[] curveIds() { - return curveIds; - } - @Override int length() { return 6 + (curveIds.length << 1); @@ -122,17 +329,9 @@ sb.append(", "); } // first check if it is a known named curve, then try other cases. - String oid = getCurveOid(curveId); - if (oid != null) { - ECParameterSpec spec = JsseJce.getECParameterSpec(oid); - // this toString() output will look nice for the current - // implementation of the ECParameterSpec class in the Sun - // provider, but may not look good for other implementations. - if (spec != null) { - sb.append(spec.toString().split(" ")[0]); - } else { - sb.append(oid); - } + String curveName = getCurveName(curveId); + if (curveName != null) { + sb.append(curveName); } else if (curveId == ARBITRARY_PRIME) { sb.append("arbitrary_explicit_prime_curves"); } else if (curveId == ARBITRARY_CHAR2) { @@ -145,16 +344,15 @@ return sb.toString(); } - // Test whether we support the curve with the given index. + // Test whether the given curve is supported. static boolean isSupported(int index) { - if ((index <= 0) || (index >= NAMED_CURVE_OID_TABLE.length)) { - return false; + for (int curveId : supportedCurveIds) { + if (index == curveId) { + return true; + } } - if (fips == false) { - // in non-FIPS mode, we support all valid indices - return true; - } - return DEFAULT.contains(index); + + return false; } static int getCurveIndex(ECParameterSpec params) { @@ -162,57 +360,32 @@ if (oid == null) { return -1; } - Integer n = curveIndices.get(oid); + Integer n = oidToIdMap.get(oid); return (n == null) ? -1 : n; } static String getCurveOid(int index) { - if ((index > 0) && (index < NAMED_CURVE_OID_TABLE.length)) { - return NAMED_CURVE_OID_TABLE[index]; - } - return null; + return idToOidMap.get(index); } - private static final int ARBITRARY_PRIME = 0xff01; - private static final int ARBITRARY_CHAR2 = 0xff02; - - // See sun.security.util.NamedCurve for the OIDs - private static final String[] NAMED_CURVE_OID_TABLE = new String[] { - null, // (0) unused - "1.3.132.0.1", // (1) sect163k1, NIST K-163 - "1.3.132.0.2", // (2) sect163r1 - "1.3.132.0.15", // (3) sect163r2, NIST B-163 - "1.3.132.0.24", // (4) sect193r1 - "1.3.132.0.25", // (5) sect193r2 - "1.3.132.0.26", // (6) sect233k1, NIST K-233 - "1.3.132.0.27", // (7) sect233r1, NIST B-233 - "1.3.132.0.3", // (8) sect239k1 - "1.3.132.0.16", // (9) sect283k1, NIST K-283 - "1.3.132.0.17", // (10) sect283r1, NIST B-283 - "1.3.132.0.36", // (11) sect409k1, NIST K-409 - "1.3.132.0.37", // (12) sect409r1, NIST B-409 - "1.3.132.0.38", // (13) sect571k1, NIST K-571 - "1.3.132.0.39", // (14) sect571r1, NIST B-571 - "1.3.132.0.9", // (15) secp160k1 - "1.3.132.0.8", // (16) secp160r1 - "1.3.132.0.30", // (17) secp160r2 - "1.3.132.0.31", // (18) secp192k1 - "1.2.840.10045.3.1.1", // (19) secp192r1, NIST P-192 - "1.3.132.0.32", // (20) secp224k1 - "1.3.132.0.33", // (21) secp224r1, NIST P-224 - "1.3.132.0.10", // (22) secp256k1 - "1.2.840.10045.3.1.7", // (23) secp256r1, NIST P-256 - "1.3.132.0.34", // (24) secp384r1, NIST P-384 - "1.3.132.0.35", // (25) secp521r1, NIST P-521 - }; - - private static final Map curveIndices; - - static { - curveIndices = new HashMap(); - for (int i = 1; i < NAMED_CURVE_OID_TABLE.length; i++) { - curveIndices.put(NAMED_CURVE_OID_TABLE[i], i); + static ECGenParameterSpec getECGenParamSpec(int index) { + AlgorithmParameters params = idToParams.get(index); + try { + return params.getParameterSpec(ECGenParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + // should be unlikely + String curveOid = getCurveOid(index); + return new ECGenParameterSpec(curveOid); } } + private static String getCurveName(int index) { + for (NamedEllipticCurve namedCurve : NamedEllipticCurve.values()) { + if (namedCurve.id == index) { + return namedCurve.name; + } + } + + return null; + } } diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Sat Jul 09 05:56:18 2016 +0000 @@ -313,11 +313,6 @@ this.cookie = null; } - if (cipherSuites.containsEC()) { - extensions.add(EllipticCurvesExtension.DEFAULT); - extensions.add(EllipticPointFormatsExtension.DEFAULT); - } - clnt_random = new RandomCookie(generator); compression_methods = NULL_COMPRESSION; } diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java Sat Jul 09 05:56:18 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2016, 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 @@ -660,13 +660,42 @@ ArrayList suites = new ArrayList<>(); if (!(activeProtocols.collection().isEmpty()) && activeProtocols.min.v != ProtocolVersion.NONE.v) { + boolean checkedCurves = false; + boolean hasCurves = false; for (CipherSuite suite : enabledCipherSuites.collection()) { if (!activeProtocols.min.obsoletes(suite) && activeProtocols.max.supports(suite)) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { - suites.add(suite); + + boolean available = true; + if (suite.keyExchange.isEC) { + if (!checkedCurves) { + hasCurves = EllipticCurvesExtension + .hasActiveCurves(algorithmConstraints); + checkedCurves = true; + + if (!hasCurves && debug != null && + Debug.isOn("verbose")) { + System.out.println( + "No available elliptic curves"); + } + } + + available = hasCurves; + + if (!available && debug != null && + Debug.isOn("verbose")) { + System.out.println( + "No active elliptic curves, ignore " + + suite); + } + } + + if (available) { + suites.add(suite); + } } } else if (debug != null && Debug.isOn("verbose")) { if (activeProtocols.min.obsoletes(suite)) { @@ -703,6 +732,8 @@ ProtocolList getActiveProtocols() { if (activeProtocols == null) { boolean enabledSSL20Hello = false; + boolean checkedCurves = false; + boolean hasCurves = false; ArrayList protocols = new ArrayList<>(4); for (ProtocolVersion protocol : enabledProtocols.collection()) { // Need not to check the SSL20Hello protocol. @@ -729,9 +760,36 @@ if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { - protocols.add(protocol); - found = true; - break; + + boolean available = true; + if (suite.keyExchange.isEC) { + if (!checkedCurves) { + hasCurves = EllipticCurvesExtension + .hasActiveCurves(algorithmConstraints); + checkedCurves = true; + + if (!hasCurves && debug != null && + Debug.isOn("verbose")) { + System.out.println( + "No activated elliptic curves"); + } + } + + available = hasCurves; + + if (!available && debug != null && + Debug.isOn("verbose")) { + System.out.println( + "No active elliptic curves, ignore " + + suite + " for " + protocol); + } + } + + if (available) { + protocols.add(protocol); + found = true; + break; + } } else if (debug != null && Debug.isOn("verbose")) { System.out.println( "Ignoring disabled cipher suite: " + suite + diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Sat Jul 09 05:56:18 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2016, 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 @@ -275,6 +275,15 @@ } } + static AlgorithmParameters getAlgorithmParameters(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return AlgorithmParameters.getInstance(algorithm); + } else { + return AlgorithmParameters.getInstance(algorithm, cryptoProvider); + } + } + static SecureRandom getSecureRandom() throws KeyManagementException { if (cryptoProvider == null) { return new SecureRandom(); @@ -394,6 +403,7 @@ JsseJce.getKeyAgreement("ECDH"); JsseJce.getKeyFactory("EC"); JsseJce.getKeyPairGenerator("EC"); + JsseJce.getAlgorithmParameters("EC"); } catch (Exception e) { mediator = false; } diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Sat Jul 09 05:56:18 2016 +0000 @@ -94,7 +94,8 @@ // we remember it for the RSA premaster secret version check private ProtocolVersion clientRequestedVersion; - private EllipticCurvesExtension supportedCurves; + // client supported elliptic curves + private EllipticCurvesExtension requestedCurves; // the preferable signature algorithm used by ServerKeyExchange message SignatureAndHashAlgorithm preferableSignatureAlgorithm; @@ -741,7 +742,7 @@ throw new SSLException("Client did not resume a session"); } - supportedCurves = (EllipticCurvesExtension) + requestedCurves = (EllipticCurvesExtension) mesg.extensions.get(ExtensionType.EXT_ELLIPTIC_CURVES); // We only need to handle the "signature_algorithm" extension @@ -1572,26 +1573,15 @@ // If we cannot continue because we do not support any of the curves that // the client requested, return false. Otherwise (all is well), return true. private boolean setupEphemeralECDHKeys() { - int index = -1; - if (supportedCurves != null) { - // if the client sent the supported curves extension, pick the - // first one that we support; - for (int curveId : supportedCurves.curveIds()) { - if (EllipticCurvesExtension.isSupported(curveId)) { - index = curveId; - break; - } - } - if (index < 0) { - // no match found, cannot use this ciphersuite - return false; - } - } else { - // pick our preference - index = EllipticCurvesExtension.DEFAULT.curveIds()[0]; + int index = (requestedCurves != null) ? + requestedCurves.getPreferredCurve(algorithmConstraints) : + EllipticCurvesExtension.getActiveCurves(algorithmConstraints); + if (index < 0) { + // no match found, cannot use this ciphersuite + return false; } - String oid = EllipticCurvesExtension.getCurveOid(index); - ecdh = new ECDHCrypt(oid, sslContext.getSecureRandom()); + + ecdh = new ECDHCrypt(index, sslContext.getSecureRandom()); return true; } @@ -1633,18 +1623,16 @@ return false; } // For ECC certs, check whether we support the EC domain parameters. - // If the client sent a EllipticCurves ClientHello extension, + // If the client sent a SupportedEllipticCurves ClientHello extension, // check against that too. if (keyAlgorithm.equals("EC")) { if (publicKey instanceof ECPublicKey == false) { return false; } ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); - int index = EllipticCurvesExtension.getCurveIndex(params); - if (!EllipticCurvesExtension.isSupported(index)) { - return false; - } - if ((supportedCurves != null) && !supportedCurves.contains(index)) { + int id = EllipticCurvesExtension.getCurveIndex(params); + if ((id <= 0) || !EllipticCurvesExtension.isSupported(id) || + ((requestedCurves != null) && !requestedCurves.contains(id))) { return false; } } diff -r 672b948cb355 -r 1449ed425710 jdk/src/java.base/share/conf/security/java.security --- a/jdk/src/java.base/share/conf/security/java.security Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/src/java.base/share/conf/security/java.security Sat Jul 09 05:56:18 2016 +0000 @@ -653,7 +653,7 @@ # # jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ - DSA keySize < 1024 + DSA keySize < 1024, EC keySize < 224 # Algorithm restrictions for Secure Socket Layer/Transport Layer Security # (SSL/TLS/DTLS) processing @@ -681,7 +681,8 @@ # # Example: # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 -jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 1024 +jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 1024, \ + EC keySize < 224 # Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) # processing in JSSE implementation. diff -r 672b948cb355 -r 1449ed425710 jdk/test/javax/net/ssl/ciphersuites/ECCurvesconstraints.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/net/ssl/ciphersuites/ECCurvesconstraints.java Sat Jul 09 05:56:18 2016 +0000 @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2016, 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. + */ + +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + +/* + * @test + * @bug 8148516 + * @summary Improve the default strength of EC in JDK + * @run main/othervm ECCurvesconstraints PKIX + * @run main/othervm ECCurvesconstraints SunX509 + */ + +import java.net.*; +import java.util.*; +import java.io.*; +import javax.net.ssl.*; +import java.security.Security; +import java.security.KeyStore; +import java.security.KeyFactory; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.*; +import java.security.interfaces.*; +import java.util.Base64; + + +public class ECCurvesconstraints { + + /* + * ============================================================= + * Set the various variables needed for the tests, then + * specify what tests to run on each side. + */ + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + static boolean separateServerThread = false; + + /* + * Where do we find the keystores? + */ + // Certificates and key used in the test. + // + // EC curve: secp224k1 + static String trustedCertStr = + "-----BEGIN CERTIFICATE-----\n" + + "MIIBCzCBugIEVz2lcjAKBggqhkjOPQQDAjAaMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" + + "ZS5vcmcwHhcNMTYwNTE5MTEzNzM5WhcNMTcwNTE5MTEzNzM5WjAaMRgwFgYDVQQD\n" + + "DA93d3cuZXhhbXBsZS5vcmcwTjAQBgcqhkjOPQIBBgUrgQQAIAM6AAT68uovMZ8f\n" + + "KARn5NOjvieJaq6h8zHYkM9w5DuN0kkOo4KBhke06EkQj0nvQQcSvppTV6RoDLY4\n" + + "djAKBggqhkjOPQQDAgNAADA9AhwMNIujM0R0llpPH6d89d1S3VRGH/78ovc+zw51\n" + + "Ah0AuZ1YlQkUbrJIzkuPSICxz5UfCWPe+7w4as+wiA==\n" + + "-----END CERTIFICATE-----"; + + // Private key in the format of PKCS#8 + static String targetPrivateKey = + "MIGCAgEAMBAGByqGSM49AgEGBSuBBAAgBGswaQIBAQQdAPbckc86mgW/zexB1Ajq\n" + + "38HntWOjdxL6XSoiAsWgBwYFK4EEACChPAM6AAT68uovMZ8fKARn5NOjvieJaq6h\n" + + "8zHYkM9w5DuN0kkOo4KBhke06EkQj0nvQQcSvppTV6RoDLY4dg=="; + + static String[] serverCerts = {trustedCertStr}; + static String[] serverKeys = {targetPrivateKey}; + static String[] clientCerts = {trustedCertStr}; + static String[] clientKeys = {targetPrivateKey}; + + static char passphrase[] = "passphrase".toCharArray(); + + /* + * Is the server ready to serve? + */ + volatile static boolean serverReady = false; + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + SSLContext context = generateSSLContext(false); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + SSLServerSocket sslServerSocket = + (SSLServerSocket)sslssf.createServerSocket(serverPort); + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept(); + try { + sslSocket.setSoTimeout(5000); + sslSocket.setSoLinger(true, 5); + + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslIS.read(); + sslOS.write('A'); + sslOS.flush(); + + throw new Exception("EC curve secp224k1 should be disabled"); + } catch (SSLHandshakeException she) { + // expected exception: no cipher suites in common + System.out.println("Expected exception: " + she); + } finally { + sslSocket.close(); + sslServerSocket.close(); + } + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLContext context = generateSSLContext(true); + SSLSocketFactory sslsf = context.getSocketFactory(); + + SSLSocket sslSocket = + (SSLSocket)sslsf.createSocket("localhost", serverPort); + + try { + sslSocket.setSoTimeout(5000); + sslSocket.setSoLinger(true, 5); + + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslOS.write('B'); + sslOS.flush(); + sslIS.read(); + + throw new Exception("EC curve secp224k1 should be disabled"); + } catch (SSLHandshakeException she) { + // expected exception: Received fatal alert + System.out.println("Expected exception: " + she); + } finally { + sslSocket.close(); + } + } + + /* + * ============================================================= + * The remainder is just support stuff + */ + private static String tmAlgorithm; // trust manager + + private static void parseArguments(String[] args) { + tmAlgorithm = args[0]; + } + + private static SSLContext generateSSLContext(boolean isClient) + throws Exception { + + // generate certificate from cert string + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // create a key store + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + + // import the trused cert + ByteArrayInputStream is = + new ByteArrayInputStream(trustedCertStr.getBytes()); + Certificate trusedCert = cf.generateCertificate(is); + is.close(); + + ks.setCertificateEntry("Export Signer", trusedCert); + + String[] certStrs = null; + String[] keyStrs = null; + if (isClient) { + certStrs = clientCerts; + keyStrs = clientKeys; + } else { + certStrs = serverCerts; + keyStrs = serverKeys; + } + + for (int i = 0; i < certStrs.length; i++) { + // generate the private key. + String keySpecStr = keyStrs[i]; + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(keySpecStr)); + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPrivateKey priKey = + (ECPrivateKey)kf.generatePrivate(priKeySpec); + + // generate certificate chain + String keyCertStr = certStrs[i]; + is = new ByteArrayInputStream(keyCertStr.getBytes()); + Certificate keyCert = cf.generateCertificate(is); + is.close(); + + Certificate[] chain = new Certificate[2]; + chain[0] = keyCert; + chain[1] = trusedCert; + + // import the key entry. + ks.setKeyEntry("key-entry-" + i, priKey, passphrase, chain); + } + + // create SSL context + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm); + tmf.init(ks); + + SSLContext ctx = SSLContext.getInstance("TLS"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509"); + kmf.init(ks, passphrase); + + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + ks = null; + + return ctx; + } + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + public static void main(String[] args) throws Exception { + if (debug) { + System.setProperty("javax.net.debug", "all"); + } + + /* + * Get the customized arguments. + */ + parseArguments(args); + + /* + * Start the tests. + */ + new ECCurvesconstraints(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + ECCurvesconstraints() throws Exception { + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + // swallow for now. Show later + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + String whichRemote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + whichRemote = "server"; + } else { + remote = clientException; + local = serverException; + whichRemote = "client"; + } + + /* + * If both failed, return the curthread's exception, but also + * print the remote side Exception + */ + if ((local != null) && (remote != null)) { + System.out.println(whichRemote + " also threw:"); + remote.printStackTrace(); + System.out.println(); + throw local; + } + + if (remote != null) { + throw remote; + } + + if (local != null) { + throw local; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died, because of " + e); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + serverException = e; + } finally { + serverReady = true; + } + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died, because of " + e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + clientException = e; + } + } + } +} diff -r 672b948cb355 -r 1449ed425710 jdk/test/sun/security/ec/TestEC.java --- a/jdk/test/sun/security/ec/TestEC.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/test/sun/security/ec/TestEC.java Sat Jul 09 05:56:18 2016 +0000 @@ -36,7 +36,7 @@ * @library ../../../java/security/testlibrary * @modules jdk.crypto.pkcs11/sun.security.pkcs11.wrapper * @compile -XDignore.symbol.file TestEC.java - * @run main/othervm TestEC + * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" TestEC */ import java.security.NoSuchProviderException; diff -r 672b948cb355 -r 1449ed425710 jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java --- a/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java Sat Jul 09 05:48:16 2016 +0000 +++ b/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java Sat Jul 09 05:56:18 2016 +0000 @@ -33,8 +33,10 @@ * @author Andreas Sterbenz * @library .. * @library ../../../../java/security/testlibrary - * @run main/othervm ClientJSSEServerJSSE - * @run main/othervm ClientJSSEServerJSSE sm policy + * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" + * ClientJSSEServerJSSE + * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" + * ClientJSSEServerJSSE sm policy */ import java.security.Provider;