changeset 12706:e4cb08798c34

8140436: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for TLS Reviewed-by: valeriep, jnimeh, apetcher
author igerasim
date Thu, 25 May 2017 21:22:55 -0700
parents 85a4c71f2987
children 91d79f61ac37
files src/share/classes/sun/security/ssl/CipherSuite.java src/share/classes/sun/security/ssl/CipherSuiteList.java src/share/classes/sun/security/ssl/ClientHandshaker.java src/share/classes/sun/security/ssl/DHCrypt.java src/share/classes/sun/security/ssl/ECDHCrypt.java src/share/classes/sun/security/ssl/EllipticCurvesExtension.java src/share/classes/sun/security/ssl/ExtensionType.java src/share/classes/sun/security/ssl/HandshakeMessage.java src/share/classes/sun/security/ssl/Handshaker.java src/share/classes/sun/security/ssl/HelloExtensions.java src/share/classes/sun/security/ssl/NamedGroup.java src/share/classes/sun/security/ssl/NamedGroupType.java src/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java src/share/classes/sun/security/ssl/ServerHandshaker.java src/share/classes/sun/security/ssl/SupportedGroupsExtension.java test/javax/net/ssl/templates/SSLSocketTemplate.java test/sun/security/ssl/com/sun/net/ssl/internal/ssl/DHKeyExchange/DHEKeySizing.java test/sun/security/ssl/com/sun/net/ssl/internal/ssl/DHKeyExchange/UseStrongDHSizes.java
diffstat 18 files changed, 2285 insertions(+), 752 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/ssl/CipherSuite.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/CipherSuite.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -42,6 +42,7 @@
 import static sun.security.ssl.CipherSuite.PRF.*;
 import static sun.security.ssl.CipherSuite.CipherType.*;
 import static sun.security.ssl.JsseJce.*;
+import static sun.security.ssl.NamedGroupType.*;
 
 /**
  * An SSL/TLS CipherSuite. Constants for the standard key exchange, cipher,
@@ -326,38 +327,38 @@
     static enum KeyExchange {
 
         // key exchange algorithms
-        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_NULL       ("NULL",       false,      NAMED_GROUP_NONE),
+        K_RSA        ("RSA",        true,       NAMED_GROUP_NONE),
+        K_RSA_EXPORT ("RSA_EXPORT", true,       NAMED_GROUP_NONE),
+        K_DH_RSA     ("DH_RSA",     false,      NAMED_GROUP_NONE),
+        K_DH_DSS     ("DH_DSS",     false,      NAMED_GROUP_NONE),
+        K_DHE_DSS    ("DHE_DSS",    true,       NAMED_GROUP_FFDHE),
+        K_DHE_RSA    ("DHE_RSA",    true,       NAMED_GROUP_FFDHE),
+        K_DH_ANON    ("DH_anon",    true,       NAMED_GROUP_FFDHE),
 
-        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),
+        K_ECDH_ECDSA ("ECDH_ECDSA",  ALLOW_ECC, NAMED_GROUP_ECDHE),
+        K_ECDH_RSA   ("ECDH_RSA",    ALLOW_ECC, NAMED_GROUP_ECDHE),
+        K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC, NAMED_GROUP_ECDHE),
+        K_ECDHE_RSA  ("ECDHE_RSA",   ALLOW_ECC, NAMED_GROUP_ECDHE),
+        K_ECDH_ANON  ("ECDH_anon",   ALLOW_ECC, NAMED_GROUP_ECDHE),
 
         // Kerberos cipher suites
-        K_KRB5       ("KRB5", true,             false),
-        K_KRB5_EXPORT("KRB5_EXPORT", true,      false),
+        K_KRB5       ("KRB5", true,             NAMED_GROUP_NONE),
+        K_KRB5_EXPORT("KRB5_EXPORT", true,      NAMED_GROUP_NONE),
 
         // renegotiation protection request signaling cipher suite
-        K_SCSV       ("SCSV",        true,      false);
+        K_SCSV       ("SCSV",        true,      NAMED_GROUP_NONE);
 
         // name of the key exchange algorithm, e.g. DHE_DSS
         final String name;
         final boolean allowed;
-        final boolean isEC;
+        final NamedGroupType groupType;
         private final boolean alwaysAvailable;
 
-        KeyExchange(String name, boolean allowed, boolean isEC) {
+        KeyExchange(String name, boolean allowed, NamedGroupType groupType) {
             this.name = name;
             this.allowed = allowed;
-            this.isEC = isEC;
+            this.groupType = groupType;
             this.alwaysAvailable = allowed &&
                 (!name.startsWith("EC")) && (!name.startsWith("KRB"));
         }
@@ -367,7 +368,7 @@
                 return true;
             }
 
-            if (isEC) {
+            if (groupType == NAMED_GROUP_ECDHE) {
                 return (allowed && JsseJce.isEcAvailable());
             } else if (name.startsWith("KRB")) {
                 return (allowed && JsseJce.isKerberosAvailable());
--- a/src/share/classes/sun/security/ssl/CipherSuiteList.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/CipherSuiteList.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -30,6 +30,7 @@
 import java.util.*;
 
 import javax.net.ssl.SSLException;
+import static sun.security.ssl.NamedGroupType.*;
 
 /**
  * A list of CipherSuites. Also maintains the lists of supported and
@@ -42,15 +43,16 @@
 
     private final Collection<CipherSuite> cipherSuites;
     private String[] suiteNames;
-
-    // flag indicating whether this list contains any ECC ciphersuites.
-    // null if not yet checked.
-    private volatile Boolean containsEC;
+    private final EnumSet<NamedGroupType> groupsTypes =
+            EnumSet.noneOf(NamedGroupType.class);
 
     // for use by buildAvailableCache() and
     // Handshaker.getKickstartMessage() only
     CipherSuiteList(Collection<CipherSuite> cipherSuites) {
         this.cipherSuites = cipherSuites;
+        for (CipherSuite suite : cipherSuites) {
+            updateGroupTypes(suite);
+        }
     }
 
     /**
@@ -59,6 +61,7 @@
     CipherSuiteList(CipherSuite suite) {
         cipherSuites = new ArrayList<CipherSuite>(1);
         cipherSuites.add(suite);
+        updateGroupTypes(suite);
     }
 
     /**
@@ -82,6 +85,7 @@
                     + suiteName + " with currently installed providers");
             }
             cipherSuites.add(suite);
+            updateGroupTypes(suite);
         }
     }
 
@@ -97,7 +101,20 @@
         }
         cipherSuites = new ArrayList<CipherSuite>(bytes.length >> 1);
         for (int i = 0; i < bytes.length; i += 2) {
-            cipherSuites.add(CipherSuite.valueOf(bytes[i], bytes[i+1]));
+            CipherSuite suite = CipherSuite.valueOf(bytes[i], bytes[i+1]);
+            cipherSuites.add(suite);
+            updateGroupTypes(suite);
+        }
+    }
+
+    // Please don't use this method except constructors.
+    private void updateGroupTypes(CipherSuite cipherSuite) {
+        if (cipherSuite.keyExchange != null && (!cipherSuite.exportable)) {
+            NamedGroupType groupType = cipherSuite.keyExchange.groupType;
+            if ((groupType != NAMED_GROUP_NONE) &&
+                    (!groupsTypes.contains(groupType))) {
+                groupsTypes.add(groupType);
+            }
         }
     }
 
@@ -108,20 +125,9 @@
         return cipherSuites.contains(suite);
     }
 
-    // Return whether this list contains any ECC ciphersuites
-    boolean containsEC() {
-        if (containsEC == null) {
-            for (CipherSuite c : cipherSuites) {
-                if (c.keyExchange.isEC) {
-                    containsEC = true;
-                    return true;
-                }
-            }
-
-            containsEC = false;
-        }
-
-        return containsEC;
+    // Return whether this list contains cipher suites of a named group type.
+    boolean contains(NamedGroupType groupType) {
+        return groupsTypes.contains(groupType);
     }
 
     /**
--- a/src/share/classes/sun/security/ssl/ClientHandshaker.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/ClientHandshaker.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -141,6 +141,10 @@
     private final static boolean allowUnsafeServerCertChange =
         Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false);
 
+    // To switch off the supported_groups extension for DHE cipher suite.
+    private static final boolean enableFFDHE =
+            Debug.getBooleanProperty("jsse.enableFFDHE", true);
+
     private List<SNIServerName> requestedServerNames =
             Collections.<SNIServerName>emptyList();
 
@@ -649,10 +653,12 @@
             ExtensionType type = ext.type;
             if (type == ExtensionType.EXT_SERVER_NAME) {
                 serverNamesAccepted = true;
-            } else if ((type != ExtensionType.EXT_ELLIPTIC_CURVES)
+            } else if ((type != ExtensionType.EXT_SUPPORTED_GROUPS)
                     && (type != ExtensionType.EXT_EC_POINT_FORMATS)
                     && (type != ExtensionType.EXT_SERVER_NAME)
                     && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) {
+                // Note: Better to check client requested extensions rather
+                // than all supported extensions.
                 fatalSE(Alerts.alert_unsupported_extension,
                     "Server sent an unsupported extension: " + type);
             }
@@ -701,6 +707,17 @@
      * our own D-H algorithm object so we can defer key calculations
      * until after we've sent the client key exchange message (which
      * gives client and server some useful parallelism).
+     *
+     * Note per section 3 of RFC 7919, if the server is not compatible with
+     * FFDHE specification, the client MAY decide to continue the connection
+     * if the selected DHE group is acceptable under local policy, or it MAY
+     * decide to terminate the connection with a fatal insufficient_security
+     * (71) alert.  The algorithm constraints mechanism is JDK local policy
+     * used for additional DHE parameters checking.  So this implementation
+     * does not check the server compatibility and just pass to the local
+     * algorithm constraints checking.  The client will continue the
+     * connection if the server selected DHE group is acceptable by the
+     * specified algorithm constraints.
      */
     private void serverKeyExchange(DH_ServerKeyExchange mesg)
             throws IOException {
@@ -1379,14 +1396,17 @@
                 sslContext.getSecureRandom(), maxProtocolVersion,
                 sessionId, cipherSuites);
 
-        // add elliptic curves and point format extensions
-        if (cipherSuites.containsEC()) {
-            EllipticCurvesExtension ece =
-                EllipticCurvesExtension.createExtension(algorithmConstraints);
-            if (ece != null) {
-                clientHelloMessage.extensions.add(ece);
+        // Add named groups extension for ECDHE and FFDHE if necessary.
+        SupportedGroupsExtension sge =
+                SupportedGroupsExtension.createExtension(
+                        algorithmConstraints,
+                        cipherSuites, enableFFDHE);
+        if (sge != null) {
+            clientHelloMessage.extensions.add(sge);
+            // Add elliptic point format extensions
+            if (cipherSuites.contains(NamedGroupType.NAMED_GROUP_ECDHE)) {
                 clientHelloMessage.extensions.add(
-                   EllipticPointFormatsExtension.DEFAULT);
+                    EllipticPointFormatsExtension.DEFAULT);
             }
         }
 
--- a/src/share/classes/sun/security/ssl/DHCrypt.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/DHCrypt.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -26,14 +26,8 @@
 
 package sun.security.ssl;
 
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
 import java.math.BigInteger;
 import java.security.*;
-import java.io.IOException;
 import javax.net.ssl.SSLHandshakeException;
 import javax.crypto.SecretKey;
 import javax.crypto.KeyAgreement;
@@ -101,7 +95,7 @@
      */
     DHCrypt(int keyLength, SecureRandom random) {
         this(keyLength,
-                ParametersHolder.definedParams.get(keyLength), random);
+            PredefinedDHParameterSpecs.definedParams.get(keyLength), random);
     }
 
     /**
@@ -116,6 +110,14 @@
     }
 
     /**
+     * Generate a Diffie-Hellman keypair using the named group.
+     */
+    DHCrypt(NamedGroup namedGroup, SecureRandom random) {
+        this(-1,        // The length (-1) is not used in the implementation.
+            SupportedGroupsExtension.getDHParameterSpec(namedGroup), random);
+    }
+
+    /**
      * Generate a Diffie-Hellman keypair using the specified size and
      * parameters.
      */
@@ -272,141 +274,4 @@
 
         return null;
     }
-
-    // lazy initialization holder class idiom for static default parameters
-    //
-    // See Effective Java Second Edition: Item 71.
-    private static class ParametersHolder {
-        private final static boolean debugIsOn =
-                (Debug.getInstance("ssl") != null) && Debug.isOn("sslctx");
-
-        //
-        // Default DH ephemeral parameters
-        //
-        private static final BigInteger g2 = BigInteger.valueOf(2);
-
-        private static final BigInteger p512 = new BigInteger(   // generated
-                "D87780E15FF50B4ABBE89870188B049406B5BEA98AB23A02" +
-                "41D88EA75B7755E669C08093D3F0CA7FC3A5A25CF067DCB9" +
-                "A43DD89D1D90921C6328884461E0B6D3", 16);
-        private static final BigInteger p768 = new BigInteger(   // RFC 2409
-                "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
-                "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
-                "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
-                "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16);
-
-        private static final BigInteger p1024 = new BigInteger(  // RFC 2409
-                "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
-                "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
-                "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
-                "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
-                "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" +
-                "FFFFFFFFFFFFFFFF", 16);
-        private static final BigInteger p2048 = new BigInteger(  // TLS FEDHE
-                "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
-                "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
-                "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
-                "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
-                "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
-                "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
-                "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
-                "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
-                "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
-                "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
-                "886B423861285C97FFFFFFFFFFFFFFFF", 16);
-
-        private static final BigInteger[] supportedPrimes = {
-                p512, p768, p1024, p2048};
-
-        // a measure of the uncertainty that prime modulus p is not a prime
-        //
-        // see BigInteger.isProbablePrime(int certainty)
-        private final static int PRIME_CERTAINTY = 120;
-
-        // the known security property, jdk.tls.server.defaultDHEParameters
-        private final static String PROPERTY_NAME =
-                "jdk.tls.server.defaultDHEParameters";
-
-        private static final Pattern spacesPattern = Pattern.compile("\\s+");
-
-        private final static Pattern syntaxPattern = Pattern.compile(
-                "(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
-                "(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
-
-        private static final Pattern paramsPattern = Pattern.compile(
-                "\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
-
-        // cache of predefined default DH ephemeral parameters
-        private final static Map<Integer,DHParameterSpec> definedParams;
-
-        static {
-            String property = AccessController.doPrivileged(
-                new PrivilegedAction<String>() {
-                    public String run() {
-                        return Security.getProperty(PROPERTY_NAME);
-                    }
-                });
-
-            if (property != null && !property.isEmpty()) {
-                // remove double quote marks from beginning/end of the property
-                if (property.length() >= 2 && property.charAt(0) == '"' &&
-                        property.charAt(property.length() - 1) == '"') {
-                    property = property.substring(1, property.length() - 1);
-                }
-
-                property = property.trim();
-            }
-
-            if (property != null && !property.isEmpty()) {
-                Matcher spacesMatcher = spacesPattern.matcher(property);
-                property = spacesMatcher.replaceAll("");
-
-                if (debugIsOn) {
-                    System.out.println("The Security Property " +
-                            PROPERTY_NAME + ": " + property);
-                }
-            }
-
-            Map<Integer,DHParameterSpec> defaultParams = new HashMap<>();
-            if (property != null && !property.isEmpty()) {
-                Matcher syntaxMatcher = syntaxPattern.matcher(property);
-                if (syntaxMatcher.matches()) {
-                    Matcher paramsFinder = paramsPattern.matcher(property);
-                    while(paramsFinder.find()) {
-                        String primeModulus = paramsFinder.group(1);
-                        BigInteger p = new BigInteger(primeModulus, 16);
-                        if (!p.isProbablePrime(PRIME_CERTAINTY)) {
-                            if (debugIsOn) {
-                                System.out.println(
-                                    "Prime modulus p in Security Property, " +
-                                    PROPERTY_NAME + ", is not a prime: " +
-                                    primeModulus);
-                            }
-
-                            continue;
-                        }
-
-                        String baseGenerator = paramsFinder.group(2);
-                        BigInteger g = new BigInteger(baseGenerator, 16);
-
-                        DHParameterSpec spec = new DHParameterSpec(p, g);
-                        int primeLen = p.bitLength();
-                        defaultParams.put(primeLen, spec);
-                    }
-                } else if (debugIsOn) {
-                    System.out.println("Invalid Security Property, " +
-                            PROPERTY_NAME + ", definition");
-                }
-            }
-
-            for (BigInteger p : supportedPrimes) {
-                int primeLen = p.bitLength();
-                defaultParams.putIfAbsent(primeLen, new DHParameterSpec(p, g2));
-            }
-
-            definedParams =
-                    Collections.<Integer,DHParameterSpec>unmodifiableMap(
-                                                                defaultParams);
-        }
-    }
 }
--- a/src/share/classes/sun/security/ssl/ECDHCrypt.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/ECDHCrypt.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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,17 +56,17 @@
     }
 
     // Called by ServerHandshaker for ephemeral ECDH
-    ECDHCrypt(int curveId, SecureRandom random) {
+    ECDHCrypt(NamedGroup namedGroup, SecureRandom random) {
         try {
             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
             ECGenParameterSpec params =
-                    EllipticCurvesExtension.getECGenParamSpec(curveId);
+                    SupportedGroupsExtension.getECGenParamSpec(namedGroup);
             kpg.initialize(params, random);
             KeyPair kp = kpg.generateKeyPair();
             privateKey = kp.getPrivate();
             publicKey = (ECPublicKey)kp.getPublic();
         } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate DH keypair", e);
+            throw new RuntimeException("Could not generate ECDH keypair", e);
         }
     }
 
@@ -79,7 +79,7 @@
             privateKey = kp.getPrivate();
             publicKey = (ECPublicKey)kp.getPublic();
         } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate DH keypair", e);
+            throw new RuntimeException("Could not generate ECDH keypair", e);
         }
     }
 
--- a/src/share/classes/sun/security/ssl/EllipticCurvesExtension.java	Thu May 25 21:21:06 2017 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,397 +0,0 @@
-/*
- * Copyright (c) 2006, 2017, 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.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 sun.security.action.GetPropertyAction;
-
-final class EllipticCurvesExtension extends HelloExtension {
-
-    /* Class and subclass dynamic debugging support */
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    private static final int ARBITRARY_PRIME = 0xff01;
-    private static final int ARBITRARY_CHAR2 = 0xff02;
-
-    // speed up the searching
-    private static final Map<String, Integer> oidToIdMap = new HashMap<>();
-    private static final Map<Integer, String> idToOidMap = new HashMap<>();
-
-    // speed up the parameters construction
-    private static final Map<Integer,
-                AlgorithmParameters> 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
-
-        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 {
-        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<Integer> 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
-                }
-            }
-            if (idList.isEmpty() && JsseJce.isEcAvailable()) {
-                throw new IllegalArgumentException(
-                    "System property jdk.tls.namedGroups(" + property + ") " +
-                    "contains no supported elliptic 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 (debug != null && idList.isEmpty()) {
-            debug.println(
-                "Initialized [jdk.tls.namedGroups|default] list contains " +
-                "no available elliptic curves. " +
-                (property != null ? "(" + property + ")" : "[Default]"));
-        }
-
-            supportedCurveIds = new int[idList.size()];
-            int i = 0;
-            for (Integer id : idList) {
-                supportedCurveIds[i++] = id;
-            }
-        }
-
-    // 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<Integer> 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 (isSupported(curveId) && 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) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    int length() {
-        return 6 + (curveIds.length << 1);
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        int k = curveIds.length << 1;
-        s.putInt16(k + 2);
-        s.putInt16(k);
-        for (int curveId : curveIds) {
-            s.putInt16(curveId);
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Extension " + type + ", curve names: {");
-        boolean first = true;
-        for (int curveId : curveIds) {
-            if (first) {
-                first = false;
-            } else {
-                sb.append(", ");
-            }
-            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) {
-                sb.append("arbitrary_explicit_char2_curves");
-            } else {
-                sb.append("unknown curve " + curveId);
-            }
-        }
-        sb.append("}");
-        return sb.toString();
-    }
-
-    // Test whether the given curve is supported.
-    static boolean isSupported(int index) {
-        for (int curveId : supportedCurveIds) {
-            if (index == curveId) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    static int getCurveIndex(ECParameterSpec params) {
-        String oid = JsseJce.getNamedCurveOid(params);
-        if (oid == null) {
-            return -1;
-        }
-        Integer n = oidToIdMap.get(oid);
-        return (n == null) ? -1 : n;
-    }
-
-    static String getCurveOid(int index) {
-        return idToOidMap.get(index);
-    }
-
-    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;
-    }
-}
--- a/src/share/classes/sun/security/ssl/ExtensionType.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/ExtensionType.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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
@@ -43,7 +43,7 @@
         return name;
     }
 
-    static List<ExtensionType> knownExtensions = new ArrayList<ExtensionType>(9);
+    static List<ExtensionType> knownExtensions = new ArrayList<>(13);
 
     static ExtensionType get(int id) {
         for (ExtensionType ext : knownExtensions) {
@@ -61,42 +61,42 @@
     }
 
     // extensions defined in RFC 3546
-    final static ExtensionType EXT_SERVER_NAME =
+    static final ExtensionType EXT_SERVER_NAME =
             e(0x0000, "server_name");            // IANA registry value: 0
-    final static ExtensionType EXT_MAX_FRAGMENT_LENGTH =
+    static final ExtensionType EXT_MAX_FRAGMENT_LENGTH =
             e(0x0001, "max_fragment_length");    // IANA registry value: 1
-    final static ExtensionType EXT_CLIENT_CERTIFICATE_URL =
+    static final ExtensionType EXT_CLIENT_CERTIFICATE_URL =
             e(0x0002, "client_certificate_url"); // IANA registry value: 2
-    final static ExtensionType EXT_TRUSTED_CA_KEYS =
+    static final ExtensionType EXT_TRUSTED_CA_KEYS =
             e(0x0003, "trusted_ca_keys");        // IANA registry value: 3
-    final static ExtensionType EXT_TRUNCATED_HMAC =
+    static final ExtensionType EXT_TRUNCATED_HMAC =
             e(0x0004, "truncated_hmac");         // IANA registry value: 4
-    final static ExtensionType EXT_STATUS_REQUEST =
+    static final ExtensionType EXT_STATUS_REQUEST =
             e(0x0005, "status_request");         // IANA registry value: 5
 
     // extensions defined in RFC 4681
-    final static ExtensionType EXT_USER_MAPPING =
+    static final ExtensionType EXT_USER_MAPPING =
             e(0x0006, "user_mapping");           // IANA registry value: 6
 
     // extensions defined in RFC 5081
-    final static ExtensionType EXT_CERT_TYPE =
+    static final ExtensionType EXT_CERT_TYPE =
             e(0x0009, "cert_type");              // IANA registry value: 9
 
-    // extensions defined in RFC 4492 (ECC)
-    final static ExtensionType EXT_ELLIPTIC_CURVES =
-            e(0x000A, "elliptic_curves");        // IANA registry value: 10
-    final static ExtensionType EXT_EC_POINT_FORMATS =
+    // extensions defined in RFC 4492 (ECC) and RFC 7919 (FFDHE)
+    static final ExtensionType EXT_SUPPORTED_GROUPS =
+            e(0x000A, "supported_groups");       // IANA registry value: 10
+    static final ExtensionType EXT_EC_POINT_FORMATS =
             e(0x000B, "ec_point_formats");       // IANA registry value: 11
 
     // extensions defined in RFC 5054
-    final static ExtensionType EXT_SRP =
+    static final ExtensionType EXT_SRP =
             e(0x000C, "srp");                    // IANA registry value: 12
 
     // extensions defined in RFC 5246
-    final static ExtensionType EXT_SIGNATURE_ALGORITHMS =
+    static final ExtensionType EXT_SIGNATURE_ALGORITHMS =
             e(0x000D, "signature_algorithms");   // IANA registry value: 13
 
     // extensions defined in RFC 5746
-    final static ExtensionType EXT_RENEGOTIATION_INFO =
+    static final ExtensionType EXT_RENEGOTIATION_INFO =
             e(0xff01, "renegotiation_info");     // IANA registry value: 65281
 }
--- a/src/share/classes/sun/security/ssl/HandshakeMessage.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/HandshakeMessage.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -982,8 +982,9 @@
     private final static int CURVE_EXPLICIT_CHAR2 = 2;
     private final static int CURVE_NAMED_CURVE    = 3;
 
-    // id of the curve we are using
-    private int curveId;
+    // id of the named group we are using
+    private int groupId;
+
     // encoded public point
     private byte[] pointBytes;
 
@@ -1002,7 +1003,8 @@
     ECDH_ServerKeyExchange(ECDHCrypt obj, PrivateKey privateKey,
             byte[] clntNonce, byte[] svrNonce, SecureRandom sr,
             SignatureAndHashAlgorithm signAlgorithm,
-            ProtocolVersion protocolVersion) throws GeneralSecurityException {
+            ProtocolVersion protocolVersion)
+            throws SSLHandshakeException, GeneralSecurityException {
 
         this.protocolVersion = protocolVersion;
 
@@ -1010,7 +1012,14 @@
         ECParameterSpec params = publicKey.getParams();
         ECPoint point = publicKey.getW();
         pointBytes = JsseJce.encodePoint(point, params.getCurve());
-        curveId = EllipticCurvesExtension.getCurveIndex(params);
+
+        NamedGroup namedGroup = NamedGroup.valueOf(params);
+        if ((namedGroup == null) || (namedGroup.oid == null) ){
+            // unlikely
+            throw new SSLHandshakeException(
+                "Unnamed EC parameter spec: " + params);
+        }
+        groupId = namedGroup.id;
 
         if (privateKey == null) {
             // ECDH_anon
@@ -1047,20 +1056,27 @@
         // These parsing errors should never occur as we negotiated
         // the supported curves during the exchange of the Hello messages.
         if (curveType == CURVE_NAMED_CURVE) {
-            curveId = input.getInt16();
-            if (!EllipticCurvesExtension.isSupported(curveId)) {
+            groupId = input.getInt16();
+            NamedGroup namedGroup = NamedGroup.valueOf(groupId);
+            if (namedGroup == null) {
                 throw new SSLHandshakeException(
-                    "Unsupported curveId: " + curveId);
+                    "Unknown named group ID: " + groupId);
             }
-            String curveOid = EllipticCurvesExtension.getCurveOid(curveId);
-            if (curveOid == null) {
+
+            if (!SupportedGroupsExtension.supports(namedGroup)) {
                 throw new SSLHandshakeException(
-                    "Unknown named curve: " + curveId);
+                    "Unsupported named group: " + namedGroup);
             }
-            parameters = JsseJce.getECParameterSpec(curveOid);
+
+            if (namedGroup.oid == null) {
+                throw new SSLHandshakeException(
+                    "Unknown named EC curve: " + namedGroup);
+            }
+
+            parameters = JsseJce.getECParameterSpec(namedGroup.oid);
             if (parameters == null) {
                 throw new SSLHandshakeException(
-                    "Unsupported curve: " + curveOid);
+                    "No supported EC parameter for named group: " + namedGroup);
             }
         } else {
             throw new SSLHandshakeException(
@@ -1143,8 +1159,8 @@
         sig.update(svrNonce);
 
         sig.update((byte)CURVE_NAMED_CURVE);
-        sig.update((byte)(curveId >> 8));
-        sig.update((byte)curveId);
+        sig.update((byte)(groupId >> 8));
+        sig.update((byte)groupId);
         sig.update((byte)pointBytes.length);
         sig.update(pointBytes);
     }
@@ -1165,7 +1181,7 @@
     @Override
     void send(HandshakeOutStream s) throws IOException {
         s.putInt8(CURVE_NAMED_CURVE);
-        s.putInt16(curveId);
+        s.putInt16(groupId);
         s.putBytes8(pointBytes);
 
         if (signatureBytes != null) {
--- a/src/share/classes/sun/security/ssl/Handshaker.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/Handshaker.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -50,6 +50,7 @@
 
 import static sun.security.ssl.CipherSuite.PRF.*;
 import static sun.security.ssl.CipherSuite.CipherType.*;
+import static sun.security.ssl.NamedGroupType.*;
 
 /**
  * Handshaker ... processes handshake records from an SSL V3.0
@@ -636,41 +637,14 @@
             ArrayList<CipherSuite> suites = new ArrayList<>();
             if (!(activeProtocols.collection().isEmpty()) &&
                     activeProtocols.min.v != ProtocolVersion.NONE.v) {
-                boolean checkedCurves = false;
-                boolean hasCurves = false;
+                Map<NamedGroupType, Boolean> cachedStatus =
+                        new EnumMap<>(NamedGroupType.class);
                 for (CipherSuite suite : enabledCipherSuites.collection()) {
-                    if (suite.obsoleted > activeProtocols.min.v &&
-                            suite.supported <= activeProtocols.max.v) {
-                        if (algorithmConstraints.permits(
-                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                suite.name, null)) {
-                            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);
-                            }
+                    if (suite.isAvailable() &&
+                            (suite.obsoleted > activeProtocols.min.v &&
+                            suite.supported <= activeProtocols.max.v)) {
+                        if (isActivatable(suite, cachedStatus)) {
+                            suites.add(suite);
                         }
                     } else if (debug != null && Debug.isOn("verbose")) {
                         if (suite.obsoleted <= activeProtocols.min.v) {
@@ -728,46 +702,15 @@
                     continue;
                 }
                 boolean found = false;
+                Map<NamedGroupType, Boolean> cachedStatus =
+                        new EnumMap<>(NamedGroupType.class);
                 for (CipherSuite suite : enabledCipherSuites.collection()) {
                     if (suite.isAvailable() && suite.obsoleted > protocol.v &&
                                                suite.supported <= protocol.v) {
-                        if (algorithmConstraints.permits(
-                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                suite.name, null)) {
-
-                            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 +
-                                 " for " + protocol);
+                        if (isActivatable(suite, cachedStatus)) {
+                            protocols.add(protocol);
+                            found = true;
+                            break;
                         }
                     } else if (debug != null && Debug.isOn("verbose")) {
                         System.out.println(
@@ -775,6 +718,7 @@
                                  " for " + protocol);
                     }
                 }
+
                 if (!found && (debug != null) && Debug.isOn("handshake")) {
                     System.out.println(
                         "No available cipher suite for " + protocol);
@@ -791,6 +735,43 @@
         return activeProtocols;
     }
 
+    private boolean isActivatable(CipherSuite suite,
+            Map<NamedGroupType, Boolean> cachedStatus) {
+
+        if (algorithmConstraints.permits(
+                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
+            boolean available = true;
+            NamedGroupType groupType = suite.keyExchange.groupType;
+            if (groupType != NAMED_GROUP_NONE) {
+                Boolean checkedStatus = cachedStatus.get(groupType);
+                if (checkedStatus == null) {
+                    available = SupportedGroupsExtension.isActivatable(
+                            algorithmConstraints, groupType);
+                    cachedStatus.put(groupType, available);
+
+                    if (!available && debug != null && Debug.isOn("verbose")) {
+                        System.out.println("No activated named group");
+                    }
+                } else {
+                    available = checkedStatus.booleanValue();
+                }
+
+                if (!available && debug != null && Debug.isOn("verbose")) {
+                    System.out.println(
+                        "No active named group, ignore " + suite);
+                }
+
+                return available;
+            } else {
+                return true;
+            }
+        } else if (debug != null && Debug.isOn("verbose")) {
+            System.out.println("Ignoring disabled cipher suite: " + suite);
+        }
+
+        return false;
+    }
+
     /**
      * As long as handshaking has not activated, we can
      * change whether session creations are allowed.
--- a/src/share/classes/sun/security/ssl/HelloExtensions.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/HelloExtensions.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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
@@ -49,7 +49,7 @@
  *      explicitly support.
  *  . ServerNameExtension: the server_name extension.
  *  . SignatureAlgorithmsExtension: the signature_algorithms extension.
- *  . EllipticCurvesExtension: the ECC supported curves extension.
+ *  . SupportedGroupsExtension: the supported groups extension.
  *  . EllipticPointFormatsExtension: the ECC supported point formats
  *      (compressed/uncompressed) extension.
  *
@@ -78,8 +78,8 @@
                 extension = new ServerNameExtension(s, extlen);
             } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) {
                 extension = new SignatureAlgorithmsExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_ELLIPTIC_CURVES) {
-                extension = new EllipticCurvesExtension(s, extlen);
+            } else if (extType == ExtensionType.EXT_SUPPORTED_GROUPS) {
+                extension = new SupportedGroupsExtension(s, extlen);
             } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) {
                 extension = new EllipticPointFormatsExtension(s, extlen);
             } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/NamedGroup.java	Thu May 25 21:22:55 2017 -0700
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017, 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.spec.ECParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import static sun.security.ssl.NamedGroupType.*;
+
+enum NamedGroup {
+    // Elliptic Curves (RFC 4492)
+    //
+    // See sun.security.util.CurveDB for the OIDs
+
+    // NIST K-163
+    SECT163_K1(1, NAMED_GROUP_ECDHE, "sect163k1", "1.3.132.0.1", true),
+
+    SECT163_R1(2, NAMED_GROUP_ECDHE, "sect163r1", "1.3.132.0.2", false),
+
+    // NIST B-163
+    SECT163_R2(3, NAMED_GROUP_ECDHE, "sect163r2", "1.3.132.0.15", true),
+
+    SECT193_R1(4, NAMED_GROUP_ECDHE, "sect193r1", "1.3.132.0.24", false),
+    SECT193_R2(5, NAMED_GROUP_ECDHE, "sect193r2", "1.3.132.0.25", false),
+
+    // NIST K-233
+    SECT233_K1(6, NAMED_GROUP_ECDHE, "sect233k1", "1.3.132.0.26", true),
+
+    // NIST B-233
+    SECT233_R1(7, NAMED_GROUP_ECDHE, "sect233r1", "1.3.132.0.27", true),
+
+    SECT239_K1(8, NAMED_GROUP_ECDHE, "sect239k1", "1.3.132.0.3", false),
+
+    // NIST K-283
+    SECT283_K1(9, NAMED_GROUP_ECDHE, "sect283k1", "1.3.132.0.16", true),
+
+    // NIST B-283
+    SECT283_R1(10, NAMED_GROUP_ECDHE, "sect283r1", "1.3.132.0.17", true),
+
+    // NIST K-409
+    SECT409_K1(11, NAMED_GROUP_ECDHE, "sect409k1", "1.3.132.0.36", true),
+
+    // NIST B-409
+    SECT409_R1(12, NAMED_GROUP_ECDHE, "sect409r1", "1.3.132.0.37", true),
+
+    // NIST K-571
+    SECT571_K1(13, NAMED_GROUP_ECDHE, "sect571k1", "1.3.132.0.38", true),
+
+    // NIST B-571
+    SECT571_R1(14, NAMED_GROUP_ECDHE, "sect571r1", "1.3.132.0.39", true),
+
+    SECP160_K1(15, NAMED_GROUP_ECDHE, "secp160k1", "1.3.132.0.9", false),
+    SECP160_R1(16, NAMED_GROUP_ECDHE, "secp160r1", "1.3.132.0.8", false),
+    SECP160_R2(17, NAMED_GROUP_ECDHE, "secp160r2", "1.3.132.0.30", false),
+    SECP192_K1(18, NAMED_GROUP_ECDHE, "secp192k1", "1.3.132.0.31", false),
+
+    // NIST P-192
+    SECP192_R1(19, NAMED_GROUP_ECDHE, "secp192r1", "1.2.840.10045.3.1.1", true),
+
+    SECP224_K1(20, NAMED_GROUP_ECDHE, "secp224k1", "1.3.132.0.32", false),
+    // NIST P-224
+    SECP224_R1(21, NAMED_GROUP_ECDHE, "secp224r1", "1.3.132.0.33", true),
+
+    SECP256_K1(22, NAMED_GROUP_ECDHE, "secp256k1", "1.3.132.0.10", false),
+
+    // NIST P-256
+    SECP256_R1(23, NAMED_GROUP_ECDHE, "secp256r1", "1.2.840.10045.3.1.7", true),
+
+    // NIST P-384
+    SECP384_R1(24, NAMED_GROUP_ECDHE, "secp384r1", "1.3.132.0.34", true),
+
+    // NIST P-521
+    SECP521_R1(25, NAMED_GROUP_ECDHE, "secp521r1", "1.3.132.0.35", true),
+
+    // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
+    FFDHE_2048(256, NAMED_GROUP_FFDHE, "ffdhe2048",  true),
+    FFDHE_3072(257, NAMED_GROUP_FFDHE, "ffdhe3072",  true),
+    FFDHE_4096(258, NAMED_GROUP_FFDHE, "ffdhe4096",  true),
+    FFDHE_6144(259, NAMED_GROUP_FFDHE, "ffdhe6144",  true),
+    FFDHE_8192(260, NAMED_GROUP_FFDHE, "ffdhe8192",  true);
+
+    int             id;
+    NamedGroupType  type;
+    String          name;
+    String          oid;
+    String          algorithm;
+    boolean         isFips;
+
+    // Constructor used for Elliptic Curve Groups (ECDHE)
+    NamedGroup(int id, NamedGroupType type,
+                String name, String oid, boolean isFips) {
+        this.id = id;
+        this.type = type;
+        this.name = name;
+        this.oid = oid;
+        this.algorithm = "EC";
+        this.isFips = isFips;
+    }
+
+    // Constructor used for Finite Field Diffie-Hellman Groups (FFDHE)
+    NamedGroup(int id, NamedGroupType type, String name, boolean isFips) {
+        this.id = id;
+        this.type = type;
+        this.name = name;
+        this.oid = null;
+        this.algorithm = "DiffieHellman";
+        this.isFips = isFips;
+    }
+
+    static NamedGroup valueOf(int id) {
+        for (NamedGroup group : NamedGroup.values()) {
+            if (group.id == id) {
+                return group;
+            }
+        }
+
+        return null;
+    }
+
+    static NamedGroup nameOf(String name) {
+        for (NamedGroup group : NamedGroup.values()) {
+            if (group.name.equals(name)) {
+                return group;
+            }
+        }
+
+        return null;
+    }
+
+    static NamedGroup valueOf(ECParameterSpec params) {
+        String oid = JsseJce.getNamedCurveOid(params);
+        if ((oid != null) && (!oid.isEmpty())) {
+            for (NamedGroup group : NamedGroup.values()) {
+                if (oid.equals(group.oid)) {
+                    return group;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return this.name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/NamedGroupType.java	Thu May 25 21:22:55 2017 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, 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;
+
+enum NamedGroupType {
+    NAMED_GROUP_ECDHE,          // Elliptic Curve Groups (ECDHE)
+    NAMED_GROUP_FFDHE,          // Finite Field Groups (DHE)
+    NAMED_GROUP_NONE            // No predefined named group
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java	Thu May 25 21:22:55 2017 -0700
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2017, 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.math.BigInteger;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+import javax.crypto.spec.DHParameterSpec;
+
+/**
+ * Predefined default DH ephemeral parameters.
+ */
+final class PredefinedDHParameterSpecs {
+    private final static boolean debugIsOn =
+            (Debug.getInstance("ssl") != null) && Debug.isOn("sslctx");
+
+    //
+    // Default DH ephemeral parameters
+    //
+    private static final BigInteger p512 = new BigInteger(       // generated
+            "D87780E15FF50B4ABBE89870188B049406B5BEA98AB23A02" +
+            "41D88EA75B7755E669C08093D3F0CA7FC3A5A25CF067DCB9" +
+            "A43DD89D1D90921C6328884461E0B6D3", 16);
+    private static final BigInteger p768 = new BigInteger(       // RFC 2409
+            "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
+            "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
+            "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
+            "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16);
+
+    private static final BigInteger p1024 = new BigInteger(      // RFC 2409
+            "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
+            "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
+            "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
+            "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
+            "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" +
+            "FFFFFFFFFFFFFFFF", 16);
+    private static final BigInteger p1536 = new BigInteger(      // RFC 3526
+            "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
+            "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
+            "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
+            "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
+            "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
+            "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
+            "83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
+            "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16);
+    private static final BigInteger p2048 = new BigInteger(      // TLS FFDHE
+            "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
+            "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
+            "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
+            "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
+            "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
+            "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
+            "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
+            "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
+            "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
+            "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
+            "886B423861285C97FFFFFFFFFFFFFFFF", 16);
+    private static final BigInteger p3072 = new BigInteger(      // TLS FFDHE
+            "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
+            "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
+            "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
+            "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
+            "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
+            "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
+            "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
+            "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
+            "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
+            "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
+            "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
+            "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
+            "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
+            "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
+            "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
+            "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", 16);
+    private static final BigInteger p4096 = new BigInteger(      // TLS FFDHE
+            "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
+            "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
+            "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
+            "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
+            "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
+            "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
+            "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
+            "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
+            "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
+            "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
+            "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
+            "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
+            "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
+            "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
+            "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
+            "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
+            "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
+            "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
+            "A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
+            "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
+            "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A" +
+            "FFFFFFFFFFFFFFFF", 16);
+    private static final BigInteger p6144 = new BigInteger(      // TLS FFDHE
+            "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
+            "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
+            "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
+            "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
+            "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
+            "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
+            "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
+            "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
+            "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
+            "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
+            "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
+            "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
+            "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
+            "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
+            "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
+            "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
+            "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
+            "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
+            "A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
+            "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
+            "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" +
+            "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" +
+            "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" +
+            "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" +
+            "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" +
+            "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" +
+            "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" +
+            "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" +
+            "D72B03746AE77F5E62292C311562A846505DC82DB854338A" +
+            "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" +
+            "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" +
+            "A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF", 16);
+    private static final BigInteger p8192 = new BigInteger(      // TLS FFDHE
+            "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
+            "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
+            "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
+            "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
+            "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
+            "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
+            "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
+            "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
+            "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
+            "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
+            "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
+            "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
+            "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
+            "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
+            "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
+            "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
+            "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
+            "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
+            "A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
+            "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
+            "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" +
+            "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" +
+            "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" +
+            "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" +
+            "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" +
+            "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" +
+            "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" +
+            "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" +
+            "D72B03746AE77F5E62292C311562A846505DC82DB854338A" +
+            "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" +
+            "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" +
+            "A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838" +
+            "1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E" +
+            "0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665" +
+            "CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282" +
+            "2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022" +
+            "BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C" +
+            "51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9" +
+            "D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457" +
+            "1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30" +
+            "FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D" +
+            "97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C" +
+            "D68C8BB7C5C6424CFFFFFFFFFFFFFFFF", 16);
+
+    private static final BigInteger[] supportedPrimes = {
+            p512, p768, p1024, p1536, p2048, p3072, p4096, p6144, p8192};
+
+    private static final BigInteger[] ffdhePrimes = {
+            p2048, p3072, p4096, p6144, p8192};
+
+    // a measure of the uncertainty that prime modulus p is not a prime
+    //
+    // see BigInteger.isProbablePrime(int certainty)
+    private final static int PRIME_CERTAINTY = 120;
+
+    // the known security property, jdk.tls.server.defaultDHEParameters
+    private final static String PROPERTY_NAME =
+            "jdk.tls.server.defaultDHEParameters";
+
+    private static final Pattern spacesPattern = Pattern.compile("\\s+");
+
+    private final static Pattern syntaxPattern = Pattern.compile(
+            "(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
+            "(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
+
+    private static final Pattern paramsPattern = Pattern.compile(
+            "\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
+
+    // cache of predefined default DH ephemeral parameters
+    final static Map<Integer, DHParameterSpec> definedParams;
+
+    // cache of Finite Field DH Ephemeral parameters (RFC 7919/FFDHE)
+    final static Map<Integer, DHParameterSpec> ffdheParams;
+
+    static {
+        String property = AccessController.doPrivileged(
+            new PrivilegedAction<String>() {
+                public String run() {
+                    return Security.getProperty(PROPERTY_NAME);
+                }
+            });
+
+        if (property != null && !property.isEmpty()) {
+            // remove double quote marks from beginning/end of the property
+            if (property.length() >= 2 && property.charAt(0) == '"' &&
+                    property.charAt(property.length() - 1) == '"') {
+                property = property.substring(1, property.length() - 1);
+            }
+
+            property = property.trim();
+        }
+
+        if (property != null && !property.isEmpty()) {
+            Matcher spacesMatcher = spacesPattern.matcher(property);
+            property = spacesMatcher.replaceAll("");
+
+            if (debugIsOn) {
+                System.out.println("The Security Property " +
+                        PROPERTY_NAME + ": " + property);
+            }
+        }
+
+        Map<Integer,DHParameterSpec> defaultParams = new HashMap<>();
+        if (property != null && !property.isEmpty()) {
+            Matcher syntaxMatcher = syntaxPattern.matcher(property);
+            if (syntaxMatcher.matches()) {
+                Matcher paramsFinder = paramsPattern.matcher(property);
+                while(paramsFinder.find()) {
+                    String primeModulus = paramsFinder.group(1);
+                    BigInteger p = new BigInteger(primeModulus, 16);
+                    if (!p.isProbablePrime(PRIME_CERTAINTY)) {
+                        if (debugIsOn) {
+                            System.out.println(
+                                "Prime modulus p in Security Property, " +
+                                PROPERTY_NAME + ", is not a prime: " +
+                                primeModulus);
+                        }
+
+                        continue;
+                    }
+
+                    String baseGenerator = paramsFinder.group(2);
+                    BigInteger g = new BigInteger(baseGenerator, 16);
+
+                    DHParameterSpec spec = new DHParameterSpec(p, g);
+                    int primeLen = p.bitLength();
+                    defaultParams.put(primeLen, spec);
+                }
+            } else if (debugIsOn) {
+                System.out.println("Invalid Security Property, " +
+                        PROPERTY_NAME + ", definition");
+            }
+        }
+
+        Map<Integer,DHParameterSpec> tempFFDHEs = new HashMap<>();
+        for (BigInteger p : ffdhePrimes) {
+            int primeLen = p.bitLength();
+            DHParameterSpec dhps = new DHParameterSpec(p, BigInteger.valueOf(2));
+            tempFFDHEs.put(primeLen, dhps);
+            defaultParams.putIfAbsent(primeLen, dhps);
+        }
+
+        for (BigInteger p : supportedPrimes) {
+            int primeLen = p.bitLength();
+            if (defaultParams.get(primeLen) == null) {
+                defaultParams.put(primeLen,
+                    new DHParameterSpec(p, BigInteger.valueOf(2)));
+            }
+        }
+
+        ffdheParams =
+            Collections.<Integer,DHParameterSpec>unmodifiableMap(tempFFDHEs);
+        definedParams =
+            Collections.<Integer,DHParameterSpec>unmodifiableMap(defaultParams);
+    }
+}
--- a/src/share/classes/sun/security/ssl/ServerHandshaker.java	Thu May 25 21:21:06 2017 -0700
+++ b/src/share/classes/sun/security/ssl/ServerHandshaker.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -93,7 +93,7 @@
     private ProtocolVersion clientRequestedVersion;
 
     // client supported elliptic curves
-    private EllipticCurvesExtension requestedCurves;
+    private SupportedGroupsExtension requestedGroups;
 
     // the preferable signature algorithm used by ServerKeyExchange message
     SignatureAndHashAlgorithm preferableSignatureAlgorithm;
@@ -683,8 +683,8 @@
                 throw new SSLException("Client did not resume a session");
             }
 
-            requestedCurves = (EllipticCurvesExtension)
-                        mesg.extensions.get(ExtensionType.EXT_ELLIPTIC_CURVES);
+            requestedGroups = (SupportedGroupsExtension)
+                    mesg.extensions.get(ExtensionType.EXT_SUPPORTED_GROUPS);
 
             // We only need to handle the "signature_algorithm" extension
             // for full handshakes and TLS 1.2 or later.
@@ -1158,6 +1158,8 @@
             }
         }
 
+        // The named group used for ECDHE and FFDHE.
+        NamedGroup namedGroup = null;
         switch (keyExchange) {
         case K_RSA:
             // need RSA certs for authentication
@@ -1183,6 +1185,37 @@
             }
             break;
         case K_DHE_RSA:
+            // Is ephemeral DH cipher suite usable for the connection?
+            //
+            // [RFC 7919] If a compatible TLS server receives a Supported
+            // Groups extension from a client that includes any FFDHE group
+            // (i.e., any codepoint between 256 and 511, inclusive, even if
+            // unknown to the server), and if none of the client-proposed
+            // FFDHE groups are known and acceptable to the server, then
+            // the server MUST NOT select an FFDHE cipher suite.  In this
+            // case, the server SHOULD select an acceptable non-FFDHE cipher
+            // suite from the client's offered list.  If the extension is
+            // present with FFDHE groups, none of the client's offered
+            // groups are acceptable by the server, and none of the client's
+            // proposed non-FFDHE cipher suites are acceptable to the server,
+            // the server MUST end the connection with a fatal TLS alert
+            // of type insufficient_security(71).
+            //
+            // Note: For compatibility, if an application is customized to
+            // use legacy sizes (512 bits for exportable cipher suites and
+            // 768 bits for others), or the cipher suite is exportable, the
+            // FFDHE extension will not be used.
+            if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
+                (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
+
+                namedGroup = requestedGroups.getPreferredGroup(
+                    algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
+                if (namedGroup == null) {
+                    // no match found, cannot use this cipher suite.
+                    return false;
+                }
+            }
+
             // need RSA certs for authentication
             if (setupPrivateKeyAndChain("RSA") == false) {
                 return false;
@@ -1203,9 +1236,20 @@
                 }
             }
 
-            setupEphemeralDHKeys(suite.exportable, privateKey);
+            setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey);
             break;
         case K_ECDHE_RSA:
+            // Is ECDHE cipher suite usable for the connection?
+            namedGroup = (requestedGroups != null) ?
+                requestedGroups.getPreferredGroup(
+                    algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
+                SupportedGroupsExtension.getPreferredECGroup(
+                    algorithmConstraints);
+            if (namedGroup == null) {
+                // no match found, cannot use this ciphersuite
+                return false;
+            }
+
             // need RSA certs for authentication
             if (setupPrivateKeyAndChain("RSA") == false) {
                 return false;
@@ -1226,11 +1270,23 @@
                 }
             }
 
-            if (setupEphemeralECDHKeys() == false) {
-                return false;
-            }
+            setupEphemeralECDHKeys(namedGroup);
             break;
         case K_DHE_DSS:
+            // Is ephemeral DH cipher suite usable for the connection?
+            //
+            // See comment in K_DHE_RSA case.
+            if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
+                (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
+
+                namedGroup = requestedGroups.getPreferredGroup(
+                    algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
+                if (namedGroup == null) {
+                    // no match found, cannot use this cipher suite.
+                    return false;
+                }
+            }
+
             // get preferable peer signature algorithm for server key exchange
             if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
                 preferableSignatureAlgorithm =
@@ -1251,9 +1307,20 @@
                 return false;
             }
 
-            setupEphemeralDHKeys(suite.exportable, privateKey);
+            setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey);
             break;
         case K_ECDHE_ECDSA:
+            // Is ECDHE cipher suite usable for the connection?
+            namedGroup = (requestedGroups != null) ?
+                requestedGroups.getPreferredGroup(
+                    algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
+                SupportedGroupsExtension.getPreferredECGroup(
+                    algorithmConstraints);
+            if (namedGroup == null) {
+                // no match found, cannot use this ciphersuite
+                return false;
+            }
+
             // get preferable peer signature algorithm for server key exchange
             if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
                 preferableSignatureAlgorithm =
@@ -1273,9 +1340,8 @@
             if (setupPrivateKeyAndChain("EC") == false) {
                 return false;
             }
-            if (setupEphemeralECDHKeys() == false) {
-                return false;
-            }
+
+            setupEphemeralECDHKeys(namedGroup);
             break;
         case K_ECDH_RSA:
             // need EC cert
@@ -1299,14 +1365,36 @@
             }
             break;
         case K_DH_ANON:
+            // Is ephemeral DH cipher suite usable for the connection?
+            //
+            // See comment in K_DHE_RSA case.
+            if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
+                (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
+                namedGroup = requestedGroups.getPreferredGroup(
+                    algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
+                if (namedGroup == null) {
+                    // no match found, cannot use this cipher suite.
+                    return false;
+                }
+            }
+
             // no certs needed for anonymous
-            setupEphemeralDHKeys(suite.exportable, null);
+            setupEphemeralDHKeys(namedGroup, suite.exportable, null);
             break;
         case K_ECDH_ANON:
-            // no certs needed for anonymous
-            if (setupEphemeralECDHKeys() == false) {
+            // Is ECDHE cipher suite usable for the connection?
+            namedGroup = (requestedGroups != null) ?
+                requestedGroups.getPreferredGroup(
+                    algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
+                SupportedGroupsExtension.getPreferredECGroup(
+                    algorithmConstraints);
+            if (namedGroup == null) {
+                // no match found, cannot use this ciphersuite
                 return false;
             }
+
+            // no certs needed for anonymous
+            setupEphemeralECDHKeys(namedGroup);
             break;
         default:
             // internal error, unknown key exchange
@@ -1350,7 +1438,15 @@
      * Acquire some "ephemeral" Diffie-Hellman  keys for this handshake.
      * We don't reuse these, for improved forward secrecy.
      */
-    private void setupEphemeralDHKeys(boolean export, Key key) {
+    private void setupEphemeralDHKeys(
+            NamedGroup namedGroup, boolean export, Key key) {
+        // Are the client and server willing to negotiate FFDHE groups?
+        if ((!useLegacyEphemeralDHKeys) && (!export) && (namedGroup != null)) {
+            dh = new DHCrypt(namedGroup, sslContext.getSecureRandom());
+
+            return;
+        }   // Otherwise, the client is not compatible with FFDHE extension.
+
         /*
          * 768 bits ephemeral DH private keys were used to be used in
          * ServerKeyExchange except that exportable ciphers max out at 512
@@ -1416,20 +1512,11 @@
         dh = new DHCrypt(keySize, sslContext.getSecureRandom());
     }
 
-    // Setup the ephemeral ECDH parameters.
-    // 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 = (requestedCurves != null) ?
-                requestedCurves.getPreferredCurve(algorithmConstraints) :
-                EllipticCurvesExtension.getActiveCurves(algorithmConstraints);
-        if (index < 0) {
-            // no match found, cannot use this ciphersuite
-            return false;
-        }
-
-        ecdh = new ECDHCrypt(index, sslContext.getSecureRandom());
-        return true;
+    /**
+     * Setup the ephemeral ECDH parameters.
+     */
+    private void setupEphemeralECDHKeys(NamedGroup namedGroup) {
+        ecdh = new ECDHCrypt(namedGroup, sslContext.getSecureRandom());
     }
 
     private void setupStaticECDHKeys() {
@@ -1477,9 +1564,11 @@
                 return false;
             }
             ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
-            int id = EllipticCurvesExtension.getCurveIndex(params);
-            if ((id <= 0) || !EllipticCurvesExtension.isSupported(id) ||
-                ((requestedCurves != null) && !requestedCurves.contains(id))) {
+            NamedGroup namedGroup = NamedGroup.valueOf(params);
+            if ((namedGroup == null) ||
+                (!SupportedGroupsExtension.supports(namedGroup)) ||
+                ((requestedGroups != null) &&
+                        !requestedGroups.contains(namedGroup.id))) {
                 return false;
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Thu May 25 21:22:55 2017 -0700
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2006, 2017, 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.io.IOException;
+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.security.spec.AlgorithmParameterSpec;
+import javax.crypto.spec.DHParameterSpec;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ArrayList;
+import javax.net.ssl.SSLProtocolException;
+
+import sun.security.action.GetPropertyAction;
+
+//
+// Note: Since RFC 7919, the extension's semantics are expanded from
+// "Supported Elliptic Curves" to "Supported Groups".  The enum datatype
+// used in the extension has been renamed from NamedCurve to NamedGroup.
+// Its semantics are likewise expanded from "named curve" to "named group".
+//
+final class SupportedGroupsExtension extends HelloExtension {
+
+    /* Class and subclass dynamic debugging support */
+    private static final Debug debug = Debug.getInstance("ssl");
+
+    private static final int ARBITRARY_PRIME = 0xff01;
+    private static final int ARBITRARY_CHAR2 = 0xff02;
+
+    // cache to speed up the parameters construction
+    private static final Map<NamedGroup,
+                AlgorithmParameters> namedGroupParams = new HashMap<>();
+
+    // the supported named groups
+    private static final NamedGroup[] supportedNamedGroups;
+
+    // the named group presented in the extension
+    private final int[] requestedNamedGroupIds;
+
+    static {
+        boolean requireFips = SunJSSE.isFIPS();
+
+        // The value of the System Property defines a list of enabled named
+        // groups in preference order, separated with comma.  For example:
+        //
+        //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
+        //
+        // If the System Property is not defined or the value is empty, the
+        // default groups 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<NamedGroup> groupList;
+        if (property != null && property.length() != 0) {   // customized groups
+            String[] groups = property.split(",");
+            groupList = new ArrayList<>(groups.length);
+            for (String group : groups) {
+                group = group.trim();
+                if (!group.isEmpty()) {
+                    NamedGroup namedGroup = NamedGroup.nameOf(group);
+                    if (namedGroup != null &&
+                            (!requireFips || namedGroup.isFips)) {
+                        if (isAvailableGroup(namedGroup)) {
+                            groupList.add(namedGroup);
+                        }
+                    }   // ignore unknown groups
+                }
+            }
+
+            if (groupList.isEmpty() && JsseJce.isEcAvailable()) {
+                throw new IllegalArgumentException(
+                    "System property jdk.tls.namedGroups(" + property + ") " +
+                    "contains no supported elliptic curves");
+            }
+        } else {        // default groups
+            NamedGroup[] groups;
+            if (requireFips) {
+                groups = new NamedGroup[] {
+                    // only NIST curves in FIPS mode
+                    NamedGroup.SECP256_R1,
+                    NamedGroup.SECP384_R1,
+                    NamedGroup.SECP521_R1,
+                    NamedGroup.SECT283_K1,
+                    NamedGroup.SECT283_R1,
+                    NamedGroup.SECT409_K1,
+                    NamedGroup.SECT409_R1,
+                    NamedGroup.SECT571_K1,
+                    NamedGroup.SECT571_R1,
+
+                    // FFDHE 2048
+                    NamedGroup.FFDHE_2048,
+                    NamedGroup.FFDHE_3072,
+                    NamedGroup.FFDHE_4096,
+                    NamedGroup.FFDHE_6144,
+                    NamedGroup.FFDHE_8192,
+                };
+            } else {
+                groups = new NamedGroup[] {
+                    // NIST curves first
+                    NamedGroup.SECP256_R1,
+                    NamedGroup.SECP384_R1,
+                    NamedGroup.SECP521_R1,
+                    NamedGroup.SECT283_K1,
+                    NamedGroup.SECT283_R1,
+                    NamedGroup.SECT409_K1,
+                    NamedGroup.SECT409_R1,
+                    NamedGroup.SECT571_K1,
+                    NamedGroup.SECT571_R1,
+
+                    // non-NIST curves
+                    NamedGroup.SECP256_K1,
+
+                    // FFDHE 2048
+                    NamedGroup.FFDHE_2048,
+                    NamedGroup.FFDHE_3072,
+                    NamedGroup.FFDHE_4096,
+                    NamedGroup.FFDHE_6144,
+                    NamedGroup.FFDHE_8192,
+                };
+            }
+
+            groupList = new ArrayList<>(groups.length);
+            for (NamedGroup group : groups) {
+                if (isAvailableGroup(group)) {
+                    groupList.add(group);
+                }
+            }
+        }
+
+        if (debug != null && groupList.isEmpty()) {
+            debug.println(
+                "Initialized [jdk.tls.namedGroups|default] list contains " +
+                "no available elliptic curves. " +
+                (property != null ? "(" + property + ")" : "[Default]"));
+        }
+
+        supportedNamedGroups = new NamedGroup[groupList.size()];
+        int i = 0;
+        for (NamedGroup namedGroup : groupList) {
+            supportedNamedGroups[i++] = namedGroup;
+        }
+    }
+
+    // check whether the group is supported by the underlying providers
+    private static boolean isAvailableGroup(NamedGroup namedGroup) {
+        AlgorithmParameters params = null;
+        AlgorithmParameterSpec spec = null;
+        if ("EC".equals(namedGroup.algorithm)) {
+            if (namedGroup.oid != null) {
+                try {
+                    params = JsseJce.getAlgorithmParameters("EC");
+                    spec = new ECGenParameterSpec(namedGroup.oid);
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        } else if ("DiffieHellman".equals(namedGroup.algorithm)) {
+            try {
+                params = JsseJce.getAlgorithmParameters("DiffieHellman");
+                spec = getFFDHEDHParameterSpec(namedGroup);
+            } catch (Exception e) {
+                return false;
+            }
+        }
+
+        if ((params != null) && (spec != null)) {
+            try {
+                params.init(spec);
+            } catch (Exception e) {
+                return false;
+            }
+
+            // cache the parameters
+            namedGroupParams.put(namedGroup, params);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private static DHParameterSpec getFFDHEDHParameterSpec(
+            NamedGroup namedGroup) {
+        DHParameterSpec spec = null;
+        switch (namedGroup) {
+            case FFDHE_2048:
+                spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
+                break;
+            case FFDHE_3072:
+                spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
+                break;
+            case FFDHE_4096:
+                spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
+                break;
+            case FFDHE_6144:
+                spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
+                break;
+            case FFDHE_8192:
+                spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
+        }
+        return spec;
+    }
+
+    private static DHParameterSpec getPredefinedDHParameterSpec(
+            NamedGroup namedGroup) {
+        DHParameterSpec spec = null;
+        switch (namedGroup) {
+            case FFDHE_2048:
+                spec = PredefinedDHParameterSpecs.definedParams.get(2048);
+                break;
+            case FFDHE_3072:
+                spec = PredefinedDHParameterSpecs.definedParams.get(3072);
+                break;
+            case FFDHE_4096:
+                spec = PredefinedDHParameterSpecs.definedParams.get(4096);
+                break;
+            case FFDHE_6144:
+                spec = PredefinedDHParameterSpecs.definedParams.get(6144);
+                break;
+            case FFDHE_8192:
+                spec = PredefinedDHParameterSpecs.definedParams.get(8192);
+        }
+
+        return spec;
+    }
+
+    private SupportedGroupsExtension(int[] requestedNamedGroupIds) {
+        super(ExtensionType.EXT_SUPPORTED_GROUPS);
+
+        this.requestedNamedGroupIds = requestedNamedGroupIds;
+    }
+
+    SupportedGroupsExtension(HandshakeInStream s, int len) throws IOException {
+        super(ExtensionType.EXT_SUPPORTED_GROUPS);
+        int k = s.getInt16();
+        if (((len & 1) != 0) || (k == 0) || (k + 2 != len)) {
+            throw new SSLProtocolException("Invalid " + type + " extension");
+        }
+
+        // Note: unknown named group will be ignored later.
+        requestedNamedGroupIds = new int[k >> 1];
+        for (int i = 0; i < requestedNamedGroupIds.length; i++) {
+            requestedNamedGroupIds[i] = s.getInt16();
+        }
+    }
+
+    // Get a local preferred supported ECDHE group permitted by the constraints.
+    static NamedGroup getPreferredECGroup(AlgorithmConstraints constraints) {
+        for (NamedGroup namedGroup : supportedNamedGroups) {
+            if ((namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
+                constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                    namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
+
+                return namedGroup;
+            }
+        }
+
+        return null;
+    }
+
+    // Is there any supported group permitted by the constraints?
+    static boolean isActivatable(
+            AlgorithmConstraints constraints, NamedGroupType type) {
+
+        boolean hasFFDHEGroups = false;
+        for (NamedGroup namedGroup : supportedNamedGroups) {
+            if (namedGroup.type == type) {
+                if (constraints.permits(
+                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                        namedGroup.algorithm,
+                        namedGroupParams.get(namedGroup))) {
+
+                    return true;
+                }
+
+                if (!hasFFDHEGroups &&
+                        (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+
+                    hasFFDHEGroups = true;
+                }
+            }
+        }
+
+        // For compatibility, if no FFDHE groups are defined, the non-FFDHE
+        // compatible mode (using DHE cipher suite without FFDHE extension)
+        // is allowed.
+        //
+        // Note that the constraints checking on DHE parameters will be
+        // performed during key exchanging in a handshake.
+        if (!hasFFDHEGroups && (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    // Create the default supported groups extension.
+    static SupportedGroupsExtension createExtension(
+            AlgorithmConstraints constraints,
+            CipherSuiteList cipherSuites, boolean enableFFDHE) {
+
+        ArrayList<Integer> groupList =
+                new ArrayList<>(supportedNamedGroups.length);
+        for (NamedGroup namedGroup : supportedNamedGroups) {
+            if ((!enableFFDHE) &&
+                (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                continue;
+            }
+
+            if (cipherSuites.contains(namedGroup.type) &&
+                constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                    namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
+
+                groupList.add(namedGroup.id);
+            }
+        }
+
+        if (!groupList.isEmpty()) {
+            int[] ids = new int[groupList.size()];
+            int i = 0;
+            for (Integer id : groupList) {
+                ids[i++] = id;
+            }
+
+            return new SupportedGroupsExtension(ids);
+        }
+
+        return null;
+    }
+
+    // get the preferred activated named group
+    NamedGroup getPreferredGroup(
+            AlgorithmConstraints constraints, NamedGroupType type) {
+
+        for (int groupId : requestedNamedGroupIds) {
+            NamedGroup namedGroup = NamedGroup.valueOf(groupId);
+            if ((namedGroup != null) && (namedGroup.type == type) &&
+                SupportedGroupsExtension.supports(namedGroup) &&
+                constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                    namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
+
+                return namedGroup;
+            }
+        }
+
+        return null;
+    }
+
+    boolean hasFFDHEGroup() {
+        for (int groupId : requestedNamedGroupIds) {
+            /*
+             * [RFC 7919] Codepoints in the "Supported Groups Registry"
+             * with a high byte of 0x01 (that is, between 256 and 511,
+             * inclusive) are set aside for FFDHE groups.
+             */
+            if ((groupId >= 256) && (groupId <= 511)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    boolean contains(int index) {
+        for (int groupId : requestedNamedGroupIds) {
+            if (index == groupId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    int length() {
+        return 6 + (requestedNamedGroupIds.length << 1);
+    }
+
+    @Override
+    void send(HandshakeOutStream s) throws IOException {
+        s.putInt16(type.id);
+        int k = requestedNamedGroupIds.length << 1;
+        s.putInt16(k + 2);
+        s.putInt16(k);
+        for (int groupId : requestedNamedGroupIds) {
+            s.putInt16(groupId);
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Extension " + type + ", group names: {");
+        boolean first = true;
+        for (int groupId : requestedNamedGroupIds) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            // first check if it is a known named group, then try other cases.
+            NamedGroup namedGroup = NamedGroup.valueOf(groupId);
+            if (namedGroup != null) {
+                sb.append(namedGroup.name);
+            } else if (groupId == ARBITRARY_PRIME) {
+                sb.append("arbitrary_explicit_prime_curves");
+            } else if (groupId == ARBITRARY_CHAR2) {
+                sb.append("arbitrary_explicit_char2_curves");
+            } else {
+                sb.append("unknown named group " + groupId);
+            }
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    static boolean supports(NamedGroup namedGroup) {
+        for (NamedGroup group : supportedNamedGroups) {
+            if (namedGroup.id == group.id) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) {
+        if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
+            throw new RuntimeException("Not a named EC group: " + namedGroup);
+        }
+
+        AlgorithmParameters params = namedGroupParams.get(namedGroup);
+        try {
+            return params.getParameterSpec(ECGenParameterSpec.class);
+        } catch (InvalidParameterSpecException ipse) {
+            // should be unlikely
+            return new ECGenParameterSpec(namedGroup.oid);
+        }
+    }
+
+    static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) {
+        if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+            throw new RuntimeException("Not a named DH group: " + namedGroup);
+        }
+
+        AlgorithmParameters params = namedGroupParams.get(namedGroup);
+        try {
+            return params.getParameterSpec(DHParameterSpec.class);
+        } catch (InvalidParameterSpecException ipse) {
+            // should be unlikely
+            return getPredefinedDHParameterSpec(namedGroup);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/net/ssl/templates/SSLSocketTemplate.java	Thu May 25 21:22:55 2017 -0700
@@ -0,0 +1,844 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ *
+ * 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.
+ */
+
+//
+// Please run in othervm mode.  SunJSSE does not support dynamic system
+// properties, no way to re-use system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 8161106 8170329
+ * @modules jdk.crypto.ec
+ * @summary Improve SSLSocket test template
+ * @run main/othervm SSLSocketTemplate
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+import java.net.InetSocketAddress;
+import java.net.SocketTimeoutException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.KeyFactory;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Template to help speed your client/server tests.
+ *
+ * Two examples that use this template:
+ *    test/sun/security/ssl/ServerHandshaker/AnonCipherWithWantClientAuth.java
+ *    test/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java
+ */
+public class SSLSocketTemplate {
+
+    /*
+     * ==================
+     * Run the test case.
+     */
+    public static void main(String[] args) throws Exception {
+        (new SSLSocketTemplate()).run();
+    }
+
+    /*
+     * Run the test case.
+     */
+    public void run() throws Exception {
+        bootup();
+    }
+
+    /*
+     * Define the server side application of the test for the specified socket.
+     */
+    protected void runServerApplication(SSLSocket socket) throws Exception {
+        // here comes the test logic
+        InputStream sslIS = socket.getInputStream();
+        OutputStream sslOS = socket.getOutputStream();
+
+        sslIS.read();
+        sslOS.write(85);
+        sslOS.flush();
+    }
+
+    /*
+     * Define the client side application of the test for the specified socket.
+     * This method is used if the returned value of
+     * isCustomizedClientConnection() is false.
+     *
+     * @param socket may be null is no client socket is generated.
+     *
+     * @see #isCustomizedClientConnection()
+     */
+    protected void runClientApplication(SSLSocket socket) throws Exception {
+        InputStream sslIS = socket.getInputStream();
+        OutputStream sslOS = socket.getOutputStream();
+
+        sslOS.write(280);
+        sslOS.flush();
+        sslIS.read();
+    }
+
+    /*
+     * Define the client side application of the test for the specified
+     * server port.  This method is used if the returned value of
+     * isCustomizedClientConnection() is true.
+     *
+     * Note that the client need to connect to the server port by itself
+     * for the actual message exchange.
+     *
+     * @see #isCustomizedClientConnection()
+     */
+    protected void runClientApplication(int serverPort) throws Exception {
+        // blank
+    }
+
+    /*
+     * Create an instance of SSLContext for client use.
+     */
+    protected SSLContext createClientSSLContext() throws Exception {
+        return createSSLContext(trustedCertStrs,
+                endEntityCertStrs, endEntityPrivateKeys,
+                endEntityPrivateKeyNames,
+                getClientContextParameters());
+    }
+
+    /*
+     * Create an instance of SSLContext for server use.
+     */
+    protected SSLContext createServerSSLContext() throws Exception {
+        return createSSLContext(trustedCertStrs,
+                endEntityCertStrs, endEntityPrivateKeys,
+                endEntityPrivateKeyNames,
+                getServerContextParameters());
+    }
+
+    /*
+     * The parameters used to configure SSLContext.
+     */
+    protected static final class ContextParameters {
+        final String contextProtocol;
+        final String tmAlgorithm;
+        final String kmAlgorithm;
+
+        ContextParameters(String contextProtocol,
+                String tmAlgorithm, String kmAlgorithm) {
+
+            this.contextProtocol = contextProtocol;
+            this.tmAlgorithm = tmAlgorithm;
+            this.kmAlgorithm = kmAlgorithm;
+        }
+    }
+
+    /*
+     * Get the client side parameters of SSLContext.
+     */
+    protected ContextParameters getClientContextParameters() {
+        return new ContextParameters("TLS", "PKIX", "NewSunX509");
+    }
+
+    /*
+     * Get the server side parameters of SSLContext.
+     */
+    protected ContextParameters getServerContextParameters() {
+        return new ContextParameters("TLS", "PKIX", "NewSunX509");
+    }
+
+    /*
+     * Does the client side use customized connection other than
+     * explicit Socket.connect(), for example, URL.openConnection()?
+     */
+    protected boolean isCustomizedClientConnection() {
+        return false;
+    }
+
+    /*
+     * Configure the server side socket.
+     */
+    protected void configureServerSocket(SSLServerSocket socket) {
+
+    }
+
+    /*
+     * =============================================
+     * Define the client and server side operations.
+     *
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+
+    /*
+     * Is the server ready to serve?
+     */
+    private final CountDownLatch serverCondition = new CountDownLatch(1);
+
+    /*
+     * Is the client ready to handshake?
+     */
+    private final CountDownLatch clientCondition = new CountDownLatch(1);
+
+    /*
+     * What's the server port?  Use any free port by default
+     */
+    private volatile int serverPort = 0;
+
+    /*
+     * Define the server side of the test.
+     */
+    private void doServerSide() throws Exception {
+        // kick start the server side service
+        SSLContext context = createServerSSLContext();
+        SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+        SSLServerSocket sslServerSocket =
+                (SSLServerSocket)sslssf.createServerSocket(serverPort);
+        configureServerSocket(sslServerSocket);
+        serverPort = sslServerSocket.getLocalPort();
+
+        // Signal the client, the server is ready to accept connection.
+        serverCondition.countDown();
+
+        // Try to accept a connection in 30 seconds.
+        SSLSocket sslSocket;
+        try {
+            sslServerSocket.setSoTimeout(30000);
+            sslSocket = (SSLSocket)sslServerSocket.accept();
+        } catch (SocketTimeoutException ste) {
+            // Ignore the test case if no connection within 30 seconds.
+            System.out.println(
+                "No incoming client connection in 30 seconds. " +
+                "Ignore in server side.");
+            return;
+        } finally {
+            sslServerSocket.close();
+        }
+
+        // handle the connection
+        try {
+            // Is it the expected client connection?
+            //
+            // Naughty test cases or third party routines may try to
+            // connection to this server port unintentionally.  In
+            // order to mitigate the impact of unexpected client
+            // connections and avoid intermittent failure, it should
+            // be checked that the accepted connection is really linked
+            // to the expected client.
+            boolean clientIsReady =
+                    clientCondition.await(30L, TimeUnit.SECONDS);
+
+            if (clientIsReady) {
+                // Run the application in server side.
+                runServerApplication(sslSocket);
+            } else {    // Otherwise, ignore
+                // We don't actually care about plain socket connections
+                // for TLS communication testing generally.  Just ignore
+                // the test if the accepted connection is not linked to
+                // the expected client or the client connection timeout
+                // in 30 seconds.
+                System.out.println(
+                        "The client is not the expected one or timeout. " +
+                        "Ignore in server side.");
+            }
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    /*
+     * Define the client side of the test.
+     */
+    private void doClientSide() throws Exception {
+
+        // Wait for server to get started.
+        //
+        // The server side takes care of the issue if the server cannot
+        // get started in 90 seconds.  The client side would just ignore
+        // the test case if the serer is not ready.
+        boolean serverIsReady =
+                serverCondition.await(90L, TimeUnit.SECONDS);
+        if (!serverIsReady) {
+            System.out.println(
+                    "The server is not ready yet in 90 seconds. " +
+                    "Ignore in client side.");
+            return;
+        }
+
+        if (isCustomizedClientConnection()) {
+            // Signal the server, the client is ready to communicate.
+            clientCondition.countDown();
+
+            // Run the application in client side.
+            runClientApplication(serverPort);
+
+            return;
+        }
+
+        SSLContext context = createClientSSLContext();
+        SSLSocketFactory sslsf = context.getSocketFactory();
+
+        try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
+            try {
+                sslSocket.connect(
+                        new InetSocketAddress("localhost", serverPort), 15000);
+            } catch (IOException ioe) {
+                // The server side may be impacted by naughty test cases or
+                // third party routines, and cannot accept connections.
+                //
+                // Just ignore the test if the connection cannot be
+                // established.
+                System.out.println(
+                        "Cannot make a connection in 15 seconds. " +
+                        "Ignore in client side.");
+                return;
+            }
+
+            // OK, here the client and server get connected.
+
+            // Signal the server, the client is ready to communicate.
+            clientCondition.countDown();
+
+            // There is still a chance in theory that the server thread may
+            // wait client-ready timeout and then quit.  The chance should
+            // be really rare so we don't consider it until it becomes a
+            // real problem.
+
+            // Run the application in client side.
+            runClientApplication(sslSocket);
+        }
+    }
+
+    /*
+     * =============================================
+     * Stuffs to customize the SSLContext instances.
+     */
+
+    /*
+     * =======================================
+     * Certificates and keys used in the test.
+     */
+    // Trusted certificates.
+    private final static String[] trustedCertStrs = {
+        // SHA256withECDSA, curve prime256v1
+        // Validity
+        //    Not Before: Nov 25 04:19:51 2016 GMT
+        //    Not After : Nov  5 04:19:51 2037 GMT
+        // Subject Key Identifier:
+        //    CA:48:E8:00:C1:42:BD:59:9B:79:D9:B4:B4:CE:3F:68:0C:C8:C4:0C
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICHDCCAcGgAwIBAgIJAJtKs6ZEcVjxMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTAeFw0xNjExMjUwNDE5NTFaFw0zNzExMDUwNDE5NTFaMDsxCzAJBgNVBAYTAlVT\n" +
+        "MQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZjZTBZ\n" +
+        "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABKMO/AFDHZia65RaqMIBX7WBdtzFj8fz\n" +
+        "ggqMADLJhoszS6qfTUDYskETw3uHfB3KAOENsoKX446qFFPuVjxS1aejga0wgaow\n" +
+        "HQYDVR0OBBYEFMpI6ADBQr1Zm3nZtLTOP2gMyMQMMGsGA1UdIwRkMGKAFMpI6ADB\n" +
+        "Qr1Zm3nZtLTOP2gMyMQMoT+kPTA7MQswCQYDVQQGEwJVUzENMAsGA1UEChMESmF2\n" +
+        "YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2WCCQCbSrOmRHFY8TAPBgNV\n" +
+        "HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqhkjOPQQDAgNJADBGAiEA5cJ/\n" +
+        "jirBbXxzpZ6kdp/Zb/yrIBnr4jiPGJTLgRTb8s4CIQChUDfP1Zqg0qJVfqFNaL4V\n" +
+        "a0EAeJHXGZnvCGGqHzoxkg==\n" +
+        "-----END CERTIFICATE-----",
+
+        // SHA256withRSA, 2048 bits
+        // Validity
+        //     Not Before: Nov 25 04:20:02 2016 GMT
+        //     Not After : Nov  5 04:20:02 2037 GMT
+        // Subject Key Identifier:
+        //     A2:DC:55:38:E4:47:7C:8B:D3:E0:CA:FA:AD:3A:C8:4A:DD:12:A0:8E
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIDpzCCAo+gAwIBAgIJAO586A+hYNXaMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
+        "BAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2Vy\n" +
+        "aXZjZTAeFw0xNjExMjUwNDIwMDJaFw0zNzExMDUwNDIwMDJaMDsxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMm3veDSU4zKXO0aAHos\n" +
+        "cFRXGLBTe+MUJXAtlkNyx7VKaMZNt5wrUuqzyi/r0LFUdRfNCzZf3X8s8HPHQVii\n" +
+        "29tK0y/yeTn4sJTATSmGaAysMJQpKQcfAQ79ItcEGQ721TFQZ3kOBdgp3t/yUYAP\n" +
+        "K2tFze/QbIw72LE52SBnPPPTzyimNw7Ai2MLl4eQlyMkTs7JS07zIiAO5QYbS8s+\n" +
+        "1NW0A3Y+d0B0q8wYEoHGq7QVjOKlSAksfO0tzi4l0Zu6Uf+J5kMAyZ4ZFgEJvGvw\n" +
+        "y/OKJ+soRFH/5cy1SL8B6AWD1y7WXugeeHTHRW1eOjTxqfG1rZqTVd2lfOMER8y1\n" +
+        "bXcCAwEAAaOBrTCBqjAdBgNVHQ4EFgQUotxVOORHfIvT4Mr6rTrISt0SoI4wawYD\n" +
+        "VR0jBGQwYoAUotxVOORHfIvT4Mr6rTrISt0SoI6hP6Q9MDsxCzAJBgNVBAYTAlVT\n" +
+        "MQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZjZYIJ\n" +
+        "AO586A+hYNXaMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3\n" +
+        "DQEBCwUAA4IBAQAtNZJSFeFU6Yid0WSCs2qLAVaTyHsUNSUPUFIIhFAKxdP4DFS0\n" +
+        "+aeOFwdqizAU3kReAYULsfwEBgO51lPBSpB+9coUNQwu7cc8Q5Xqw/llRB0PrINS\n" +
+        "pZl7PW6Ur2ExTBocnUT9A/nhm8iO4PFD/Ly11sf5OdZihhX69NJ2h3a3gcrLjIpO\n" +
+        "L/ewTOgSi5xs+AGGQa+huN3YVL7dh+/rCUvMZVSBX5PloxWS5MMJi0Ui6YjwCFGO\n" +
+        "J4G9m7pI+gJs/x1UG0AgplMF2UCFfiY1SAeE2nKAeOOXAXEwEjFy0ToVTmqXx7fg\n" +
+        "m9YjhuePxlBrd2DF/YW0pc8gmLnrtm4rKPLz\n" +
+        "-----END CERTIFICATE-----",
+
+        // SHA256withDSA, 2048 bits
+        // Validity
+        //     Not Before: Nov 25 04:19:56 2016 GMT
+        //     Not After : Nov  5 04:19:56 2037 GMT
+        // Subject Key Identifier:
+        //     19:46:10:43:24:6A:A5:14:BE:E2:92:01:79:F0:4C:5F:E1:AE:81:B5
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIFCzCCBLGgAwIBAgIJAOnEn6YZD/sAMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
+        "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+        "Y2UwHhcNMTYxMTI1MDQxOTU2WhcNMzcxMTA1MDQxOTU2WjA7MQswCQYDVQQGEwJV\n" +
+        "UzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2Uw\n" +
+        "ggNGMIICOQYHKoZIzjgEATCCAiwCggEBAJa17ZYdIChv5yeYuPK3zXxgUEGGsdUD\n" +
+        "AzfQUxtMryCtc3aNgWLxsN1/QYvp9v+hh4twnG20VemCEH9Qlx06Pxg74DwSOA83\n" +
+        "SecO2y7cdgmrHpep9drxKbXVZafwBhbTSwhV+IDO7EO6+LaRvZuya/YOqNIE9ENx\n" +
+        "FVk0NrNsDB6pfDEXZsCZALMN2mcl8KGn1q7vdzJQUEV7F6uLGP33znVfmQyWJH3Y\n" +
+        "W09WVCFXHvDzZHGXDO2O2QwIU1B5AsXnOGeLnKgXzErCoNKwUbVFP0W0OVeJo4pc\n" +
+        "ZfL/8TVELGG90AuuH1V3Gsq6PdzCDPw4Uv/v5m7/6kwXqBQxAJA7jhMCIQCORIGV\n" +
+        "mHy5nBLRhIP4vC7vsTxb4CTApPVmZeL5gTIwtQKCAQB2VZLY22k2+IQM6deRGK3L\n" +
+        "l7tPChGrKnGmTbtUThIza70Sp9DmTBiLzMEY+IgG8kYuT5STVxWjs0cxXCKZGMQW\n" +
+        "tioMtiXPA2M3HA0/8E0mDLSmzb0RAd2xxnDyGsuqo1eVmx7PLjN3bn3EjhD/+j3d\n" +
+        "Jx3ZVScMGyq7sVWppUvpudEXi+2etf6GUHjrcX27juo7u4zQ1ezC/HYG1H+jEFqG\n" +
+        "hdQ6b7H+LBHZH9LegOyIZTMrzAY/TwIr77sXrNJWRoxmDErKB+8bRDybYhNJswlZ\n" +
+        "m0N5YYUlPmepgbl6XzwCv0y0d81h3bayqIPLXEUtRAl9GuM0hNAlA1Y+qSn9xLFY\n" +
+        "A4IBBQACggEAZgWC0uflwqQQP1GRU1tolmFZwyVtKre7SjYgCeQBrOa0Xnj/SLaD\n" +
+        "g1HZ1oH0hccaR/45YouJiCretbbsQ77KouldGSGqTHJgRL75Y2z5uvxa60+YxZ0Z\n" +
+        "v8xvZnj4seyOjgJLxSSYSPl5n/F70RaNiCLVz/kGe6OQ8KoAeQjdDTOHXCegO9KX\n" +
+        "tvhM7EaYc8CII9OIR7S7PXJW0hgLKynZcu/Unh02aM0ABh/uLmw1+tvo8e8KTp98\n" +
+        "NKYSVf6kV3/ya58n4h64UbIYL08JoKUM/5SFETcKAZTU0YKZbpWTM79oJMr8oYVk\n" +
+        "P9jKitNsXq0Xkzt5dSO0kfu/kM7zpnaFsqOBrTCBqjAdBgNVHQ4EFgQUGUYQQyRq\n" +
+        "pRS+4pIBefBMX+GugbUwawYDVR0jBGQwYoAUGUYQQyRqpRS+4pIBefBMX+GugbWh\n" +
+        "P6Q9MDsxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5K\n" +
+        "U1NFIFRlc3QgU2VyaXZjZYIJAOnEn6YZD/sAMA8GA1UdEwEB/wQFMAMBAf8wCwYD\n" +
+        "VR0PBAQDAgEGMAsGCWCGSAFlAwQDAgNHADBEAiAwBafz5RRR9nc4cCYoYuBlT/D9\n" +
+        "9eayhkjhBY/zYunypwIgNp/JnFR88/T4hh36QfSKBGXId9RBCM6uaOkOKnEGkps=\n" +
+        "-----END CERTIFICATE-----"
+        };
+
+    // End entity certificate.
+    private final static String[] endEntityCertStrs = {
+        // SHA256withECDSA, curve prime256v1
+        // Validity
+        //     Not Before: Nov 25 04:19:51 2016 GMT
+        //     Not After : Aug 12 04:19:51 2036 GMT
+        // Authority Key Identifier:
+        //     CA:48:E8:00:C1:42:BD:59:9B:79:D9:B4:B4:CE:3F:68:0C:C8:C4:0C
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIB1zCCAXygAwIBAgIJAPFq2QL/nUNZMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTAeFw0xNjExMjUwNDE5NTFaFw0zNjA4MTIwNDE5NTFaMFUxCzAJBgNVBAYTAlVT\n" +
+        "MQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZjZTEY\n" +
+        "MBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" +
+        "QgAE4yvRGVvy9iVATyuHPJVdX6+lh/GLm/sRJ5qLT/3PVFOoNIvlEVNiARo7xhyj\n" +
+        "2p6bnf32gNg5Ye+QCw20VUv9E6NPME0wCwYDVR0PBAQDAgPoMB0GA1UdDgQWBBSO\n" +
+        "hHlHZQp9hyBfSGTSQWeszqMXejAfBgNVHSMEGDAWgBTKSOgAwUK9WZt52bS0zj9o\n" +
+        "DMjEDDAKBggqhkjOPQQDAgNJADBGAiEAu3t6cvFglBAZfkhZlEwB04ZjUFqyfiRj\n" +
+        "4Hr275E4ZoQCIQDUEonJHlmA19J6oobfR5lYsmoqPm1r0DPm/IiNNKGKKA==\n" +
+        "-----END CERTIFICATE-----",
+
+        // SHA256withRSA, 2048 bits
+        // Validity
+        //     Not Before: Nov 25 04:20:02 2016 GMT
+        //     Not After : Aug 12 04:20:02 2036 GMT
+        // Authority Key Identifier:
+        //     A2:DC:55:38:E4:47:7C:8B:D3:E0:CA:FA:AD:3A:C8:4A:DD:12:A0:8E
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIDdjCCAl6gAwIBAgIJAJDcIGOlAmBmMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
+        "BAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2Vy\n" +
+        "aXZjZTAeFw0xNjExMjUwNDIwMDJaFw0zNjA4MTIwNDIwMDJaMFUxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTEYMBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC\n" +
+        "AQ8AMIIBCgKCAQEAp0dHrifTg2aY0sH03f2JjK2fW4DL6gLDKq0YirsNE07z54LF\n" +
+        "IeeDio49XwPjB7OpbUTC1hf/YKZ7XiRWyPa1rYozZ88emhZt+cpkyKz+nmW4avlA\n" +
+        "WnrV+gx4+bU9T+WuBWdAraBcq27Y1I26yfCEtC8k3+O0sdlHbhasF+BUDmX/n4+n\n" +
+        "ifJdbNm5eSDx8eFYHFTdjhAud3An2X6QD9WWSoJpPdDi4utHhFAlxW6osjJxsAPv\n" +
+        "zo8YsqmpCMjZcEks4ZsqiZKKiWUWUAjCcbMerDPDX29fyeo348uztvJsmNRzfcwl\n" +
+        "FtwxpYdxuZqwHi2QoNaQTGXjjbZFmjn7qEkjXwIDAQABo2MwYTALBgNVHQ8EBAMC\n" +
+        "A+gwHQYDVR0OBBYEFP+henfufE6Znr60lRkmayadVdxnMB8GA1UdIwQYMBaAFKLc\n" +
+        "VTjkR3yL0+DK+q06yErdEqCOMBIGA1UdEQEB/wQIMAaHBH8AAAEwDQYJKoZIhvcN\n" +
+        "AQELBQADggEBAK56pV2XoAIkrHFTCkWtYX518nuvkzN6a6BqPKALQlmlbJnq/lhV\n" +
+        "tPQx79b0j7tow28l2ze/3M0hRb5Ft/d/7mITZNMR+0owk4U51AU2NacRt7fpoxu5\n" +
+        "wX3hTa4VgX2+BAXeoWF+Yzy6Jj5gAVmSLzBnkTUH0d+EyL1pp+DFE3QdvZqf3+nP\n" +
+        "zkxz15h3iW8FwI+7/19MX2j2XB/sG8mJpqoszWw8lM4qCa2eWyCbqSHhPi+/+rGg\n" +
+        "dDG5uzZeOC845GEH2T3tHDA+F3WwcZG/W+4RR6ZaaHlqPKKMcwFL73YbsqdCiKBv\n" +
+        "p6sXrhIiP0oXImRBRLDlidj5TIOLfAtNM9A=\n" +
+        "-----END CERTIFICATE-----",
+
+        // SHA256withDSA, 2048 bits
+        // Validity
+        //    Not Before: Nov 25 04:19:56 2016 GMT
+        //    Not After : Aug 12 04:19:56 2036 GMT
+        // Authority Key Identifier:
+        //     19:46:10:43:24:6A:A5:14:BE:E2:92:01:79:F0:4C:5F:E1:AE:81:B5
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIE2jCCBICgAwIBAgIJAONcI1oba9V9MAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
+        "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+        "Y2UwHhcNMTYxMTI1MDQxOTU2WhcNMzYwODEyMDQxOTU2WjBVMQswCQYDVQQGEwJV\n" +
+        "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" +
+        "GDAWBgNVBAMMD1JlZ3Jlc3Npb24gVGVzdDCCA0YwggI5BgcqhkjOOAQBMIICLAKC\n" +
+        "AQEAlrXtlh0gKG/nJ5i48rfNfGBQQYax1QMDN9BTG0yvIK1zdo2BYvGw3X9Bi+n2\n" +
+        "/6GHi3CcbbRV6YIQf1CXHTo/GDvgPBI4DzdJ5w7bLtx2Casel6n12vEptdVlp/AG\n" +
+        "FtNLCFX4gM7sQ7r4tpG9m7Jr9g6o0gT0Q3EVWTQ2s2wMHql8MRdmwJkAsw3aZyXw\n" +
+        "oafWru93MlBQRXsXq4sY/ffOdV+ZDJYkfdhbT1ZUIVce8PNkcZcM7Y7ZDAhTUHkC\n" +
+        "xec4Z4ucqBfMSsKg0rBRtUU/RbQ5V4mjilxl8v/xNUQsYb3QC64fVXcayro93MIM\n" +
+        "/DhS/+/mbv/qTBeoFDEAkDuOEwIhAI5EgZWYfLmcEtGEg/i8Lu+xPFvgJMCk9WZl\n" +
+        "4vmBMjC1AoIBAHZVktjbaTb4hAzp15EYrcuXu08KEasqcaZNu1ROEjNrvRKn0OZM\n" +
+        "GIvMwRj4iAbyRi5PlJNXFaOzRzFcIpkYxBa2Kgy2Jc8DYzccDT/wTSYMtKbNvREB\n" +
+        "3bHGcPIay6qjV5WbHs8uM3dufcSOEP/6Pd0nHdlVJwwbKruxVamlS+m50ReL7Z61\n" +
+        "/oZQeOtxfbuO6ju7jNDV7ML8dgbUf6MQWoaF1Dpvsf4sEdkf0t6A7IhlMyvMBj9P\n" +
+        "Aivvuxes0lZGjGYMSsoH7xtEPJtiE0mzCVmbQ3lhhSU+Z6mBuXpfPAK/TLR3zWHd\n" +
+        "trKog8tcRS1ECX0a4zSE0CUDVj6pKf3EsVgDggEFAAKCAQBEGmdP55PyE3M+Q3fU\n" +
+        "dCGq0sbKw/04xPVhaNYRnRKNR82n+wb8bMCI1vvFqXy1BB6svti4mTHbQZ8+bQXm\n" +
+        "gyce67uYMwIa5BIk6omNGCeW/kd4ruPgyFxeb6O/Y/7w6AWyRmQttlxRA5M5OhSC\n" +
+        "tVS4oVC1KK1EfHAUh7mu8S8GrWJoJAWA3PM97Oy/HSGCEUl6HGEu1m7FHPhOKeYG\n" +
+        "cLkSaov5cbCYO76smHchI+tdUciVqeL3YKQdS+KAzsQoeAZIu/WpbaI1V+5/rSG1\n" +
+        "I94uBITLCjlJfJZ1aredCDrRXOFH7qgSBhM8/WzwFpFCnnpbSKMgrcrKubsFmW9E\n" +
+        "jQhXo2MwYTALBgNVHQ8EBAMCA+gwHQYDVR0OBBYEFNA9PhQOjB+05fxxXPNqe0OT\n" +
+        "doCjMB8GA1UdIwQYMBaAFBlGEEMkaqUUvuKSAXnwTF/hroG1MBIGA1UdEQEB/wQI\n" +
+        "MAaHBH8AAAEwCwYJYIZIAWUDBAMCA0cAMEQCIE0LM2sZi+L8tjH9sgjLEwJmYZvO\n" +
+        "yqNfQnXrkTCb+MLMAiBZLaRTVJrOW3edQjum+SonKKuiN22bRclO6pGuNRCtng==\n" +
+        "-----END CERTIFICATE-----"
+        };
+
+    // Private key in the format of PKCS#8.
+    private final static String[] endEntityPrivateKeys = {
+        //
+        // EC private key related to cert endEntityCertStrs[0].
+        //
+        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGAy4Pxrd2keM7AdP\n" +
+        "VNUMEO5iO681v4/tstVGfdXkCTuhRANCAATjK9EZW/L2JUBPK4c8lV1fr6WH8Yub\n" +
+        "+xEnmotP/c9UU6g0i+URU2IBGjvGHKPanpud/faA2Dlh75ALDbRVS/0T",
+
+        //
+        // RSA private key related to cert endEntityCertStrs[1].
+        //
+        "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnR0euJ9ODZpjS\n" +
+        "wfTd/YmMrZ9bgMvqAsMqrRiKuw0TTvPngsUh54OKjj1fA+MHs6ltRMLWF/9gpnte\n" +
+        "JFbI9rWtijNnzx6aFm35ymTIrP6eZbhq+UBaetX6DHj5tT1P5a4FZ0CtoFyrbtjU\n" +
+        "jbrJ8IS0LyTf47Sx2UduFqwX4FQOZf+fj6eJ8l1s2bl5IPHx4VgcVN2OEC53cCfZ\n" +
+        "fpAP1ZZKgmk90OLi60eEUCXFbqiyMnGwA+/OjxiyqakIyNlwSSzhmyqJkoqJZRZQ\n" +
+        "CMJxsx6sM8Nfb1/J6jfjy7O28myY1HN9zCUW3DGlh3G5mrAeLZCg1pBMZeONtkWa\n" +
+        "OfuoSSNfAgMBAAECggEAWnAHKPkPObN2XDvQj1RL0WrtBSOVG2dy7Ne4tQh8ATxm\n" +
+        "UXw56CKq03YjaANJ8xgHObQ7QlSnFTHs8PDkmrIHd1OIh09LVDNcMfhilLwyzKBi\n" +
+        "HDO1vzU6Cn5DyX1bMJ8UfodcSIKyl1zOjdwyaItIs8HpRcJuJtk57SME18PIrh9H\n" +
+        "EWchWSxTvPvKDY2bhb4vBMgVPfTQO3yc8gY/1J5vKPqDpyEuCGjV13zd/AoL/u5A\n" +
+        "sG10hQ2veJ9KAn1xwPwEoAkCdNLL8vPB1rCbeMZNamqHZRihfoOgDnnJIf3FtUFF\n" +
+        "8bS2FM2kGQR+05SZdhBmJl0obPrbBWil/2vxqeFrgQKBgQDZl1yQsFss2BKK2VAh\n" +
+        "9PKc8tU1v6RpHQZhJEDSct2slHQS5DF6bWl5kJiPevXSvcnjgoPamZf7Joii+Rds\n" +
+        "3xtPQmRw2vQmXBQzQS1YoRuh4hUlkdFWCjUNNg1kvipue6FO4PVg3ViP7llN8PXK\n" +
+        "rSpVxn0a36UiN9jN2zMOUs6tJwKBgQDEzlqa7DghMli7TUdL44uvD2/ceiHqHMMw\n" +
+        "5eLsEHeRVn/EVU99beKw/dAOGwRssTpCd9h7fwzQ2503/Qb/Goe0nKE7+xvt3/sE\n" +
+        "n2Y8Qfv1W1+hGb2qU2jhQaR5bZrLZp0+BgRuQ4ttpYvzopYe4FLZWhDBA0zsGyu0\n" +
+        "nCi7lUSrCQKBgGeGYW8hyS9r2l6fiEWvsiLEUnbRKFsuiRN82S6HojpzI0q9sWDL\n" +
+        "X6yMBFn3qa/LxpttRGikPTAsJERN+Tw+ZlLuhrU/J3x8wMumDfomJOx/kYofd5bV\n" +
+        "ImqXtgWhiLSqM5RA6d5dUb6hK3Iu2/LDMuo+ltVLZNkD8y32RbNh6J1vAoGAbLqQ\n" +
+        "pgyhSf3Vtc0Q+aVB87p0k3tKJ1wynl4zSzYhyMLgHakAHIzL8/qVqmVUwXP8euJZ\n" +
+        "UIk1nGHobxk0d1XB6Y+rKEcn+/iFZt1ljx7pQ3ly0L824NXqGKC6bHeYUI1li/Gp\n" +
+        "Gv3oFvCh7D1D8NUAEKLIpMndAohUUhkAC/qAkHkCgYEAzSIarDNquayV+umC1SXm\n" +
+        "Zo6XLuzWjudLxPd2lyCfwR2aRKlrb+5OFYErX+RSLyCJmaqVZMyXP09PBIvNXu2Z\n" +
+        "+gbx5WUC+kA+6zdKEPXowei6i6EHMXYT2AL7395ZbPajZjsCduE3WuUztuHrhtMm\n" +
+        "JI+k1o4rCnSLlX4gWdN1oTs=",
+
+        //
+        // DSA private key related to cert endEntityCertStrs[2].
+        //
+        "MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQCWte2WHSAob+cnmLjyt818YFBB\n" +
+        "hrHVAwM30FMbTK8grXN2jYFi8bDdf0GL6fb/oYeLcJxttFXpghB/UJcdOj8YO+A8\n" +
+        "EjgPN0nnDtsu3HYJqx6XqfXa8Sm11WWn8AYW00sIVfiAzuxDuvi2kb2bsmv2DqjS\n" +
+        "BPRDcRVZNDazbAweqXwxF2bAmQCzDdpnJfChp9au73cyUFBFexerixj99851X5kM\n" +
+        "liR92FtPVlQhVx7w82RxlwztjtkMCFNQeQLF5zhni5yoF8xKwqDSsFG1RT9FtDlX\n" +
+        "iaOKXGXy//E1RCxhvdALrh9VdxrKuj3cwgz8OFL/7+Zu/+pMF6gUMQCQO44TAiEA\n" +
+        "jkSBlZh8uZwS0YSD+Lwu77E8W+AkwKT1ZmXi+YEyMLUCggEAdlWS2NtpNviEDOnX\n" +
+        "kRity5e7TwoRqypxpk27VE4SM2u9EqfQ5kwYi8zBGPiIBvJGLk+Uk1cVo7NHMVwi\n" +
+        "mRjEFrYqDLYlzwNjNxwNP/BNJgy0ps29EQHdscZw8hrLqqNXlZsezy4zd259xI4Q\n" +
+        "//o93Scd2VUnDBsqu7FVqaVL6bnRF4vtnrX+hlB463F9u47qO7uM0NXswvx2BtR/\n" +
+        "oxBahoXUOm+x/iwR2R/S3oDsiGUzK8wGP08CK++7F6zSVkaMZgxKygfvG0Q8m2IT\n" +
+        "SbMJWZtDeWGFJT5nqYG5el88Ar9MtHfNYd22sqiDy1xFLUQJfRrjNITQJQNWPqkp\n" +
+        "/cSxWAQiAiAKHYbYwEy0XS9J0MeKQmqPswn0nCJKvH+esfMKkZvV3w=="
+        };
+
+    // Private key names of endEntityPrivateKeys.
+    private final static String[] endEntityPrivateKeyNames = {
+        "EC",
+        "RSA",
+        "DSA",
+        };
+
+    /*
+     * Create an instance of SSLContext with the specified trust/key materials.
+     */
+    private SSLContext createSSLContext(
+            String[] trustedMaterials,
+            String[] keyMaterialCerts,
+            String[] keyMaterialKeys,
+            String[] keyMaterialKeyAlgs,
+            ContextParameters params) throws Exception {
+
+        KeyStore ts = null;     // trust store
+        KeyStore ks = null;     // key store
+        char passphrase[] = "passphrase".toCharArray();
+
+        // Generate certificate from cert string.
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        // Import the trused certs.
+        ByteArrayInputStream is;
+        if (trustedMaterials != null && trustedMaterials.length != 0) {
+            ts = KeyStore.getInstance("JKS");
+            ts.load(null, null);
+
+            Certificate[] trustedCert =
+                    new Certificate[trustedMaterials.length];
+            for (int i = 0; i < trustedMaterials.length; i++) {
+                String trustedCertStr = trustedMaterials[i];
+
+                is = new ByteArrayInputStream(trustedCertStr.getBytes());
+                try {
+                    trustedCert[i] = cf.generateCertificate(is);
+                } finally {
+                    is.close();
+                }
+
+                ts.setCertificateEntry("trusted-cert-" + i, trustedCert[i]);
+            }
+        }
+
+        // Import the key materials.
+        //
+        // Note that certification pathes bigger than one are not supported yet.
+        boolean hasKeyMaterials =
+            (keyMaterialCerts != null) && (keyMaterialCerts.length != 0) &&
+            (keyMaterialKeys != null) && (keyMaterialKeys.length != 0) &&
+            (keyMaterialKeyAlgs != null) && (keyMaterialKeyAlgs.length != 0) &&
+            (keyMaterialCerts.length == keyMaterialKeys.length) &&
+            (keyMaterialCerts.length == keyMaterialKeyAlgs.length);
+        if (hasKeyMaterials) {
+            ks = KeyStore.getInstance("JKS");
+            ks.load(null, null);
+
+            for (int i = 0; i < keyMaterialCerts.length; i++) {
+                String keyCertStr = keyMaterialCerts[i];
+
+                // generate the private key.
+                PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
+                    Base64.getMimeDecoder().decode(keyMaterialKeys[i]));
+                KeyFactory kf =
+                    KeyFactory.getInstance(keyMaterialKeyAlgs[i]);
+                PrivateKey priKey = kf.generatePrivate(priKeySpec);
+
+                // generate certificate chain
+                is = new ByteArrayInputStream(keyCertStr.getBytes());
+                Certificate keyCert = null;
+                try {
+                    keyCert = cf.generateCertificate(is);
+                } finally {
+                    is.close();
+                }
+
+                Certificate[] chain = new Certificate[] { keyCert };
+
+                // import the key entry.
+                ks.setKeyEntry("cert-" + keyMaterialKeyAlgs[i],
+                        priKey, passphrase, chain);
+            }
+        }
+
+        // Create an SSLContext object.
+        TrustManagerFactory tmf =
+                TrustManagerFactory.getInstance(params.tmAlgorithm);
+        tmf.init(ts);
+
+        SSLContext context = SSLContext.getInstance(params.contextProtocol);
+        if (hasKeyMaterials && ks != null) {
+            KeyManagerFactory kmf =
+                    KeyManagerFactory.getInstance(params.kmAlgorithm);
+            kmf.init(ks, passphrase);
+
+            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+        } else {
+            context.init(null, tmf.getTrustManagers(), null);
+        }
+
+        return context;
+    }
+
+    /*
+     * =================================================
+     * Stuffs to boot up the client-server mode testing.
+     */
+    private Thread clientThread = null;
+    private Thread serverThread = null;
+    private volatile Exception serverException = null;
+    private volatile Exception clientException = null;
+
+    /*
+     * 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.
+     */
+    private static final boolean separateServerThread = false;
+
+    /*
+     * Boot up the testing, used to drive remainder of the test.
+     */
+    private void bootup() throws Exception {
+        Exception startException = null;
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            startException = e;
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            if (serverThread != null) {
+                serverThread.join();
+            }
+        } else {
+            if (clientThread != null) {
+                clientThread.join();
+            }
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+        } else {
+            remote = clientException;
+            local = serverException;
+        }
+
+        Exception exception = null;
+
+        /*
+         * Check various exception conditions.
+         */
+        if ((local != null) && (remote != null)) {
+            // If both failed, return the curthread's exception.
+            local.initCause(remote);
+            exception = local;
+        } else if (local != null) {
+            exception = local;
+        } else if (remote != null) {
+            exception = remote;
+        } else if (startException != null) {
+            exception = startException;
+        }
+
+        /*
+         * If there was an exception *AND* a startException,
+         * output it.
+         */
+        if (exception != null) {
+            if (exception != startException && startException != null) {
+                exception.addSuppressed(startException);
+            }
+            throw exception;
+        }
+
+        // Fall-through: no exception to throw!
+    }
+
+    private void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        logException("Server died", e);
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                logException("Server failed", e);
+                serverException = e;
+            }
+        }
+    }
+
+    private void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        logException("Client died", e);
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                logException("Client failed", e);
+                clientException = e;
+            }
+        }
+    }
+
+    private synchronized void logException(String prefix, Throwable cause) {
+        System.out.println(prefix + ": " + cause);
+        cause.printStackTrace(System.out);
+    }
+}
--- a/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/DHKeyExchange/DHEKeySizing.java	Thu May 25 21:21:06 2017 -0700
+++ b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/DHKeyExchange/DHEKeySizing.java	Thu May 25 21:22:55 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -31,33 +31,44 @@
  * @bug 6956398
  * @summary make ephemeral DH key match the length of the certificate key
  * @run main/othervm
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1639 267
+ * @run main/othervm -Djsse.enableFFDHE=false
  *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=matched
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=matched
  *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=legacy
  *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=1024
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=1024
  *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
  *
- * @run main/othervm
+ * @run main/othervm -Djsse.enableFFDHE=false
  *      DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 229 75
  *
- * @run main/othervm
+ * @run main/othervm -Djsse.enableFFDHE=false
  *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1383 139
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=legacy
  *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1319 107
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=matched
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=matched
  *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1639 267
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=1024
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=1024
  *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1383 139
  *
- * @run main/othervm
+ * @run main/othervm -Djsse.enableFFDHE=false
  *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=legacy
  *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 293 107
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=matched
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=matched
  *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
- * @run main/othervm -Djdk.tls.ephemeralDHKeySize=1024
+ * @run main/othervm -Djsse.enableFFDHE=false
+ *      -Djdk.tls.ephemeralDHKeySize=1024
  *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
  */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/DHKeyExchange/UseStrongDHSizes.java	Thu May 25 21:22:55 2017 -0700
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * 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 8140436
+ * @modules jdk.crypto.ec
+ * @library /javax/net/ssl/templates
+ * @summary Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for TLS
+ * @run main/othervm UseStrongDHSizes 2048
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Security;
+import javax.net.ssl.SSLSocket;
+
+public class UseStrongDHSizes extends SSLSocketTemplate {
+    /*
+     * Run the test case.
+     */
+    public static void main(String[] args) throws Exception {
+        // reset the security property to make sure that the algorithms
+        // and keys used in this test are not disabled unexpectedly.
+        String constraint = "DH keySize < " + Integer.valueOf(args[0]);
+        Security.setProperty("jdk.tls.disabledAlgorithms", constraint);
+        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
+        (new UseStrongDHSizes()).run();
+    }
+
+    @Override
+    protected void runServerApplication(SSLSocket socket) throws Exception {
+        String ciphers[] = {
+                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+                "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+                "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"};
+
+        socket.setEnabledCipherSuites(ciphers);
+        socket.setWantClientAuth(true);
+
+        InputStream sslIS = socket.getInputStream();
+        OutputStream sslOS = socket.getOutputStream();
+
+        sslIS.read();
+        sslOS.write(85);
+        sslOS.flush();
+    }
+
+    @Override
+    protected void runClientApplication(SSLSocket socket) throws Exception {
+        String ciphers[] = {
+                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+                "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+                "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"};
+        socket.setEnabledCipherSuites(ciphers);
+        socket.setUseClientMode(true);
+
+        InputStream sslIS = socket.getInputStream();
+        OutputStream sslOS = socket.getOutputStream();
+
+        sslOS.write(280);
+        sslOS.flush();
+        sslIS.read();
+    }
+}