changeset 55853:946f7f2d321c

8171279: Support X25519 and X448 in TLS Reviewed-by: xuelei, mullan
author wetmore
date Wed, 12 Jun 2019 18:58:00 -0700
parents 1357c4996b2e
children 74f0622db875
files src/java.base/share/classes/sun/security/ssl/CipherSuite.java src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java src/java.base/share/classes/sun/security/ssl/HandshakeContext.java src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java src/java.base/share/classes/sun/security/ssl/NamedGroup.java src/java.base/share/classes/sun/security/ssl/NamedGroupCredentials.java src/java.base/share/classes/sun/security/ssl/NamedGroupPossession.java src/java.base/share/classes/sun/security/ssl/SSLExtension.java src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java src/java.base/share/classes/sun/security/ssl/SignatureScheme.java src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java src/java.base/share/classes/sun/security/ssl/TransportContext.java src/java.base/share/classes/sun/security/ssl/Utilities.java src/java.base/share/classes/sun/security/ssl/X509Authentication.java src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java
diffstat 27 files changed, 1788 insertions(+), 1000 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Wed Jun 12 18:58:00 2019 -0700
@@ -35,8 +35,8 @@
 import static sun.security.ssl.CipherSuite.KeyExchange.*;
 import static sun.security.ssl.CipherSuite.MacAlg.*;
 import static sun.security.ssl.SSLCipher.*;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
-import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
+import sun.security.ssl.NamedGroup.NamedGroupType;
+import static sun.security.ssl.NamedGroup.NamedGroupType.*;
 
 /**
  * Enum for SSL/(D)TLS cipher suites.
@@ -184,7 +184,7 @@
             K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256),
 
     //
-    // not forward screcy cipher suites.
+    // not forward secret cipher suites.
     //
 
     // AES_256(GCM)
@@ -1106,11 +1106,18 @@
         K_DH_ANON       ("DH_anon",        true,  true,   NAMED_GROUP_FFDHE),
         K_DH_ANON_EXPORT("DH_anon_EXPORT", true,  true,   NAMED_GROUP_NONE),
 
-        K_ECDH_ECDSA    ("ECDH_ECDSA",     true,  false,  NAMED_GROUP_ECDHE),
-        K_ECDH_RSA      ("ECDH_RSA",       true,  false,  NAMED_GROUP_ECDHE),
-        K_ECDHE_ECDSA   ("ECDHE_ECDSA",    true,  false,  NAMED_GROUP_ECDHE),
-        K_ECDHE_RSA     ("ECDHE_RSA",      true,  false,  NAMED_GROUP_ECDHE),
-        K_ECDH_ANON     ("ECDH_anon",      true,  true,   NAMED_GROUP_ECDHE),
+        // These KeyExchanges can use either ECDHE/XDH, so we'll use a
+        // varargs here.
+        K_ECDH_ECDSA    ("ECDH_ECDSA",     JsseJce.ALLOW_ECC,  false,
+                NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
+        K_ECDH_RSA      ("ECDH_RSA",       JsseJce.ALLOW_ECC,  false,
+            NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
+        K_ECDHE_ECDSA   ("ECDHE_ECDSA",    JsseJce.ALLOW_ECC,  false,
+            NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
+        K_ECDHE_RSA     ("ECDHE_RSA",      JsseJce.ALLOW_ECC,  false,
+            NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
+        K_ECDH_ANON     ("ECDH_anon",      JsseJce.ALLOW_ECC,  true,
+            NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
 
         // renegotiation protection request signaling cipher suite
         K_SCSV          ("SCSV",           true,  true,   NAMED_GROUP_NONE);
@@ -1118,19 +1125,16 @@
         // name of the key exchange algorithm, e.g. DHE_DSS
         final String name;
         final boolean allowed;
-        final NamedGroupType groupType;
+        final NamedGroupType[] groupTypes;
         private final boolean alwaysAvailable;
         private final boolean isAnonymous;
 
         KeyExchange(String name, boolean allowed,
-                boolean isAnonymous, NamedGroupType groupType) {
+                boolean isAnonymous, NamedGroupType... groupTypes) {
             this.name = name;
-            if (groupType == NAMED_GROUP_ECDHE) {
-                this.allowed = JsseJce.ALLOW_ECC;
-            } else {
-                this.allowed = allowed;
-            }
-            this.groupType = groupType;
+            this.groupTypes = groupTypes;
+            this.allowed = allowed;
+
             this.alwaysAvailable = allowed && (!name.startsWith("EC"));
             this.isAnonymous = isAnonymous;
         }
@@ -1140,7 +1144,8 @@
                 return true;
             }
 
-            if (groupType == NAMED_GROUP_ECDHE) {
+            if (NamedGroupType.arrayContains(
+                    groupTypes, NamedGroupType.NAMED_GROUP_ECDHE)) {
                 return (allowed && JsseJce.isEcAvailable());
             } else {
                 return allowed;
--- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -42,7 +42,6 @@
 import sun.security.ssl.DHKeyExchange.DHECredentials;
 import sun.security.ssl.DHKeyExchange.DHEPossession;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 import sun.security.util.HexDumpEncoder;
 
 /**
--- a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -36,19 +36,12 @@
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.InvalidKeySpecException;
-import javax.crypto.KeyAgreement;
-import javax.crypto.SecretKey;
 import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.DHParameterSpec;
 import javax.crypto.spec.DHPublicKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLHandshakeException;
 import sun.security.action.GetPropertyAction;
-import sun.security.ssl.CipherSuite.HashAlg;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupType;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Possession;
 import sun.security.util.KeyUtil;
@@ -61,7 +54,7 @@
     static final SSLKeyAgreementGenerator kaGenerator =
             new DHEKAGenerator();
 
-    static final class DHECredentials implements SSLCredentials {
+    static final class DHECredentials implements NamedGroupCredentials {
         final DHPublicKey popPublicKey;
         final NamedGroup namedGroup;
 
@@ -70,6 +63,16 @@
             this.namedGroup = namedGroup;
         }
 
+        @Override
+        public PublicKey getPublicKey() {
+            return popPublicKey;
+        }
+
+        @Override
+        public NamedGroup getNamedGroup() {
+            return namedGroup;
+        }
+
         static DHECredentials valueOf(NamedGroup ng,
             byte[] encodedPublic) throws IOException, GeneralSecurityException {
 
@@ -98,7 +101,7 @@
         }
     }
 
-    static final class DHEPossession implements SSLPossession {
+    static final class DHEPossession implements NamedGroupPossession {
         final PrivateKey privateKey;
         final DHPublicKey publicKey;
         final NamedGroup namedGroup;
@@ -174,13 +177,13 @@
         // Generate and validate DHPublicKeySpec
         private KeyPair generateDHKeyPair(
                 KeyPairGenerator kpg) throws GeneralSecurityException {
-            boolean doExtraValiadtion =
+            boolean doExtraValidation =
                     (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
             boolean isRecovering = false;
             for (int i = 0; i <= 2; i++) {      // Try to recover from failure.
                 KeyPair kp = kpg.generateKeyPair();
                 // validate the Diffie-Hellman public key
-                if (doExtraValiadtion) {
+                if (doExtraValidation) {
                     DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
                     try {
                         KeyUtil.validate(spec);
@@ -231,6 +234,21 @@
 
             return encoded;
         }
+
+        @Override
+        public PublicKey getPublicKey() {
+            return publicKey;
+        }
+
+        @Override
+        public NamedGroup getNamedGroup() {
+            return namedGroup;
+        }
+
+        @Override
+        public PrivateKey getPrivateKey() {
+            return privateKey;
+        }
     }
 
     private static final class
@@ -298,7 +316,7 @@
         // Used for ServerKeyExchange, TLS 1.2 and prior versions.
         @Override
         public SSLPossession createPossession(HandshakeContext context) {
-            NamedGroup preferableNamedGroup = null;
+            NamedGroup preferableNamedGroup;
             if (!useLegacyEphemeralDHKeys &&
                     (context.clientRequestedNamedGroups != null) &&
                     (!context.clientRequestedNamedGroups.isEmpty())) {
@@ -306,7 +324,8 @@
                         SupportedGroups.getPreferredGroup(
                                 context.negotiatedProtocol,
                                 context.algorithmConstraints,
-                                NamedGroupType.NAMED_GROUP_FFDHE,
+                                new NamedGroupType [] {
+                                    NamedGroupType.NAMED_GROUP_FFDHE },
                                 context.clientRequestedNamedGroups);
                 if (preferableNamedGroup != null) {
                     return new DHEPossession(preferableNamedGroup,
@@ -392,7 +411,7 @@
 
     private static final
             class DHEKAGenerator implements SSLKeyAgreementGenerator {
-        static private DHEKAGenerator instance = new DHEKAGenerator();
+        private static final DHEKAGenerator instance = new DHEKAGenerator();
 
         // Prevent instantiation of this class.
         private DHEKAGenerator() {
@@ -442,93 +461,8 @@
                     "No sufficient DHE key agreement parameters negotiated");
             }
 
-            return new DHEKAKeyDerivation(context,
+            return new KAKeyDerivation("DiffieHellman", context,
                     dhePossession.privateKey, dheCredentials.popPublicKey);
         }
-
-        private static final
-                class DHEKAKeyDerivation implements SSLKeyDerivation {
-            private final HandshakeContext context;
-            private final PrivateKey localPrivateKey;
-            private final PublicKey peerPublicKey;
-
-            DHEKAKeyDerivation(HandshakeContext context,
-                    PrivateKey localPrivateKey,
-                    PublicKey peerPublicKey) {
-                this.context = context;
-                this.localPrivateKey = localPrivateKey;
-                this.peerPublicKey = peerPublicKey;
-            }
-
-            @Override
-            public SecretKey deriveKey(String algorithm,
-                    AlgorithmParameterSpec params) throws IOException {
-                if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
-                    return t12DeriveKey(algorithm, params);
-                } else {
-                    return t13DeriveKey(algorithm, params);
-                }
-            }
-
-            private SecretKey t12DeriveKey(String algorithm,
-                    AlgorithmParameterSpec params) throws IOException {
-                try {
-                    KeyAgreement ka = KeyAgreement.getInstance("DiffieHellman");
-                    ka.init(localPrivateKey);
-                    ka.doPhase(peerPublicKey, true);
-                    SecretKey preMasterSecret =
-                            ka.generateSecret("TlsPremasterSecret");
-                    SSLMasterKeyDerivation mskd =
-                            SSLMasterKeyDerivation.valueOf(
-                                    context.negotiatedProtocol);
-                    if (mskd == null) {
-                        // unlikely
-                        throw new SSLHandshakeException(
-                            "No expected master key derivation for protocol: " +
-                            context.negotiatedProtocol.name);
-                    }
-                    SSLKeyDerivation kd = mskd.createKeyDerivation(
-                            context, preMasterSecret);
-                    return kd.deriveKey("MasterSecret", params);
-                } catch (GeneralSecurityException gse) {
-                    throw (SSLHandshakeException) new SSLHandshakeException(
-                        "Could not generate secret").initCause(gse);
-                }
-            }
-
-            private SecretKey t13DeriveKey(String algorithm,
-                    AlgorithmParameterSpec params) throws IOException {
-                try {
-                    KeyAgreement ka = KeyAgreement.getInstance("DiffieHellman");
-                    ka.init(localPrivateKey);
-                    ka.doPhase(peerPublicKey, true);
-                    SecretKey sharedSecret =
-                            ka.generateSecret("TlsPremasterSecret");
-
-                    HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
-                    SSLKeyDerivation kd = context.handshakeKeyDerivation;
-                    HKDF hkdf = new HKDF(hashAlg.name);
-                    if (kd == null) {   // No PSK is in use.
-                        // If PSK is not in use Early Secret will still be
-                        // HKDF-Extract(0, 0).
-                        byte[] zeros = new byte[hashAlg.hashLength];
-                        SecretKeySpec ikm =
-                                new SecretKeySpec(zeros, "TlsPreSharedSecret");
-                        SecretKey earlySecret =
-                                hkdf.extract(zeros, ikm, "TlsEarlySecret");
-                        kd = new SSLSecretDerivation(context, earlySecret);
-                    }
-
-                    // derive salt secret
-                    SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
-
-                    // derive handshake secret
-                    return hkdf.extract(saltSecret, sharedSecret, algorithm);
-                } catch (GeneralSecurityException gse) {
-                    throw (SSLHandshakeException) new SSLHandshakeException(
-                        "Could not generate secret").initCause(gse);
-                }
-            }
-        }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -48,7 +48,6 @@
 import sun.security.ssl.DHKeyExchange.DHECredentials;
 import sun.security.ssl.DHKeyExchange.DHEPossession;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 import sun.security.ssl.X509Authentication.X509Credentials;
 import sun.security.ssl.X509Authentication.X509Possession;
 import sun.security.util.HexDumpEncoder;
--- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -27,31 +27,28 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.security.AlgorithmConstraints;
-import java.security.CryptoPrimitive;
 import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
 import java.security.PublicKey;
 import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.XECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
+import java.security.spec.NamedParameterSpec;
 import java.text.MessageFormat;
-import java.util.EnumSet;
 import java.util.Locale;
 import javax.crypto.SecretKey;
-import javax.net.ssl.SSLHandshakeException;
-import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
-import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 import sun.security.ssl.X509Authentication.X509Credentials;
 import sun.security.ssl.X509Authentication.X509Possession;
-import sun.security.util.ECUtil;
 import sun.security.util.HexDumpEncoder;
 
 /**
  * Pack of the "ClientKeyExchange" handshake message.
+ *
+ * This file is used by both the ECDH/ECDHE/XDH code since much of the
+ * code is the same between the EC named groups (i.e.
+ * x25519/x448/secp*r1), even though the APIs are very different (i.e.
+ * ECPublicKey/XECPublicKey, KeyExchange.getInstance("EC"/"XDH"), etc.).
  */
 final class ECDHClientKeyExchange {
     static final SSLConsumer ecdhHandshakeConsumer =
@@ -65,19 +62,17 @@
             new ECDHEClientKeyExchangeProducer();
 
     /**
-     * The ECDH/ECDHE ClientKeyExchange handshake message.
+     * The ECDH/ECDHE/XDH ClientKeyExchange handshake message.
      */
     private static final
             class ECDHClientKeyExchangeMessage extends HandshakeMessage {
         private final byte[] encodedPoint;
 
         ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
-                ECPublicKey publicKey) {
+                byte[] encodedPublicKey) {
             super(handshakeContext);
 
-            ECPoint point = publicKey.getW();
-            ECParameterSpec params = publicKey.getParams();
-            encodedPoint = ECUtil.encodePoint(point, params.getCurve());
+            this.encodedPoint = encodedPublicKey;
         }
 
         ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
@@ -90,34 +85,6 @@
             }
         }
 
-        // Check constraints of the specified EC public key.
-        static void checkConstraints(AlgorithmConstraints constraints,
-                ECPublicKey publicKey,
-                byte[] encodedPoint) throws SSLHandshakeException {
-
-            try {
-                ECParameterSpec params = publicKey.getParams();
-                ECPoint point =
-                        ECUtil.decodePoint(encodedPoint, params.getCurve());
-                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
-
-                KeyFactory kf = KeyFactory.getInstance("EC");
-                ECPublicKey peerPublicKey =
-                        (ECPublicKey)kf.generatePublic(spec);
-
-                // check constraints of ECPublicKey
-                if (!constraints.permits(
-                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                        peerPublicKey)) {
-                    throw new SSLHandshakeException(
-                        "ECPublicKey does not comply to algorithm constraints");
-                }
-            } catch (GeneralSecurityException | java.io.IOException e) {
-                throw (SSLHandshakeException) new SSLHandshakeException(
-                        "Could not generate ECPublicKey").initCause(e);
-            }
-        }
-
         @Override
         public SSLHandshake handshakeType() {
             return SSLHandshake.CLIENT_KEY_EXCHANGE;
@@ -194,24 +161,41 @@
             }
 
             PublicKey publicKey = x509Credentials.popPublicKey;
-            if (!publicKey.getAlgorithm().equals("EC")) {
+
+            NamedGroup namedGroup = null;
+            String algorithm = publicKey.getAlgorithm();
+
+            // Determine which NamedGroup we'll be using, then use
+            // the creator functions.
+            if (algorithm.equals("EC")) {
+                ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
+                namedGroup = NamedGroup.valueOf(params);
+            } else if (algorithm.equals("XDH")) {
+                AlgorithmParameterSpec params =
+                        ((XECPublicKey)publicKey).getParams();
+                if (params instanceof NamedParameterSpec) {
+                    String name = ((NamedParameterSpec)params).getName();
+                    namedGroup = NamedGroup.nameOf(name);
+                }
+            } else {
                 throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Not EC server certificate for ECDH client key exchange");
+                    "Not EC/XDH server certificate for " +
+                            "ECDH client key exchange");
             }
 
-            ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
-            NamedGroup namedGroup = NamedGroup.valueOf(params);
             if (namedGroup == null) {
                 throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Unsupported EC server cert for ECDH client key exchange");
+                    "Unsupported EC/XDH server cert for " +
+                        "ECDH client key exchange");
             }
 
-            ECDHEPossession ecdhePossession = new ECDHEPossession(
-                    namedGroup, chc.sslContext.getSecureRandom());
-            chc.handshakePossessions.add(ecdhePossession);
+            SSLPossession sslPossession = namedGroup.createPossession(
+                    chc.sslContext.getSecureRandom());
+
+            chc.handshakePossessions.add(sslPossession);
             ECDHClientKeyExchangeMessage cke =
                     new ECDHClientKeyExchangeMessage(
-                            chc, ecdhePossession.publicKey);
+                            chc, sslPossession.encode());
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                 SSLLogger.fine(
                     "Produced ECDH ClientKeyExchange handshake message", cke);
@@ -283,18 +267,35 @@
                     "No expected EC server cert for ECDH client key exchange");
             }
 
-            ECParameterSpec params = x509Possession.getECParameterSpec();
-            if (params == null) {
+            // Determine which NamedGroup we'll be using, then use
+            // the creator functions.
+            NamedGroup namedGroup = null;
+
+            // Iteratively determine the X509Possession type's ParameterSpec.
+            ECParameterSpec ecParams = x509Possession.getECParameterSpec();
+            NamedParameterSpec namedParams = null;
+            if (ecParams != null) {
+                namedGroup = NamedGroup.valueOf(ecParams);
+            }
+
+            // Wasn't EC, try XEC.
+            if (ecParams == null) {
+                namedParams = x509Possession.getXECParameterSpec();
+                namedGroup = NamedGroup.nameOf(namedParams.getName());
+            }
+
+            // Can't figure this out, bail.
+            if ((ecParams == null) && (namedParams == null)) {
                 // unlikely, have been checked during cipher suite negotiation.
                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Not EC server cert for ECDH client key exchange");
+                    "Not EC/XDH server cert for ECDH client key exchange");
             }
 
-            NamedGroup namedGroup = NamedGroup.valueOf(params);
+            // unlikely, have been checked during cipher suite negotiation.
             if (namedGroup == null) {
-                // unlikely, have been checked during cipher suite negotiation.
                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Unsupported EC server cert for ECDH client key exchange");
+                    "Unknown named group in server cert for " +
+                        "ECDH client key exchange");
             }
 
             SSLKeyExchange ke = SSLKeyExchange.valueOf(
@@ -306,7 +307,7 @@
                         "Not supported key exchange type");
             }
 
-            // parse the handshake message
+            // parse either handshake message containing either EC/XEC.
             ECDHClientKeyExchangeMessage cke =
                     new ECDHClientKeyExchangeMessage(shc, message);
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -316,27 +317,17 @@
 
             // create the credentials
             try {
-                ECPoint point =
-                    ECUtil.decodePoint(cke.encodedPoint, params.getCurve());
-                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+                NamedGroup ng = namedGroup;  // "effectively final" the lambda
+                // AlgorithmConstraints are checked internally.
+                SSLCredentials sslCredentials = namedGroup.decodeCredentials(
+                        cke.encodedPoint, shc.algorithmConstraints,
+                        s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                        "ClientKeyExchange " + ng + ": " + s));
 
-                KeyFactory kf = KeyFactory.getInstance("EC");
-                ECPublicKey peerPublicKey =
-                        (ECPublicKey)kf.generatePublic(spec);
-
-                // check constraints of peer ECPublicKey
-                if (!shc.algorithmConstraints.permits(
-                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                        peerPublicKey)) {
-                    throw new SSLHandshakeException(
-                        "ECPublicKey does not comply to algorithm constraints");
-                }
-
-                shc.handshakeCredentials.add(new ECDHECredentials(
-                        peerPublicKey, namedGroup));
-            } catch (GeneralSecurityException | java.io.IOException e) {
-                throw (SSLHandshakeException)(new SSLHandshakeException(
-                        "Could not generate ECPublicKey").initCause(e));
+                shc.handshakeCredentials.add(sslCredentials);
+            } catch (GeneralSecurityException e) {
+                throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Cannot decode ECDH PublicKey: " + namedGroup);
             }
 
             // update the states
@@ -374,25 +365,37 @@
             // The producing happens in client side only.
             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 
-            ECDHECredentials ecdheCredentials = null;
+            SSLCredentials sslCredentials = null;
+            NamedGroup ng = null;
+            PublicKey publicKey = null;
+
+            // Find a good EC/XEC credential to use, determine the
+            // NamedGroup to use for creating Possessions/Credentials/Keys.
             for (SSLCredentials cd : chc.handshakeCredentials) {
-                if (cd instanceof ECDHECredentials) {
-                    ecdheCredentials = (ECDHECredentials)cd;
+                if (cd instanceof NamedGroupCredentials) {
+                    NamedGroupCredentials creds = (NamedGroupCredentials)cd;
+                    ng = creds.getNamedGroup();
+                    publicKey = creds.getPublicKey();
+                    sslCredentials = cd;
                     break;
                 }
             }
 
-            if (ecdheCredentials == null) {
+            if (sslCredentials == null) {
                 throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
                     "No ECDHE credentials negotiated for client key exchange");
             }
 
-            ECDHEPossession ecdhePossession = new ECDHEPossession(
-                    ecdheCredentials, chc.sslContext.getSecureRandom());
-            chc.handshakePossessions.add(ecdhePossession);
+            SSLPossession sslPossession = ng.createPossession(
+                    chc.sslContext.getSecureRandom());
+
+            chc.handshakePossessions.add(sslPossession);
+
+            // Write the EC/XEC message.
             ECDHClientKeyExchangeMessage cke =
                     new ECDHClientKeyExchangeMessage(
-                            chc, ecdhePossession.publicKey);
+                            chc, sslPossession.encode());
+
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                 SSLLogger.fine(
                     "Produced ECDHE ClientKeyExchange handshake message", cke);
@@ -450,23 +453,29 @@
             // The consuming happens in server side only.
             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 
-            ECDHEPossession ecdhePossession = null;
+            SSLPossession sslPossession = null;
+            NamedGroup namedGroup = null;
+
+           // Find a good EC/XEC credential to use, determine the
+           // NamedGroup to use for creating Possessions/Credentials/Keys.
             for (SSLPossession possession : shc.handshakePossessions) {
-                if (possession instanceof ECDHEPossession) {
-                    ecdhePossession = (ECDHEPossession)possession;
+                if (possession instanceof NamedGroupPossession) {
+                    NamedGroupPossession poss =
+                            (NamedGroupPossession)possession;
+                    namedGroup = poss.getNamedGroup();
+                    sslPossession = poss;
                     break;
                 }
             }
-            if (ecdhePossession == null) {
+
+            if (sslPossession == null) {
                 // unlikely
                 throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
                     "No expected ECDHE possessions for client key exchange");
             }
 
-            ECParameterSpec params = ecdhePossession.publicKey.getParams();
-            NamedGroup namedGroup = NamedGroup.valueOf(params);
             if (namedGroup == null) {
-                // unlikely, have been checked during cipher suite negotiation.
+                // unlikely, have been checked during cipher suite negotiation
                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
                     "Unsupported EC server cert for ECDHE client key exchange");
             }
@@ -480,7 +489,7 @@
                         "Not supported key exchange type");
             }
 
-            // parse the handshake message
+            // parse the EC/XEC handshake message
             ECDHClientKeyExchangeMessage cke =
                     new ECDHClientKeyExchangeMessage(shc, message);
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -490,27 +499,17 @@
 
             // create the credentials
             try {
-                ECPoint point =
-                    ECUtil.decodePoint(cke.encodedPoint, params.getCurve());
-                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+                NamedGroup ng = namedGroup; // "effectively final" the lambda
+                // AlgorithmConstraints are checked internally.
+                SSLCredentials sslCredentials = namedGroup.decodeCredentials(
+                        cke.encodedPoint, shc.algorithmConstraints,
+                        s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                        "ClientKeyExchange " + ng + ": " + s));
 
-                KeyFactory kf = KeyFactory.getInstance("EC");
-                ECPublicKey peerPublicKey =
-                        (ECPublicKey)kf.generatePublic(spec);
-
-                // check constraints of peer ECPublicKey
-                if (!shc.algorithmConstraints.permits(
-                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                        peerPublicKey)) {
-                    throw new SSLHandshakeException(
-                        "ECPublicKey does not comply to algorithm constraints");
-                }
-
-                shc.handshakeCredentials.add(new ECDHECredentials(
-                        peerPublicKey, namedGroup));
-            } catch (GeneralSecurityException | java.io.IOException e) {
-                throw (SSLHandshakeException)(new SSLHandshakeException(
-                        "Could not generate ECPublicKey").initCause(e));
+                shc.handshakeCredentials.add(sslCredentials);
+            } catch (GeneralSecurityException e) {
+                throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Cannot decode named group: " + namedGroup);
             }
 
             // update the states
--- a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -36,7 +36,6 @@
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.interfaces.ECPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
@@ -44,25 +43,30 @@
 import java.util.EnumSet;
 import javax.crypto.KeyAgreement;
 import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
 import javax.net.ssl.SSLHandshakeException;
-import sun.security.ssl.CipherSuite.HashAlg;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupType;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Credentials;
 import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.ssl.XDHKeyExchange.XDHECredentials;
+import sun.security.ssl.XDHKeyExchange.XDHEPossession;
 import sun.security.util.ECUtil;
 
 final class ECDHKeyExchange {
     static final SSLPossessionGenerator poGenerator =
             new ECDHEPossessionGenerator();
-    static final SSLKeyAgreementGenerator ecdheKAGenerator =
-            new ECDHEKAGenerator();
     static final SSLKeyAgreementGenerator ecdhKAGenerator =
             new ECDHKAGenerator();
 
-    static final class ECDHECredentials implements SSLCredentials {
+    // TLSv1.3
+    static final SSLKeyAgreementGenerator ecdheKAGenerator =
+            new ECDHEKAGenerator();
+
+    // TLSv1-1.2, the KA gets more difficult with EC/XEC keys
+    static final SSLKeyAgreementGenerator ecdheXdhKAGenerator =
+            new ECDHEXDHKAGenerator();
+
+    static final class ECDHECredentials implements NamedGroupCredentials {
         final ECPublicKey popPublicKey;
         final NamedGroup namedGroup;
 
@@ -71,6 +75,16 @@
             this.namedGroup = namedGroup;
         }
 
+        @Override
+        public PublicKey getPublicKey() {
+            return popPublicKey;
+        }
+
+        @Override
+        public NamedGroup getNamedGroup() {
+            return namedGroup;
+        }
+
         static ECDHECredentials valueOf(NamedGroup namedGroup,
             byte[] encodedPoint) throws IOException, GeneralSecurityException {
 
@@ -98,7 +112,7 @@
         }
     }
 
-    static final class ECDHEPossession implements SSLPossession {
+    static final class ECDHEPossession implements NamedGroupPossession {
         final PrivateKey privateKey;
         final ECPublicKey publicKey;
         final NamedGroup namedGroup;
@@ -199,6 +213,21 @@
                         "Could not generate ECPublicKey").initCause(e);
             }
         }
+
+        @Override
+        public PublicKey getPublicKey() {
+            return publicKey;
+        }
+
+        @Override
+        public NamedGroup getNamedGroup() {
+            return namedGroup;
+        }
+
+        @Override
+        public PrivateKey getPrivateKey() {
+            return privateKey;
+        }
     }
 
     private static final
@@ -210,24 +239,31 @@
 
         @Override
         public SSLPossession createPossession(HandshakeContext context) {
-            NamedGroup preferableNamedGroup = null;
+
+            NamedGroup preferableNamedGroup;
+
+            // Find most preferred EC or XEC groups
             if ((context.clientRequestedNamedGroups != null) &&
                     (!context.clientRequestedNamedGroups.isEmpty())) {
                 preferableNamedGroup = SupportedGroups.getPreferredGroup(
                         context.negotiatedProtocol,
                         context.algorithmConstraints,
-                        NamedGroupType.NAMED_GROUP_ECDHE,
+                        new NamedGroupType[] {
+                            NamedGroupType.NAMED_GROUP_ECDHE,
+                            NamedGroupType.NAMED_GROUP_XDH },
                         context.clientRequestedNamedGroups);
             } else {
                 preferableNamedGroup = SupportedGroups.getPreferredGroup(
                         context.negotiatedProtocol,
                         context.algorithmConstraints,
-                        NamedGroupType.NAMED_GROUP_ECDHE);
+                        new NamedGroupType[] {
+                            NamedGroupType.NAMED_GROUP_ECDHE,
+                            NamedGroupType.NAMED_GROUP_XDH });
             }
 
             if (preferableNamedGroup != null) {
-                return new ECDHEPossession(preferableNamedGroup,
-                            context.sslContext.getSecureRandom());
+                return preferableNamedGroup.createPossession(
+                    context.sslContext.getSecureRandom());
             }
 
             // no match found, cannot use this cipher suite.
@@ -298,7 +334,7 @@
                     "No sufficient ECDHE key agreement parameters negotiated");
             }
 
-            return new ECDHEKAKeyDerivation(shc,
+            return new KAKeyDerivation("ECDH", shc,
                 x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
         }
 
@@ -347,7 +383,7 @@
                     "No sufficient ECDH key agreement parameters negotiated");
             }
 
-            return new ECDHEKAKeyDerivation(chc,
+            return new KAKeyDerivation("ECDH", chc,
                 ecdhePossession.privateKey, x509Credentials.popPublicKey);
         }
     }
@@ -391,94 +427,73 @@
                     "No sufficient ECDHE key agreement parameters negotiated");
             }
 
-            return new ECDHEKAKeyDerivation(context,
+            return new KAKeyDerivation("ECDH", context,
                 ecdhePossession.privateKey, ecdheCredentials.popPublicKey);
         }
     }
 
+    /*
+     * A Generator for TLSv1-1.2 to create a ECDHE or a XDH KeyDerivation
+     * object depending on the negotiated group.
+     */
     private static final
-            class ECDHEKAKeyDerivation implements SSLKeyDerivation {
-        private final HandshakeContext context;
-        private final PrivateKey localPrivateKey;
-        private final PublicKey peerPublicKey;
-
-        ECDHEKAKeyDerivation(HandshakeContext context,
-                PrivateKey localPrivateKey,
-                PublicKey peerPublicKey) {
-            this.context = context;
-            this.localPrivateKey = localPrivateKey;
-            this.peerPublicKey = peerPublicKey;
+            class ECDHEXDHKAGenerator implements SSLKeyAgreementGenerator {
+        // Prevent instantiation of this class.
+        private ECDHEXDHKAGenerator() {
+            // blank
         }
 
         @Override
-        public SecretKey deriveKey(String algorithm,
-                AlgorithmParameterSpec params) throws IOException {
-            if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
-                return t12DeriveKey(algorithm, params);
-            } else {
-                return t13DeriveKey(algorithm, params);
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+
+            NamedGroupPossession namedGroupPossession = null;
+            NamedGroupCredentials namedGroupCredentials = null;
+            NamedGroup namedGroup = null;
+
+            // Find a possession/credential combo using the same named group
+            search:
+            for (SSLPossession poss : context.handshakePossessions) {
+                for (SSLCredentials cred : context.handshakeCredentials) {
+                    if (((poss instanceof ECDHEPossession) &&
+                            (cred instanceof ECDHECredentials)) ||
+                            (((poss instanceof XDHEPossession) &&
+                            (cred instanceof XDHECredentials)))) {
+                        NamedGroupPossession p = (NamedGroupPossession)poss;
+                        NamedGroupCredentials c = (NamedGroupCredentials)cred;
+                        if (p.getNamedGroup() != c.getNamedGroup()) {
+                            continue;
+                        } else {
+                            namedGroup = p.getNamedGroup();
+                        }
+                        namedGroupPossession = p;
+                        namedGroupCredentials = c;
+                        break search;
+                    }
+                }
             }
-        }
 
-        private SecretKey t12DeriveKey(String algorithm,
-                AlgorithmParameterSpec params) throws IOException {
-            try {
-                KeyAgreement ka = KeyAgreement.getInstance("ECDH");
-                ka.init(localPrivateKey);
-                ka.doPhase(peerPublicKey, true);
-                SecretKey preMasterSecret =
-                        ka.generateSecret("TlsPremasterSecret");
+            if (namedGroupPossession == null || namedGroupCredentials == null) {
+                throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No sufficient ECDHE/XDH key agreement " +
+                            "parameters negotiated");
+            }
 
-                SSLMasterKeyDerivation mskd =
-                        SSLMasterKeyDerivation.valueOf(
-                                context.negotiatedProtocol);
-                if (mskd == null) {
-                    // unlikely
-                    throw new SSLHandshakeException(
-                            "No expected master key derivation for protocol: " +
-                            context.negotiatedProtocol.name);
-                }
-                SSLKeyDerivation kd = mskd.createKeyDerivation(
-                        context, preMasterSecret);
-                return kd.deriveKey("MasterSecret", params);
-            } catch (GeneralSecurityException gse) {
-                throw (SSLHandshakeException) new SSLHandshakeException(
-                    "Could not generate secret").initCause(gse);
+            String alg;
+            switch (namedGroup.type) {
+                case NAMED_GROUP_ECDHE:
+                    alg = "ECDH";
+                    break;
+                case NAMED_GROUP_XDH:
+                    alg = "XDH";
+                    break;
+                default:
+                    throw new RuntimeException("Unexpected named group type");
             }
-        }
 
-        private SecretKey t13DeriveKey(String algorithm,
-                AlgorithmParameterSpec params) throws IOException {
-            try {
-                KeyAgreement ka = KeyAgreement.getInstance("ECDH");
-                ka.init(localPrivateKey);
-                ka.doPhase(peerPublicKey, true);
-                SecretKey sharedSecret =
-                        ka.generateSecret("TlsPremasterSecret");
-
-                HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
-                SSLKeyDerivation kd = context.handshakeKeyDerivation;
-                HKDF hkdf = new HKDF(hashAlg.name);
-                if (kd == null) {   // No PSK is in use.
-                    // If PSK is not in use Early Secret will still be
-                    // HKDF-Extract(0, 0).
-                    byte[] zeros = new byte[hashAlg.hashLength];
-                    SecretKeySpec ikm =
-                            new SecretKeySpec(zeros, "TlsPreSharedSecret");
-                    SecretKey earlySecret =
-                            hkdf.extract(zeros, ikm, "TlsEarlySecret");
-                    kd = new SSLSecretDerivation(context, earlySecret);
-                }
-
-                // derive salt secret
-                SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
-
-                // derive handshake secret
-                return hkdf.extract(saltSecret, sharedSecret, algorithm);
-            } catch (GeneralSecurityException gse) {
-                throw (SSLHandshakeException) new SSLHandshakeException(
-                    "Could not generate secret").initCause(gse);
-            }
+            return new KAKeyDerivation(alg, context,
+                    namedGroupPossession.getPrivateKey(),
+                    namedGroupCredentials.getPublicKey());
         }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -27,32 +27,21 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
-import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.Signature;
 import java.security.SignatureException;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
 import java.text.MessageFormat;
-import java.util.EnumSet;
 import java.util.Locale;
-import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
-import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Credentials;
 import sun.security.ssl.X509Authentication.X509Possession;
-import sun.security.util.ECUtil;
 import sun.security.util.HexDumpEncoder;
 
 /**
@@ -80,14 +69,14 @@
         // signature bytes, or null if anonymous
         private final byte[] paramsSignature;
 
-        // public key object encapsulated in this message
-        private final ECPublicKey publicKey;
-
         private final boolean useExplicitSigAlgorithm;
 
         // the signature algorithm used by this ServerKeyExchange message
         private final SignatureScheme signatureScheme;
 
+        // the parsed credential object
+        private SSLCredentials sslCredentials;
+
         ECDHServerKeyExchangeMessage(
                 HandshakeContext handshakeContext) throws IOException {
             super(handshakeContext);
@@ -96,38 +85,38 @@
             ServerHandshakeContext shc =
                     (ServerHandshakeContext)handshakeContext;
 
-            ECDHEPossession ecdhePossession = null;
+            // Find the Possessions needed
+            NamedGroupPossession namedGroupPossession = null;
             X509Possession x509Possession = null;
             for (SSLPossession possession : shc.handshakePossessions) {
-                if (possession instanceof ECDHEPossession) {
-                    ecdhePossession = (ECDHEPossession)possession;
+                if (possession instanceof NamedGroupPossession) {
+                    namedGroupPossession = (NamedGroupPossession)possession;
                     if (x509Possession != null) {
                         break;
                     }
                 } else if (possession instanceof X509Possession) {
                     x509Possession = (X509Possession)possession;
-                    if (ecdhePossession != null) {
+                    if (namedGroupPossession != null) {
                         break;
                     }
                 }
             }
 
-            if (ecdhePossession == null) {
+            if (namedGroupPossession == null) {
                 // unlikely
                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
                     "No ECDHE credentials negotiated for server key exchange");
             }
 
-            publicKey = ecdhePossession.publicKey;
-            ECParameterSpec params = publicKey.getParams();
-            ECPoint point = publicKey.getW();
-            publicPoint = ECUtil.encodePoint(point, params.getCurve());
+            // Find the NamedGroup used for the ephemeral keys.
+            namedGroup = namedGroupPossession.getNamedGroup();
+            publicPoint = namedGroup.encodePossessionPublicKey(
+                    namedGroupPossession);
 
-            this.namedGroup = NamedGroup.valueOf(params);
             if ((namedGroup == null) || (namedGroup.oid == null) ) {
                 // unlikely
                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Unnamed EC parameter spec: " + params);
+                    "Missing Named Group");
             }
 
             if (x509Possession == null) {
@@ -216,39 +205,23 @@
                     "Unsupported named group: " + namedGroup);
             }
 
-            if (namedGroup.oid == null) {
-                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Unknown named EC curve: " + namedGroup);
-            }
-
-            ECParameterSpec parameters =
-                    ECUtil.getECParameterSpec(null, namedGroup.oid);
-            if (parameters == null) {
-                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "No supported EC parameter: " + namedGroup);
-            }
-
             publicPoint = Record.getBytes8(m);
             if (publicPoint.length == 0) {
                 throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Insufficient ECPoint data: " + namedGroup);
+                    "Insufficient Point data: " + namedGroup);
             }
 
-            ECPublicKey ecPublicKey = null;
             try {
-                ECPoint point =
-                        ECUtil.decodePoint(publicPoint, parameters.getCurve());
-                KeyFactory factory = KeyFactory.getInstance("EC");
-                ecPublicKey = (ECPublicKey)factory.generatePublic(
-                    new ECPublicKeySpec(point, parameters));
-            } catch (NoSuchAlgorithmException |
-                    InvalidKeySpecException | IOException ex) {
-                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Invalid ECPoint: " + namedGroup, ex);
+                sslCredentials = namedGroup.decodeCredentials(
+                    publicPoint, handshakeContext.algorithmConstraints,
+                     s -> chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                     "ServerKeyExchange " + namedGroup + ": " + (s)));
+            } catch (GeneralSecurityException ex) {
+                throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Cannot decode named group: " +
+                        NamedGroup.nameOf(namedGroupId));
             }
 
-            publicKey = ecPublicKey;
-
             X509Credentials x509Credentials = null;
             for (SSLCredentials cd : chc.handshakeCredentials) {
                 if (cd instanceof X509Credentials) {
@@ -529,6 +502,7 @@
             // The consuming happens in client side only.
             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 
+            // AlgorithmConstraints are checked during decoding
             ECDHServerKeyExchangeMessage skem =
                     new ECDHServerKeyExchangeMessage(chc, message);
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -537,22 +511,9 @@
             }
 
             //
-            // validate
-            //
-            // check constraints of EC PublicKey
-            if (!chc.algorithmConstraints.permits(
-                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                    skem.publicKey)) {
-                throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
-                        "ECDH ServerKeyExchange does not comply " +
-                        "to algorithm constraints");
-            }
-
-            //
             // update
             //
-            chc.handshakeCredentials.add(
-                    new ECDHECredentials(skem.publicKey, skem.namedGroup));
+            chc.handshakeCredentials.add(skem.sslCredentials);
 
             //
             // produce
--- a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -34,7 +34,7 @@
 import sun.security.ssl.SSLExtension.ExtensionConsumer;
 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupType;
 
 /**
  * Pack of the "ec_point_formats" extensions [RFC 4492].
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Wed Jun 12 18:58:00 2019 -0700
@@ -46,9 +46,8 @@
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLHandshakeException;
 import javax.security.auth.x500.X500Principal;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
-import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
+import sun.security.ssl.NamedGroup.NamedGroupType;
+import static sun.security.ssl.NamedGroup.NamedGroupType.*;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 
 abstract class HandshakeContext implements ConnectionContext {
@@ -519,31 +518,38 @@
                 return true;
             }
 
-            boolean available;
-            NamedGroupType groupType = suite.keyExchange.groupType;
-            if (groupType != NAMED_GROUP_NONE) {
-                Boolean checkedStatus = cachedStatus.get(groupType);
-                if (checkedStatus == null) {
-                    available = SupportedGroups.isActivatable(
-                            algorithmConstraints, groupType);
-                    cachedStatus.put(groupType, available);
+            // Is at least one of the group types available?
+            boolean groupAvailable, retval = false;
+            NamedGroupType[] groupTypes = suite.keyExchange.groupTypes;
+            for (NamedGroupType groupType : groupTypes) {
+                if (groupType != NAMED_GROUP_NONE) {
+                    Boolean checkedStatus = cachedStatus.get(groupType);
+                    if (checkedStatus == null) {
+                        groupAvailable = SupportedGroups.isActivatable(
+                                algorithmConstraints, groupType);
+                        cachedStatus.put(groupType, groupAvailable);
 
-                    if (!available &&
-                            SSLLogger.isOn && SSLLogger.isOn("verbose")) {
-                        SSLLogger.fine("No activated named group");
+                        if (!groupAvailable &&
+                                SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                            SSLLogger.fine(
+                                    "No activated named group in " + groupType);
+                        }
+                    } else {
+                        groupAvailable = checkedStatus;
                     }
+
+                    retval |= groupAvailable;
                 } else {
-                    available = checkedStatus;
+                    retval |= true;
                 }
+            }
 
-                if (!available && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
-                    SSLLogger.fine(
-                        "No active named group, ignore " + suite);
-                }
-                return available;
-            } else {
-                return true;
+            if (!retval && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                SSLLogger.fine("No active named group(s), ignore " + suite);
             }
+
+            return retval;
+
         } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
             SSLLogger.fine("Ignore disabled cipher suite: " + suite);
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java	Wed Jun 12 18:58:00 2019 -0700
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2019, 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 javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLHandshakeException;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * A common class for creating various KeyDerivation types.
+ */
+public class KAKeyDerivation implements SSLKeyDerivation {
+
+    private final String algorithmName;
+    private final HandshakeContext context;
+    private final PrivateKey localPrivateKey;
+    private final PublicKey peerPublicKey;
+
+    KAKeyDerivation(String algorithmName,
+            HandshakeContext context,
+            PrivateKey localPrivateKey,
+            PublicKey peerPublicKey) {
+        this.algorithmName = algorithmName;
+        this.context = context;
+        this.localPrivateKey = localPrivateKey;
+        this.peerPublicKey = peerPublicKey;
+    }
+
+    @Override
+    public SecretKey deriveKey(String algorithm,
+            AlgorithmParameterSpec params) throws IOException {
+        if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
+            return t12DeriveKey(algorithm, params);
+        } else {
+            return t13DeriveKey(algorithm, params);
+        }
+    }
+
+    /**
+     * Handle the TLSv1-1.2 objects, which don't use the HKDF algorithms.
+     */
+    private SecretKey t12DeriveKey(String algorithm,
+            AlgorithmParameterSpec params) throws IOException {
+        try {
+            KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
+            ka.init(localPrivateKey);
+            ka.doPhase(peerPublicKey, true);
+            SecretKey preMasterSecret
+                    = ka.generateSecret("TlsPremasterSecret");
+            SSLMasterKeyDerivation mskd
+                    = SSLMasterKeyDerivation.valueOf(
+                            context.negotiatedProtocol);
+            if (mskd == null) {
+                // unlikely
+                throw new SSLHandshakeException(
+                        "No expected master key derivation for protocol: "
+                        + context.negotiatedProtocol.name);
+            }
+            SSLKeyDerivation kd = mskd.createKeyDerivation(
+                    context, preMasterSecret);
+            return kd.deriveKey("MasterSecret", params);
+        } catch (GeneralSecurityException gse) {
+            throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not generate secret").initCause(gse);
+        }
+    }
+
+    /**
+     * Handle the TLSv1.3 objects, which use the HKDF algorithms.
+     */
+    private SecretKey t13DeriveKey(String algorithm,
+            AlgorithmParameterSpec params) throws IOException {
+        try {
+            KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
+            ka.init(localPrivateKey);
+            ka.doPhase(peerPublicKey, true);
+            SecretKey sharedSecret
+                    = ka.generateSecret("TlsPremasterSecret");
+
+            CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
+            SSLKeyDerivation kd = context.handshakeKeyDerivation;
+            HKDF hkdf = new HKDF(hashAlg.name);
+            if (kd == null) {   // No PSK is in use.
+                // If PSK is not in use Early Secret will still be
+                // HKDF-Extract(0, 0).
+                byte[] zeros = new byte[hashAlg.hashLength];
+                SecretKeySpec ikm
+                        = new SecretKeySpec(zeros, "TlsPreSharedSecret");
+                SecretKey earlySecret
+                        = hkdf.extract(zeros, ikm, "TlsEarlySecret");
+                kd = new SSLSecretDerivation(context, earlySecret);
+            }
+
+            // derive salt secret
+            SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
+
+            // derive handshake secret
+            return hkdf.extract(saltSecret, sharedSecret, algorithm);
+        } catch (GeneralSecurityException gse) {
+            throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not generate secret").initCause(gse);
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -27,27 +27,19 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.security.CryptoPrimitive;
 import java.security.GeneralSecurityException;
 import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import javax.net.ssl.SSLProtocolException;
-import sun.security.ssl.DHKeyExchange.DHECredentials;
-import sun.security.ssl.DHKeyExchange.DHEPossession;
-import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
-import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
 import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
 import sun.security.ssl.SSLExtension.ExtensionConsumer;
 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.util.HexDumpEncoder;
 
@@ -264,8 +256,7 @@
                 for (SSLPossession pos : poses) {
                     // update the context
                     chc.handshakePossessions.add(pos);
-                    if (!(pos instanceof ECDHEPossession) &&
-                            !(pos instanceof DHEPossession)) {
+                    if (!(pos instanceof NamedGroupPossession)) {
                         // May need more possesion types in the future.
                         continue;
                     }
@@ -353,46 +344,18 @@
                     continue;
                 }
 
-                if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
-                    try {
-                        ECDHECredentials ecdhec =
-                            ECDHECredentials.valueOf(ng, entry.keyExchange);
-                        if (ecdhec != null) {
-                            if (!shc.algorithmConstraints.permits(
-                                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                    ecdhec.popPublicKey)) {
-                                SSLLogger.warning(
-                                        "ECDHE key share entry does not " +
-                                        "comply to algorithm constraints");
-                            } else {
-                                credentials.add(ecdhec);
-                            }
-                        }
-                    } catch (IOException | GeneralSecurityException ex) {
-                        SSLLogger.warning(
-                                "Cannot decode named group: " +
-                                NamedGroup.nameOf(entry.namedGroupId));
+                try {
+                    SSLCredentials kaCred =
+                        ng.decodeCredentials(entry.keyExchange,
+                        shc.algorithmConstraints,
+                        s -> SSLLogger.warning(s));
+                    if (kaCred != null) {
+                        credentials.add(kaCred);
                     }
-                } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
-                    try {
-                        DHECredentials dhec =
-                                DHECredentials.valueOf(ng, entry.keyExchange);
-                        if (dhec != null) {
-                            if (!shc.algorithmConstraints.permits(
-                                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                    dhec.popPublicKey)) {
-                                SSLLogger.warning(
-                                        "DHE key share entry does not " +
-                                        "comply to algorithm constraints");
-                            } else {
-                                credentials.add(dhec);
-                            }
-                        }
-                    } catch (IOException | GeneralSecurityException ex) {
-                        SSLLogger.warning(
-                                "Cannot decode named group: " +
-                                NamedGroup.nameOf(entry.namedGroupId));
-                    }
+                } catch (GeneralSecurityException ex) {
+                    SSLLogger.warning(
+                        "Cannot decode named group: " +
+                        NamedGroup.nameOf(entry.namedGroupId));
                 }
             }
 
@@ -526,10 +489,9 @@
             KeyShareEntry keyShare = null;
             for (SSLCredentials cd : shc.handshakeCredentials) {
                 NamedGroup ng = null;
-                if (cd instanceof ECDHECredentials) {
-                    ng = ((ECDHECredentials)cd).namedGroup;
-                } else if (cd instanceof DHECredentials) {
-                    ng = ((DHECredentials)cd).namedGroup;
+                if (cd instanceof NamedGroupCredentials) {
+                    NamedGroupCredentials creds = (NamedGroupCredentials)cd;
+                    ng = creds.getNamedGroup();
                 }
 
                 if (ng == null) {
@@ -547,8 +509,7 @@
 
                 SSLPossession[] poses = ke.createPossessions(shc);
                 for (SSLPossession pos : poses) {
-                    if (!(pos instanceof ECDHEPossession) &&
-                            !(pos instanceof DHEPossession)) {
+                    if (!(pos instanceof NamedGroupPossession)) {
                         // May need more possesion types in the future.
                         continue;
                     }
@@ -567,7 +528,7 @@
                                 me.getKey(), me.getValue());
                     }
 
-                    // We have got one! Don't forgor to break.
+                    // We have got one! Don't forget to break.
                     break;
                 }
             }
@@ -643,49 +604,16 @@
             }
 
             SSLCredentials credentials = null;
-            if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
-                try {
-                    ECDHECredentials ecdhec =
-                            ECDHECredentials.valueOf(ng, keyShare.keyExchange);
-                    if (ecdhec != null) {
-                        if (!chc.algorithmConstraints.permits(
-                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                ecdhec.popPublicKey)) {
-                            throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
-                                    "ECDHE key share entry does not " +
-                                    "comply to algorithm constraints");
-                        } else {
-                            credentials = ecdhec;
-                        }
-                    }
-                } catch (IOException | GeneralSecurityException ex) {
-                    throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
-                            "Cannot decode named group: " +
-                            NamedGroup.nameOf(keyShare.namedGroupId));
+            try {
+                SSLCredentials kaCred = ng.decodeCredentials(
+                    keyShare.keyExchange, chc.algorithmConstraints,
+                    s -> chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, s));
+                if (kaCred != null) {
+                    credentials = kaCred;
                 }
-            } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
-                try {
-                    DHECredentials dhec =
-                            DHECredentials.valueOf(ng, keyShare.keyExchange);
-                    if (dhec != null) {
-                        if (!chc.algorithmConstraints.permits(
-                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                dhec.popPublicKey)) {
-                            throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
-                                    "DHE key share entry does not " +
-                                    "comply to algorithm constraints");
-                        } else {
-                            credentials = dhec;
-                        }
-                    }
-                } catch (IOException | GeneralSecurityException ex) {
-                    throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
-                            "Cannot decode named group: " +
-                            NamedGroup.nameOf(keyShare.namedGroupId));
-                }
-            } else {
+            } catch (GeneralSecurityException ex) {
                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
-                        "Unsupported named group: " +
+                        "Cannot decode named group: " +
                         NamedGroup.nameOf(keyShare.namedGroupId));
             }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java	Wed Jun 12 18:58:00 2019 -0700
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2019, 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 javax.crypto.spec.DHParameterSpec;
+import javax.net.ssl.SSLException;
+import java.io.IOException;
+import java.security.*;
+import java.security.spec.*;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.crypto.*;
+import sun.security.ssl.DHKeyExchange.DHEPossession;
+import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+
+import sun.security.util.ECUtil;
+
+/**
+ * An enum containing all known named groups for use in TLS.
+ *
+ * The enum also contains the required properties of each group and the
+ * required functions (e.g. encoding/decoding).
+ */
+enum NamedGroup {
+    // Elliptic Curves (RFC 4492)
+    //
+    // See sun.security.util.CurveDB for the OIDs
+    // NIST K-163
+
+    SECT163_K1(0x0001, "sect163k1", "1.3.132.0.1",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT163_R1(0x0002, "sect163r1", "1.3.132.0.2",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST B-163
+    SECT163_R2(0x0003, "sect163r2", "1.3.132.0.15",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT193_R1(0x0004, "sect193r1", "1.3.132.0.24",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT193_R2(0x0005, "sect193r2", "1.3.132.0.25",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST K-233
+    SECT233_K1(0x0006, "sect233k1", "1.3.132.0.26",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST B-233
+    SECT233_R1(0x0007, "sect233r1", "1.3.132.0.27",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT239_K1(0x0008, "sect239k1", "1.3.132.0.3",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST K-283
+    SECT283_K1(0x0009, "sect283k1", "1.3.132.0.16",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST B-283
+    SECT283_R1(0x000A, "sect283r1", "1.3.132.0.17",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST K-409
+    SECT409_K1(0x000B, "sect409k1", "1.3.132.0.36",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST B-409
+    SECT409_R1(0x000C, "sect409r1", "1.3.132.0.37",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST K-571
+    SECT571_K1(0x000D, "sect571k1", "1.3.132.0.38",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST B-571
+    SECT571_R1(0x000E, "sect571r1", "1.3.132.0.39",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECP160_K1(0x000F, "secp160k1", "1.3.132.0.9",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECP160_R1(0x0010, "secp160r1", "1.3.132.0.8",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECP160_R2(0x0011, "secp160r2", "1.3.132.0.30",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECP192_K1(0x0012, "secp192k1", "1.3.132.0.31",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST P-192
+    SECP192_R1(0x0013, "secp192r1", "1.2.840.10045.3.1.1",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECP224_K1(0x0014, "secp224k1", "1.3.132.0.32",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST P-224
+    SECP224_R1(0x0015, "secp224r1", "1.3.132.0.33",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    SECP256_K1(0x0016, "secp256k1", "1.3.132.0.10",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12),
+
+    // NIST P-256
+    SECP256_R1(0x0017, "secp256r1", "1.2.840.10045.3.1.7",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+
+    // NIST P-384
+    SECP384_R1(0x0018, "secp384r1", "1.3.132.0.34",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+
+    // NIST P-521
+    SECP521_R1(0x0019, "secp521r1", "1.3.132.0.35",
+            NamedGroupType.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+
+    // x25519 and x448 (RFC 8422/8446)
+    X25519(0x001D, "x25519", "1.3.101.110",
+            NamedGroupType.NAMED_GROUP_XDH,
+            ProtocolVersion.PROTOCOLS_TO_13),
+    X448(0x001E, "x448", "1.3.101.111",
+            NamedGroupType.NAMED_GROUP_XDH,
+            ProtocolVersion.PROTOCOLS_TO_13),
+
+    // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
+    FFDHE_2048(0x0100, "ffdhe2048", null,
+            NamedGroupType.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+    FFDHE_3072(0x0101, "ffdhe3072", null,
+            NamedGroupType.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+    FFDHE_4096(0x0102, "ffdhe4096", null,
+            NamedGroupType.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+    FFDHE_6144(0x0103, "ffdhe6144", null,
+            NamedGroupType.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+    FFDHE_8192(0x0104, "ffdhe8192", null,
+            NamedGroupType.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13),
+
+    // Elliptic Curves (RFC 4492)
+    //
+    // arbitrary prime and characteristic-2 curves
+    ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves", null,
+            NamedGroupType.NAMED_GROUP_ARBITRARY,
+            ProtocolVersion.PROTOCOLS_TO_12),
+    ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves", null,
+            NamedGroupType.NAMED_GROUP_ARBITRARY,
+            ProtocolVersion.PROTOCOLS_TO_12);
+
+    final int id;               // hash + signature
+    final NamedGroupType type;  // group type
+    final String name;          // literal name
+    final String oid;           // object identifier of the named group
+    final String algorithm;     // signature algorithm
+    final ProtocolVersion[] supportedProtocols;
+    private final NamedGroupFunctions functions;    // may be null
+
+    // Constructor used for all NamedGroup types
+    private NamedGroup(int id, String name, String oid,
+            NamedGroupType namedGroupType,
+            ProtocolVersion[] supportedProtocols) {
+        this.id = id;
+        this.name = name;
+        this.oid = oid;
+        this.type = namedGroupType;
+        this.supportedProtocols = supportedProtocols;
+
+        if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+            this.functions = ECDHFunctions.getInstance();
+            this.algorithm = "EC";
+        } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+            this.functions = FFDHFunctions.getInstance();
+            this.algorithm = "DiffieHellman";
+        } else if (this.type == NamedGroupType.NAMED_GROUP_XDH) {
+            this.functions = XDHFunctions.getInstance();
+            this.algorithm = "XDH";
+        } else if (this.type == NamedGroupType.NAMED_GROUP_ARBITRARY) {
+            this.functions = null;
+            this.algorithm = "EC";
+        } else {
+            throw new RuntimeException("Unexpected Named Group Type");
+        }
+    }
+
+    private Optional<NamedGroupFunctions> getFunctions() {
+        return Optional.ofNullable(functions);
+    }
+
+    // The next set of methods search & retrieve NamedGroups.
+
+    static NamedGroup valueOf(int id) {
+        for (NamedGroup group : NamedGroup.values()) {
+            if (group.id == id) {
+                return group;
+            }
+        }
+
+        return null;
+    }
+
+    static NamedGroup valueOf(ECParameterSpec params) {
+        String oid = ECUtil.getCurveName(null, params);
+        if ((oid != null) && (!oid.isEmpty())) {
+            for (NamedGroup group : NamedGroup.values()) {
+                if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE)
+                        && oid.equals(group.oid)) {
+                    return group;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    static NamedGroup valueOf(DHParameterSpec params) {
+        for (NamedGroup ng : NamedGroup.values()) {
+            if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+                continue;
+            }
+
+            DHParameterSpec ngParams = null;
+            // functions is non-null for FFDHE type
+            AlgorithmParameters aps = ng.functions.getParameters(ng);
+            try {
+                ngParams = aps.getParameterSpec(DHParameterSpec.class);
+            } catch (InvalidParameterSpecException ipse) {
+                // should be unlikely
+            }
+
+            if (ngParams == null) {
+                continue;
+            }
+
+            if (ngParams.getP().equals(params.getP())
+                    && ngParams.getG().equals(params.getG())) {
+                return ng;
+            }
+        }
+
+        return null;
+    }
+
+    static NamedGroup nameOf(String name) {
+        for (NamedGroup group : NamedGroup.values()) {
+            if (group.name.equals(name)) {
+                return group;
+            }
+        }
+
+        return null;
+    }
+
+    static String nameOf(int id) {
+        for (NamedGroup group : NamedGroup.values()) {
+            if (group.id == id) {
+                return group.name;
+            }
+        }
+
+        return "UNDEFINED-NAMED-GROUP(" + id + ")";
+    }
+
+    // Are the NamedGroups available for the protocol desired?
+
+    boolean isAvailable(List<ProtocolVersion> protocolVersions) {
+        for (ProtocolVersion pv : supportedProtocols) {
+            if (protocolVersions.contains(pv)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean isAvailable(ProtocolVersion protocolVersion) {
+        for (ProtocolVersion pv : supportedProtocols) {
+            if (protocolVersion == pv) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Are the NamedGroups available for the ciphersuites desired?
+
+    boolean isSupported(List<CipherSuite> cipherSuites) {
+        for (CipherSuite cs : cipherSuites) {
+            boolean isMatch = isAvailable(cs.supportedProtocols);
+            if (isMatch && ((cs.keyExchange == null)
+                    || (NamedGroupType.arrayContains(
+                        cs.keyExchange.groupTypes, type)))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // lazy loading of parameters
+    AlgorithmParameters getParameters() {
+        Optional<NamedGroupFunctions> ngf = getFunctions();
+        if (ngf.isEmpty()) {
+            return null;
+        }
+        return ngf.get().getParameters(this);
+    }
+
+    // The next set of methods use the NamedGroupFunctions table
+    // to do various operations in a consistent way.
+
+    AlgorithmParameterSpec getParameterSpec() {
+        Optional<NamedGroupFunctions> ngf = getFunctions();
+        if (ngf.isEmpty()) {
+            return null;
+        }
+        return ngf.get().getParameterSpec(this);
+    }
+
+    byte[] encodePossessionPublicKey(
+            NamedGroupPossession namedGroupPossession) {
+
+        Optional<NamedGroupFunctions> ngf = getFunctions();
+        if (ngf.isEmpty()) {
+            return null;
+        }
+        return ngf.get().encodePossessionPublicKey(namedGroupPossession);
+    }
+
+    SSLCredentials decodeCredentials(byte[] encoded,
+            AlgorithmConstraints constraints,
+            ExceptionSupplier onConstraintFail)
+            throws IOException, GeneralSecurityException {
+
+        Optional<NamedGroupFunctions> ngf = getFunctions();
+        if (ngf.isEmpty()) {
+            return null;
+        }
+        return ngf.get().decodeCredentials(this, encoded, constraints,
+                onConstraintFail);
+    }
+
+    SSLPossession createPossession(SecureRandom random) {
+
+        Optional<NamedGroupFunctions> ngf = getFunctions();
+        if (ngf.isEmpty()) {
+            return null;
+        }
+        return ngf.get().createPossession(this, random);
+    }
+
+    SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
+            throws IOException {
+
+        Optional<NamedGroupFunctions> ngf = getFunctions();
+        if (ngf.isEmpty()) {
+            return null;
+        }
+        return ngf.get().createKeyDerivation(hc);
+
+    }
+
+    boolean isAvailableGroup() {
+        Optional<NamedGroupFunctions> ngfOpt = getFunctions();
+        if (ngfOpt.isEmpty()) {
+            return false;
+        }
+        NamedGroupFunctions ngf = ngfOpt.get();
+        return ngf.isAvailable(this);
+    }
+
+    enum NamedGroupType {
+        NAMED_GROUP_ECDHE,      // Elliptic Curve Groups (ECDHE)
+        NAMED_GROUP_FFDHE,      // Finite Field Groups (DHE)
+        NAMED_GROUP_XDH,        // Finite Field Groups (XDH)
+        NAMED_GROUP_ARBITRARY,  // arbitrary prime and curves (ECDHE)
+        NAMED_GROUP_NONE;       // Not predefined named group
+
+        boolean isSupported(List<CipherSuite> cipherSuites) {
+            for (CipherSuite cs : cipherSuites) {
+                if (cs.keyExchange == null ||
+                        arrayContains(cs.keyExchange.groupTypes, this)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        static boolean arrayContains(NamedGroupType[] namedGroupTypes,
+                NamedGroupType namedGroupType) {
+            for (NamedGroupType ng : namedGroupTypes) {
+                if (ng == namedGroupType) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    interface ExceptionSupplier {
+        void apply(String s) throws SSLException;
+    }
+
+    /*
+     * A list of functions to do NamedGroup operations in a
+     * algorithm-independent and consistent way.
+     */
+    private static abstract class NamedGroupFunctions {
+
+        // cache to speed up the parameters construction
+        protected static final Map<NamedGroup, AlgorithmParameters>
+                namedGroupParams = new ConcurrentHashMap<>();
+
+        protected void checkConstraints(PublicKey publicKey,
+                AlgorithmConstraints constraints,
+                ExceptionSupplier onConstraintFail)
+                throws SSLException {
+
+            if (!constraints.permits(
+                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                    publicKey)) {
+
+                onConstraintFail.apply("key share entry does not "
+                        + "comply with algorithm constraints");
+            }
+        }
+
+        public AlgorithmParameters getParameters(NamedGroup ng) {
+
+            AlgorithmParameters result = namedGroupParams.get(ng);
+            if (result == null) {
+                Optional<AlgorithmParameters> paramsOpt = getParametersImpl(ng);
+                if (paramsOpt.isPresent()) {
+                    result = paramsOpt.get();
+                    namedGroupParams.put(ng, result);
+                }
+            }
+
+            return result;
+        }
+
+        public abstract byte[] encodePossessionPublicKey(
+                NamedGroupPossession namedGroupPossession);
+
+        public abstract SSLCredentials decodeCredentials(
+                NamedGroup ng, byte[] encoded,
+                AlgorithmConstraints constraints,
+                ExceptionSupplier onConstraintFail)
+                throws IOException, GeneralSecurityException;
+
+        public abstract SSLPossession createPossession(NamedGroup ng,
+                SecureRandom random);
+
+        public abstract SSLKeyDerivation createKeyDerivation(
+                HandshakeContext hc) throws IOException;
+
+        protected abstract Optional<AlgorithmParameters> getParametersImpl(
+                NamedGroup ng);
+
+        public abstract AlgorithmParameterSpec getParameterSpec(NamedGroup ng);
+
+        public abstract boolean isAvailable(NamedGroup ng);
+    }
+
+    private static class FFDHFunctions extends NamedGroupFunctions {
+
+        // lazy initialization
+        private static class FunctionsHolder {
+            private static final FFDHFunctions instance = new FFDHFunctions();
+        }
+
+        private static FFDHFunctions getInstance() {
+            return FunctionsHolder.instance;
+        }
+
+        @Override
+        public byte[] encodePossessionPublicKey(
+                NamedGroupPossession namedGroupPossession) {
+            return ((DHEPossession)namedGroupPossession).encode();
+        }
+
+        @Override
+        public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
+                AlgorithmConstraints constraints,
+                ExceptionSupplier onConstraintFail)
+                throws IOException, GeneralSecurityException {
+
+            DHKeyExchange.DHECredentials result
+                    = DHKeyExchange.DHECredentials.valueOf(ng, encoded);
+
+            checkConstraints(result.getPublicKey(), constraints,
+                    onConstraintFail);
+
+            return result;
+        }
+
+        @Override
+        public SSLPossession createPossession(
+                NamedGroup ng, SecureRandom random) {
+            return new DHKeyExchange.DHEPossession(ng, random);
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
+                throws IOException {
+
+            return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
+        }
+
+        @Override
+        public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) {
+            return getDHParameterSpec(ng);
+        }
+
+        DHParameterSpec getDHParameterSpec(NamedGroup ng) {
+
+            AlgorithmParameters params = getParameters(ng);
+            try {
+                return params.getParameterSpec(DHParameterSpec.class);
+            } catch (InvalidParameterSpecException ipse) {
+                // should be unlikely
+                return getPredefinedDHParameterSpec(ng);
+            }
+        }
+
+        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;
+        }
+
+        @Override
+        public boolean isAvailable(NamedGroup ng) {
+
+            AlgorithmParameters params = getParameters(ng);
+            return params != null;
+        }
+
+        @Override
+        protected Optional<AlgorithmParameters> getParametersImpl(
+                NamedGroup ng) {
+            try {
+                AlgorithmParameters params
+                        = AlgorithmParameters.getInstance("DiffieHellman");
+                AlgorithmParameterSpec spec
+                        = getFFDHEDHParameterSpec(ng);
+                params.init(spec);
+                return Optional.of(params);
+            } catch (InvalidParameterSpecException
+                    | NoSuchAlgorithmException ex) {
+                return Optional.empty();
+            }
+        }
+
+    }
+
+    private static class ECDHFunctions extends NamedGroupFunctions {
+
+        // lazy initialization
+        private static class FunctionsHolder {
+            private static final ECDHFunctions instance = new ECDHFunctions();
+        }
+
+        private static ECDHFunctions getInstance() {
+            return FunctionsHolder.instance;
+        }
+
+        @Override
+        public byte[] encodePossessionPublicKey(
+                NamedGroupPossession namedGroupPossession) {
+            return ((ECDHEPossession)namedGroupPossession).encode();
+        }
+
+        @Override
+        public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
+                AlgorithmConstraints constraints,
+                ExceptionSupplier onConstraintFail)
+                throws IOException, GeneralSecurityException {
+
+            ECDHKeyExchange.ECDHECredentials result
+                    = ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded);
+
+            checkConstraints(result.getPublicKey(), constraints,
+                    onConstraintFail);
+
+            return result;
+        }
+
+        @Override
+        public SSLPossession createPossession(
+                NamedGroup ng, SecureRandom random) {
+            return new ECDHKeyExchange.ECDHEPossession(ng, random);
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
+                throws IOException {
+
+            return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
+        }
+
+        @Override
+        public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) {
+            return SupportedGroupsExtension.SupportedGroups
+                    .getECGenParamSpec(ng);
+        }
+
+        @Override
+        public boolean isAvailable(NamedGroup ng) {
+
+            AlgorithmParameters params = getParameters(ng);
+            return params != null;
+        }
+
+        @Override
+        protected Optional<AlgorithmParameters> getParametersImpl(
+                NamedGroup ng) {
+            try {
+                AlgorithmParameters params
+                        = AlgorithmParameters.getInstance("EC");
+                AlgorithmParameterSpec spec
+                        = new ECGenParameterSpec(ng.oid);
+                params.init(spec);
+                return Optional.of(params);
+            } catch (InvalidParameterSpecException
+                    | NoSuchAlgorithmException ex) {
+                return Optional.empty();
+            }
+        }
+    }
+
+    private static class XDHFunctions extends NamedGroupFunctions {
+
+        // lazy initialization
+        private static class FunctionsHolder {
+            private static final XDHFunctions instance = new XDHFunctions();
+        }
+
+        private static XDHFunctions getInstance() {
+            return FunctionsHolder.instance;
+        }
+
+        @Override
+        public byte[] encodePossessionPublicKey(NamedGroupPossession poss) {
+            return ((XDHKeyExchange.XDHEPossession)poss).encode();
+        }
+
+        @Override
+        public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
+                AlgorithmConstraints constraints,
+                ExceptionSupplier onConstraintFail)
+                throws IOException, GeneralSecurityException {
+
+            XDHKeyExchange.XDHECredentials result
+                    = XDHKeyExchange.XDHECredentials.valueOf(ng, encoded);
+
+            checkConstraints(result.getPublicKey(), constraints,
+                    onConstraintFail);
+
+            return result;
+        }
+
+        @Override
+        public SSLPossession createPossession(
+                NamedGroup ng, SecureRandom random) {
+            return new XDHKeyExchange.XDHEPossession(ng, random);
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
+                throws IOException {
+            return XDHKeyExchange.xdheKAGenerator.createKeyDerivation(hc);
+        }
+
+        @Override
+        public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) {
+            return new NamedParameterSpec(ng.name);
+        }
+
+        @Override
+        public boolean isAvailable(NamedGroup ng) {
+
+            try {
+                KeyAgreement.getInstance(ng.algorithm);
+                return true;
+            } catch (NoSuchAlgorithmException ex) {
+                return false;
+            }
+        }
+
+        @Override
+        protected Optional<AlgorithmParameters> getParametersImpl(
+                NamedGroup ng) {
+            return Optional.empty();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/NamedGroupCredentials.java	Wed Jun 12 18:58:00 2019 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019, 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.PublicKey;
+
+interface NamedGroupCredentials extends SSLCredentials {
+
+    PublicKey getPublicKey();
+
+    NamedGroup getNamedGroup();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/NamedGroupPossession.java	Wed Jun 12 18:58:00 2019 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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.PrivateKey;
+import java.security.PublicKey;
+
+interface NamedGroupPossession extends SSLPossession {
+
+    NamedGroup getNamedGroup();
+
+    PublicKey getPublicKey();
+
+    PrivateKey getPrivateKey();
+}
--- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -484,6 +484,25 @@
     final SSLHandshake handshakeType;
     final String name;
     final ProtocolVersion[] supportedProtocols;
+
+    /*
+     * networkProducer: produces outbound handshake data.
+     *
+     * onLoadConsumer:  parses inbound data.  It may not be appropriate
+     *                  to act until all of the message inputs have
+     *                  been parsed.  (e.g. parsing keyShares and choosing
+     *                  a local value without having seen the SupportedGroups
+     *                  extension.)
+     *
+     * onLoadAbsence:   if a missing message needs special handling
+     *                  during the load phase.
+     *
+     * onTradeConsumer: act on the parsed message once all inbound data has
+     *                  been traded and parsed.
+     *
+     * onTradeAbsence:  if a missing message needs special handling
+     *                  during the trade phase.
+     */
     final HandshakeProducer networkProducer;
     final ExtensionConsumer onLoadConsumer;
     final HandshakeAbsence  onLoadAbsence;
--- a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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,10 +30,6 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-import sun.security.ssl.DHKeyExchange.DHEPossession;
-import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Possession;
 
@@ -243,8 +239,7 @@
     static SSLKeyExchange valueOf(NamedGroup namedGroup) {
         SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup);
         if (ka != null) {
-            return new SSLKeyExchange(
-                null, T13KeyAgreement.valueOf(namedGroup));
+            return new SSLKeyExchange(null, ka);
         }
 
         return null;
@@ -337,7 +332,7 @@
         ECDH            ("ecdh",        null,
                                         ECDHKeyExchange.ecdhKAGenerator),
         ECDHE           ("ecdhe",       ECDHKeyExchange.poGenerator,
-                                        ECDHKeyExchange.ecdheKAGenerator);
+                                        ECDHKeyExchange.ecdheXdhKAGenerator);
 
         final String name;
         final SSLPossessionGenerator possessionGenerator;
@@ -570,27 +565,13 @@
 
         @Override
         public SSLPossession createPossession(HandshakeContext hc) {
-            if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
-                return new ECDHEPossession(
-                        namedGroup, hc.sslContext.getSecureRandom());
-            } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
-                return new DHEPossession(
-                        namedGroup, hc.sslContext.getSecureRandom());
-            }
-
-            return null;
+            return namedGroup.createPossession(hc.sslContext.getSecureRandom());
         }
 
         @Override
         public SSLKeyDerivation createKeyDerivation(
                 HandshakeContext hc) throws IOException {
-            if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
-                return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
-            } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
-                return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
-            }
-
-            return null;
+            return namedGroup.createKeyDerivation(hc);
         }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2019, 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
@@ -313,7 +313,9 @@
                     handshakeBuffer.put(handshakeFrag);
                     handshakeBuffer.rewind();
                     break;
-                } if (remaining == handshakeMessageLen) {
+                }
+
+                if (remaining == handshakeMessageLen) {
                     if (handshakeHash.isHashable(handshakeType)) {
                         handshakeHash.receive(handshakeFrag);
                     }
--- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Wed Jun 12 18:58:00 2019 -0700
@@ -26,7 +26,6 @@
 package sun.security.ssl;
 
 import java.security.*;
-import java.security.interfaces.ECPrivateKey;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.MGF1ParameterSpec;
@@ -39,8 +38,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupType;
 import sun.security.ssl.X509Authentication.X509Possession;
 import sun.security.util.KeyUtil;
 import sun.security.util.SignatureUtil;
@@ -432,10 +430,10 @@
         }
         for (SignatureScheme ss : schemes) {
             if (ss.isAvailable && (keySize >= ss.minimalKeySize) &&
-                ss.handshakeSupportedProtocols.contains(version) &&
-                keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
-                if (ss.namedGroup != null &&
-                    ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                    ss.handshakeSupportedProtocols.contains(version) &&
+                    keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
+                if ((ss.namedGroup != null) && (ss.namedGroup.type ==
+                        NamedGroupType.NAMED_GROUP_ECDHE)) {
                     ECParameterSpec params =
                             x509Possession.getECParameterSpec();
                     if (params != null &&
--- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Wed Jun 12 18:58:00 2019 -0700
@@ -30,29 +30,24 @@
 import java.security.AlgorithmConstraints;
 import java.security.AlgorithmParameters;
 import java.security.CryptoPrimitive;
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
-import java.security.spec.ECParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
-import javax.crypto.spec.DHParameterSpec;
 import javax.net.ssl.SSLProtocolException;
 import sun.security.action.GetPropertyAction;
+import sun.security.ssl.NamedGroup.NamedGroupType;
 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
 import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
 import sun.security.ssl.SSLExtension.ExtensionConsumer;
 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
-import sun.security.util.ECUtil;
+
 
 /**
  * Pack of the "supported_groups" extensions [RFC 4492/7919].
@@ -158,320 +153,11 @@
         }
     }
 
-    static enum NamedGroupType {
-        NAMED_GROUP_ECDHE     ("EC"),
-        NAMED_GROUP_FFDHE     ("DiffieHellman"),
-        NAMED_GROUP_X25519    ("x25519"),
-        NAMED_GROUP_X448      ("x448"),
-        NAMED_GROUP_ARBITRARY ("EC"),
-        NAMED_GROUP_NONE      ("");
-
-        private final String algorithm;
-
-        private NamedGroupType(String algorithm) {
-            this.algorithm = algorithm;
-        }
-
-        boolean isSupported(List<CipherSuite> cipherSuites) {
-            for (CipherSuite cs : cipherSuites) {
-                if (cs.keyExchange == null ||
-                        cs.keyExchange.groupType == this) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-    }
-
-    static enum NamedGroup {
-        // Elliptic Curves (RFC 4492)
-        //
-        // See sun.security.util.CurveDB for the OIDs
-        // NIST K-163
-        SECT163_K1  (0x0001, "sect163k1", "1.3.132.0.1",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECT163_R1  (0x0002, "sect163r1", "1.3.132.0.2",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST B-163
-        SECT163_R2  (0x0003, "sect163r2", "1.3.132.0.15",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECT193_R1  (0x0004, "sect193r1", "1.3.132.0.24",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECT193_R2  (0x0005, "sect193r2", "1.3.132.0.25",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST K-233
-        SECT233_K1  (0x0006, "sect233k1", "1.3.132.0.26",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST B-233
-        SECT233_R1  (0x0007, "sect233r1", "1.3.132.0.27",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECT239_K1  (0x0008, "sect239k1", "1.3.132.0.3",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST K-283
-        SECT283_K1  (0x0009, "sect283k1", "1.3.132.0.16",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST B-283
-        SECT283_R1  (0x000A, "sect283r1", "1.3.132.0.17",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST K-409
-        SECT409_K1  (0x000B, "sect409k1", "1.3.132.0.36",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST B-409
-        SECT409_R1  (0x000C, "sect409r1", "1.3.132.0.37",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST K-571
-        SECT571_K1  (0x000D, "sect571k1", "1.3.132.0.38",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST B-571
-        SECT571_R1  (0x000E, "sect571r1", "1.3.132.0.39",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECP160_K1  (0x000F, "secp160k1", "1.3.132.0.9",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECP160_R1  (0x0010, "secp160r1", "1.3.132.0.8",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECP160_R2  (0x0011, "secp160r2", "1.3.132.0.30",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECP192_K1  (0x0012, "secp192k1", "1.3.132.0.31",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST P-192
-        SECP192_R1  (0x0013, "secp192r1", "1.2.840.10045.3.1.1",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECP224_K1  (0x0014, "secp224k1", "1.3.132.0.32",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        // NIST P-224
-        SECP224_R1  (0x0015, "secp224r1", "1.3.132.0.33",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        SECP256_K1  (0x0016, "secp256k1", "1.3.132.0.10",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-
-        // NIST P-256
-        SECP256_R1  (0x0017, "secp256r1", "1.2.840.10045.3.1.7",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-
-        // NIST P-384
-        SECP384_R1  (0x0018, "secp384r1", "1.3.132.0.34",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-
-        // NIST P-521
-        SECP521_R1  (0x0019, "secp521r1", "1.3.132.0.35",
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-
-        // x25519 and x448
-        X25519      (0x001D, "x25519", null,
-                            NamedGroupType.NAMED_GROUP_X25519,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-        X448        (0x001E, "x448", null,
-                            NamedGroupType.NAMED_GROUP_X448,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-
-        // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
-        FFDHE_2048  (0x0100, "ffdhe2048", null,
-                            NamedGroupType.NAMED_GROUP_FFDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-        FFDHE_3072  (0x0101, "ffdhe3072", null,
-                            NamedGroupType.NAMED_GROUP_FFDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-        FFDHE_4096  (0x0102, "ffdhe4096", null,
-                            NamedGroupType.NAMED_GROUP_FFDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-        FFDHE_6144  (0x0103, "ffdhe6144", null,
-                            NamedGroupType.NAMED_GROUP_FFDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-        FFDHE_8192  (0x0104, "ffdhe8192", null,
-                            NamedGroupType.NAMED_GROUP_FFDHE,
-                            ProtocolVersion.PROTOCOLS_TO_13),
-
-        // Elliptic Curves (RFC 4492)
-        //
-        // arbitrary prime and characteristic-2 curves
-        ARBITRARY_PRIME  (0xFF01, "arbitrary_explicit_prime_curves", null,
-                            NamedGroupType.NAMED_GROUP_ARBITRARY,
-                            ProtocolVersion.PROTOCOLS_TO_12),
-        ARBITRARY_CHAR2  (0xFF02, "arbitrary_explicit_char2_curves", null,
-                            NamedGroupType.NAMED_GROUP_ARBITRARY,
-                            ProtocolVersion.PROTOCOLS_TO_12);
-
-        final int id;               // hash + signature
-        final NamedGroupType type;  // group type
-        final String name;          // literal name
-        final String oid;           // object identifier of the named group
-        final String algorithm;     // signature algorithm
-        final ProtocolVersion[] supportedProtocols;
-
-        private NamedGroup(int id, String name, String oid,
-                NamedGroupType namedGroupType,
-                ProtocolVersion[] supportedProtocols) {
-            this.id = id;
-            this.type = namedGroupType;
-            this.name = name;
-            this.oid = oid;
-            this.algorithm = namedGroupType.algorithm;
-            this.supportedProtocols = supportedProtocols;
-        }
-
-        static NamedGroup valueOf(int id) {
-            for (NamedGroup group : NamedGroup.values()) {
-                if (group.id == id) {
-                    return group;
-                }
-            }
-
-            return null;
-        }
-
-        static NamedGroup valueOf(ECParameterSpec params) {
-            String oid = ECUtil.getCurveName(null, params);
-            if ((oid != null) && (!oid.isEmpty())) {
-                for (NamedGroup group : NamedGroup.values()) {
-                    if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
-                            oid.equals(group.oid)) {
-                        return group;
-                    }
-                }
-            }
-
-            return null;
-        }
-
-        static NamedGroup valueOf(DHParameterSpec params) {
-            for (Map.Entry<NamedGroup, AlgorithmParameters> me :
-                    SupportedGroups.namedGroupParams.entrySet()) {
-                NamedGroup ng = me.getKey();
-                if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
-                    continue;
-                }
-
-                DHParameterSpec ngParams = null;
-                AlgorithmParameters aps = me.getValue();
-                try {
-                    ngParams = aps.getParameterSpec(DHParameterSpec.class);
-                } catch (InvalidParameterSpecException ipse) {
-                    // should be unlikely
-                }
-
-                if (ngParams == null) {
-                    continue;
-                }
-
-                if (ngParams.getP().equals(params.getP()) &&
-                        ngParams.getG().equals(params.getG())) {
-                    return ng;
-                }
-            }
-
-            return null;
-        }
-
-        static NamedGroup nameOf(String name) {
-            for (NamedGroup group : NamedGroup.values()) {
-                if (group.name.equals(name)) {
-                    return group;
-                }
-            }
-
-            return null;
-        }
-
-        static String nameOf(int id) {
-            for (NamedGroup group : NamedGroup.values()) {
-                if (group.id == id) {
-                    return group.name;
-                }
-            }
-
-            return "UNDEFINED-NAMED-GROUP(" + id + ")";
-        }
-
-        boolean isAvailable(List<ProtocolVersion> protocolVersions) {
-            for (ProtocolVersion pv : supportedProtocols) {
-                if (protocolVersions.contains(pv)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        boolean isAvailable(ProtocolVersion protocolVersion) {
-            for (ProtocolVersion pv : supportedProtocols) {
-                if (protocolVersion == pv) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        boolean isSupported(List<CipherSuite> cipherSuites) {
-            for (CipherSuite cs : cipherSuites) {
-                boolean isMatch = isAvailable(cs.supportedProtocols);
-                if (isMatch && (cs.keyExchange == null ||
-                        cs.keyExchange.groupType == type)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        // lazy loading of parameters
-        AlgorithmParameters getParameters() {
-            return SupportedGroups.namedGroupParams.get(this);
-        }
-
-        AlgorithmParameterSpec getParameterSpec() {
-            if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) {
-                return SupportedGroups.getECGenParamSpec(this);
-            } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) {
-                return SupportedGroups.getDHParameterSpec(this);
-            }
-
-            return null;
-        }
-    }
-
     static class SupportedGroups {
         // To switch off the supported_groups extension for DHE cipher suite.
         static final boolean enableFFDHE =
                 Utilities.getBooleanProperty("jsse.enableFFDHE", true);
 
-        // cache to speed up the parameters construction
-        static final Map<NamedGroup,
-                    AlgorithmParameters> namedGroupParams = new HashMap<>();
-
         // the supported named groups
         static final NamedGroup[] supportedNamedGroups;
 
@@ -516,10 +202,19 @@
                 }
             } else {        // default groups
                 NamedGroup[] groups = new NamedGroup[] {
-                        // NIST curves first
+
+                        // Primary XDH (RFC 7748) curves
+                        NamedGroup.X25519,
+
+                        // Primary NIST curves (e.g. used in TLSv1.3)
                         NamedGroup.SECP256_R1,
                         NamedGroup.SECP384_R1,
                         NamedGroup.SECP521_R1,
+
+                        // Secondary XDH curves
+                        NamedGroup.X448,
+
+                        // Secondary NIST curves
                         NamedGroup.SECT283_K1,
                         NamedGroup.SECT283_R1,
                         NamedGroup.SECT409_K1,
@@ -530,7 +225,7 @@
                         // non-NIST curves
                         NamedGroup.SECP256_K1,
 
-                        // FFDHE 2048
+                        // FFDHE (RFC 7919)
                         NamedGroup.FFDHE_2048,
                         NamedGroup.FFDHE_3072,
                         NamedGroup.FFDHE_4096,
@@ -560,126 +255,27 @@
 
         // check whether the group is supported by the underlying providers
         private static boolean isAvailableGroup(NamedGroup namedGroup) {
-            AlgorithmParameters params = null;
-            AlgorithmParameterSpec spec = null;
-            if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
-                if (namedGroup.oid != null) {
-                    try {
-                        params = AlgorithmParameters.getInstance("EC");
-                        spec = new ECGenParameterSpec(namedGroup.oid);
-                    } catch (NoSuchAlgorithmException e) {
-                        return false;
-                    }
-                }
-            } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
-                try {
-                    params = AlgorithmParameters.getInstance("DiffieHellman");
-                    spec = getFFDHEDHParameterSpec(namedGroup);
-                } catch (NoSuchAlgorithmException e) {
-                    return false;
-                }
-            }   // Otherwise, unsupported.
+            return namedGroup.isAvailableGroup();
+        }
 
-            if ((params != null) && (spec != null)) {
-                try {
-                    params.init(spec);
-                } catch (InvalidParameterSpecException e) {
-                    return false;
-                }
-
-                // cache the parameters
-                namedGroupParams.put(namedGroup, params);
-
-                return true;
+        static ECGenParameterSpec getECGenParamSpec(NamedGroup ng) {
+            if (ng.type != NamedGroupType.NAMED_GROUP_ECDHE) {
+                 throw new RuntimeException(
+                         "Not a named EC group: " + ng);
             }
 
-            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;
-        }
-
-        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);
-            if (params == null) {
-                throw new RuntimeException(
-                        "Not a supported EC named group: " + namedGroup);
-            }
-
+            // parameters are non-null
+            AlgorithmParameters params = ng.getParameters();
             try {
                 return params.getParameterSpec(ECGenParameterSpec.class);
             } catch (InvalidParameterSpecException ipse) {
                 // should be unlikely
-                return new ECGenParameterSpec(namedGroup.oid);
+                return new ECGenParameterSpec(ng.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);
-            if (params == null) {
-                throw new RuntimeException(
-                        "Not a supported DH named group: " + namedGroup);
-            }
-
-            try {
-                return params.getParameterSpec(DHParameterSpec.class);
-            } catch (InvalidParameterSpecException ipse) {
-                // should be unlikely
-                return getPredefinedDHParameterSpec(namedGroup);
-            }
+        static AlgorithmParameters getParameters(NamedGroup ng) {
+            return ng.getParameters();
         }
 
         // Is there any supported group permitted by the constraints?
@@ -692,7 +288,7 @@
                     if (constraints.permits(
                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                             namedGroup.algorithm,
-                            namedGroupParams.get(namedGroup))) {
+                            getParameters(namedGroup))) {
 
                         return true;
                     }
@@ -723,7 +319,7 @@
             return constraints.permits(
                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                             namedGroup.algorithm,
-                            namedGroupParams.get(namedGroup));
+                            getParameters(namedGroup));
         }
 
         // Is the named group supported?
@@ -739,16 +335,16 @@
 
         static NamedGroup getPreferredGroup(
                 ProtocolVersion negotiatedProtocol,
-                AlgorithmConstraints constraints, NamedGroupType type,
+                AlgorithmConstraints constraints, NamedGroupType[] types,
                 List<NamedGroup> requestedNamedGroups) {
             for (NamedGroup namedGroup : requestedNamedGroups) {
-                if ((namedGroup.type == type) &&
+                if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
                         namedGroup.isAvailable(negotiatedProtocol) &&
                         isSupported(namedGroup) &&
                         constraints.permits(
                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                                 namedGroup.algorithm,
-                                namedGroupParams.get(namedGroup))) {
+                                getParameters(namedGroup))) {
                     return namedGroup;
                 }
             }
@@ -758,14 +354,14 @@
 
         static NamedGroup getPreferredGroup(
                 ProtocolVersion negotiatedProtocol,
-                AlgorithmConstraints constraints, NamedGroupType type) {
+                AlgorithmConstraints constraints, NamedGroupType[] types) {
             for (NamedGroup namedGroup : supportedNamedGroups) {
-                if ((namedGroup.type == type) &&
+                if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
                         namedGroup.isAvailable(negotiatedProtocol) &&
                         constraints.permits(
                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                                 namedGroup.algorithm,
-                                namedGroupParams.get(namedGroup))) {
+                                getParameters(namedGroup))) {
                     return namedGroup;
                 }
             }
@@ -813,7 +409,7 @@
                         ng.isSupported(chc.activeCipherSuites) &&
                         chc.algorithmConstraints.permits(
                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                            ng.algorithm, namedGroupParams.get(ng))) {
+                            ng.algorithm, getParameters(ng))) {
                     namedGroups.add(ng);
                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.fine(
@@ -940,7 +536,7 @@
                         ng.isSupported(shc.activeCipherSuites) &&
                         shc.algorithmConstraints.permits(
                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                            ng.algorithm, namedGroupParams.get(ng))) {
+                            ng.algorithm, getParameters(ng))) {
                     namedGroups.add(ng);
                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.fine(
--- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java	Wed Jun 12 18:58:00 2019 -0700
@@ -39,7 +39,6 @@
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSocket;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 
 /**
  * SSL/(D)TLS transportation context.
--- a/src/java.base/share/classes/sun/security/ssl/Utilities.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/Utilities.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -61,8 +61,8 @@
 
         int size = serverNames.size();
         List<SNIServerName> sniList = (size != 0) ?
-                new ArrayList<SNIServerName>(serverNames) :
-                new ArrayList<SNIServerName>(1);
+                new ArrayList<>(serverNames) :
+                new ArrayList<>(1);
 
         boolean reset = false;
         for (int i = 0; i < size; i++) {
@@ -147,7 +147,7 @@
     static String indent(String source, String prefix) {
         StringBuilder builder = new StringBuilder();
         if (source == null) {
-             builder.append("\n" + prefix + "<blank message>");
+             builder.append("\n").append(prefix).append("<blank message>");
         } else {
             String[] lines = lineBreakPatern.split(source);
             boolean isFirst = true;
@@ -232,4 +232,21 @@
         }
         return b;
     }
+
+    static void reverseBytes(byte[] arr) {
+        int i = 0;
+        int j = arr.length - 1;
+
+        while (i < j) {
+            swap(arr, i, j);
+            i++;
+            j--;
+        }
+    }
+
+    private static void swap(byte[] arr, int i, int j) {
+        byte tmp = arr[i];
+        arr[i] = arr[j];
+        arr[j] = tmp;
+    }
 }
--- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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,13 +30,15 @@
 import java.security.cert.X509Certificate;
 import java.security.interfaces.ECKey;
 import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.XECKey;
+import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECParameterSpec;
+import java.security.spec.NamedParameterSpec;
 import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.Map;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.X509ExtendedKeyManager;
-import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 
 enum X509Authentication implements SSLAuthentication {
@@ -148,6 +150,35 @@
 
             return null;
         }
+
+        // Similar to above, but for XEC.
+        NamedParameterSpec getXECParameterSpec() {
+            if (popPrivateKey == null ||
+                    !"XEC".equals(popPrivateKey.getAlgorithm())) {
+                return null;
+            }
+
+            if (popPrivateKey instanceof XECKey) {
+                AlgorithmParameterSpec params =
+                        ((XECKey)popPrivateKey).getParams();
+                if (params instanceof NamedParameterSpec){
+                    return (NamedParameterSpec)params;
+                }
+            } else if (popCerts != null && popCerts.length != 0) {
+                // The private key not extractable, get the parameters from
+                // the X.509 certificate.
+                PublicKey publicKey = popCerts[0].getPublicKey();
+                if (publicKey instanceof XECKey) {
+                    AlgorithmParameterSpec params =
+                            ((XECKey)publicKey).getParams();
+                    if (params instanceof NamedParameterSpec){
+                        return (NamedParameterSpec)params;
+                    }
+                }
+            }
+
+            return null;
+        }
     }
 
     static final class X509Credentials implements SSLCredentials {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java	Wed Jun 12 18:58:00 2019 -0700
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2019, 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.math.BigInteger;
+import java.security.*;
+import java.security.interfaces.XECPublicKey;
+import java.security.spec.*;
+import sun.security.ssl.NamedGroup.NamedGroupType;
+import sun.security.util.*;
+
+/**
+ * Specifics for XEC/XDH Keys/Exchanges
+ */
+final class XDHKeyExchange {
+
+    static final SSLKeyAgreementGenerator xdheKAGenerator
+            = new XDHEKAGenerator();
+
+    static final class XDHECredentials implements NamedGroupCredentials {
+
+        final XECPublicKey popPublicKey;
+        final NamedGroup namedGroup;
+
+        XDHECredentials(XECPublicKey popPublicKey, NamedGroup namedGroup) {
+            this.popPublicKey = popPublicKey;
+            this.namedGroup = namedGroup;
+        }
+
+        @Override
+        public PublicKey getPublicKey() {
+            return popPublicKey;
+        }
+
+        @Override
+        public NamedGroup getNamedGroup() {
+            return namedGroup;
+        }
+
+        /**
+         * Parse the encoded Point into the XDHECredentials using the
+         * namedGroup.
+         */
+        static XDHECredentials valueOf(NamedGroup namedGroup,
+                byte[] encodedPoint) throws IOException,
+                GeneralSecurityException {
+
+            if (namedGroup.type != NamedGroupType.NAMED_GROUP_XDH) {
+                throw new RuntimeException(
+                        "Credentials decoding:  Not XDH named group");
+            }
+
+            if (encodedPoint == null || encodedPoint.length == 0) {
+                return null;
+            }
+
+            byte[] uBytes = encodedPoint.clone();
+            Utilities.reverseBytes(uBytes);
+            BigInteger u = new BigInteger(1, uBytes);
+
+            XECPublicKeySpec xecPublicKeySpec = new XECPublicKeySpec(
+                    new NamedParameterSpec(namedGroup.name), u);
+            KeyFactory factory = KeyFactory.getInstance(namedGroup.algorithm);
+            XECPublicKey publicKey = (XECPublicKey) factory.generatePublic(
+                    xecPublicKeySpec);
+
+            return new XDHECredentials(publicKey, namedGroup);
+        }
+    }
+
+    static final class XDHEPossession implements NamedGroupPossession {
+
+        final PrivateKey privateKey;
+        final XECPublicKey publicKey;
+        final NamedGroup namedGroup;
+
+        XDHEPossession(NamedGroup namedGroup, SecureRandom random) {
+            try {
+                KeyPairGenerator kpg
+                        = KeyPairGenerator.getInstance(namedGroup.algorithm);
+                AlgorithmParameterSpec params = namedGroup.getParameterSpec();
+                kpg.initialize(params, random);
+                KeyPair kp = kpg.generateKeyPair();
+                privateKey = kp.getPrivate();
+                publicKey = (XECPublicKey) kp.getPublic();
+            } catch (GeneralSecurityException e) {
+                throw new RuntimeException(
+                        "Could not generate XDH keypair", e);
+            }
+
+            this.namedGroup = namedGroup;
+        }
+
+        @Override
+        public byte[] encode() {
+
+            byte[] uBytes = ECUtil.trimZeroes(publicKey.getU().toByteArray());
+
+            int expLength;
+            switch (namedGroup) {
+                case X25519:
+                    expLength = 32;
+                    break;
+                case X448:
+                    expLength = 56;
+                    break;
+                default:
+                    throw new RuntimeException("Invalid XDH group");
+            }
+
+            if (uBytes.length > expLength) {
+                throw new RuntimeException("Encoded XDH key too large");
+            }
+
+            if (uBytes.length != expLength) {
+                byte[] tmp = new byte[expLength];
+                System.arraycopy(uBytes, 0, tmp,
+                        expLength - uBytes.length, uBytes.length);
+                uBytes = tmp;
+            }
+
+            Utilities.reverseBytes(uBytes);
+            return (uBytes);
+        }
+
+        @Override
+        public PublicKey getPublicKey() {
+            return publicKey;
+        }
+
+        @Override
+        public NamedGroup getNamedGroup() {
+            return namedGroup;
+        }
+
+        @Override
+        public PrivateKey getPrivateKey() {
+            return privateKey;
+        }
+    }
+
+    private static final class XDHEKAGenerator
+            implements SSLKeyAgreementGenerator {
+
+        // Prevent instantiation of this class.
+        private XDHEKAGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+            XDHEPossession xdhePossession = null;
+            XDHECredentials xdheCredentials = null;
+            for (SSLPossession poss : context.handshakePossessions) {
+                if (!(poss instanceof XDHEPossession)) {
+                    continue;
+                }
+
+                NamedGroup ng = ((XDHEPossession) poss).namedGroup;
+                for (SSLCredentials cred : context.handshakeCredentials) {
+                    if (!(cred instanceof XDHECredentials)) {
+                        continue;
+                    }
+                    if (ng.equals(((XDHECredentials) cred).namedGroup)) {
+                        xdheCredentials = (XDHECredentials) cred;
+                        break;
+                    }
+                }
+
+                if (xdheCredentials != null) {
+                    xdhePossession = (XDHEPossession) poss;
+                    break;
+                }
+            }
+
+            if (xdhePossession == null || xdheCredentials == null) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No sufficient XDHE key agreement "
+                        + "parameters negotiated");
+            }
+
+            return new KAKeyDerivation("XDH", context,
+                    xdhePossession.privateKey, xdheCredentials.popPublicKey);
+        }
+    }
+}
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -38,6 +38,7 @@
 import javax.crypto.KeyAgreementSpi;
 import javax.crypto.SecretKey;
 import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
 import java.util.function.Function;
 
 public class XDHKeyAgreement extends KeyAgreementSpi {
@@ -202,7 +203,15 @@
             throws IllegalStateException, NoSuchAlgorithmException,
             InvalidKeyException {
 
-        throw new NoSuchAlgorithmException("Not supported");
+        if (algorithm == null) {
+            throw new NoSuchAlgorithmException("Algorithm must not be null");
+        }
+
+        if (!(algorithm.equals("TlsPremasterSecret"))) {
+            throw new NoSuchAlgorithmException(
+                    "Only supported for algorithm TlsPremasterSecret");
+        }
+        return new SecretKeySpec(engineGenerateSecret(), algorithm);
     }
 
     static class X25519 extends XDHKeyAgreement {
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -156,7 +156,7 @@
             new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
         bySize.put(bits, params);
         byOid.put(oid, params);
-        byName.put(name, params);
+        byName.put(name.toLowerCase(), params);
     }
 
     public static Optional<XECParameters> getByOid(ObjectIdentifier id) {
@@ -166,7 +166,7 @@
         return Optional.ofNullable(SIZE_MAP.get(size));
     }
     public static Optional<XECParameters> getByName(String name) {
-        return Optional.ofNullable(NAME_MAP.get(name));
+        return Optional.ofNullable(NAME_MAP.get(name.toLowerCase()));
     }
 
     boolean oidEquals(XECParameters other) {
--- a/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java	Wed Jun 12 23:21:24 2019 +0200
+++ b/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java	Wed Jun 12 18:58:00 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -189,10 +189,15 @@
     }
 
     /*
+     * Configure the client side socket.
+     */
+    protected void configureClientSocket(SSLSocket socket) {
+    }
+
+    /*
      * Configure the server side socket.
      */
     protected void configureServerSocket(SSLServerSocket socket) {
-
     }
 
     /*
@@ -317,6 +322,7 @@
 
         try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
             try {
+                configureClientSocket(sslSocket);
                 sslSocket.connect(
                         new InetSocketAddress("localhost", serverPort), 15000);
             } catch (IOException ioe) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java	Wed Jun 12 18:58:00 2019 -0700
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+ /*
+  * @test
+  * @bug 8171279
+  * @library /javax/net/ssl/templates
+  * @summary Test TLS connection with each individual supported group
+  * @run main/othervm SupportedGroups x25519
+  * @run main/othervm SupportedGroups x448
+  * @run main/othervm SupportedGroups secp256r1
+  * @run main/othervm SupportedGroups secp384r1
+  * @run main/othervm SupportedGroups secp521r1
+  * @run main/othervm SupportedGroups ffdhe2048
+  * @run main/othervm SupportedGroups ffdhe3072
+  * @run main/othervm SupportedGroups ffdhe4096
+  * @run main/othervm SupportedGroups ffdhe6144
+  * @run main/othervm SupportedGroups ffdhe8192
+ */
+import java.util.Arrays;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLServerSocket;
+
+public class SupportedGroups extends SSLSocketTemplate {
+
+    private static volatile int index;
+    private static final String[][][] protocols = {
+        {{"TLSv1.3"}, {"TLSv1.3"}},
+        {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}},
+        {{"TLSv1.2"}, {"TLSv1.3", "TLSv1.2"}},
+        {{"TLSv1.2"}, {"TLSv1.2"}}
+    };
+
+    // Servers are configured before clients, increment test case after.
+    @Override
+    protected void configureClientSocket(SSLSocket socket) {
+        String[] ps = protocols[index][0];
+
+        System.out.print("Setting client protocol(s): ");
+        Arrays.stream(ps).forEachOrdered(System.out::print);
+        System.out.println();
+
+        socket.setEnabledProtocols(ps);
+    }
+
+    @Override
+    protected void configureServerSocket(SSLServerSocket serverSocket) {
+        String[] ps = protocols[index][1];
+
+        System.out.print("Setting server protocol(s): ");
+        Arrays.stream(ps).forEachOrdered(System.out::print);
+        System.out.println();
+
+        serverSocket.setEnabledProtocols(ps);
+    }
+
+    /*
+     * Run the test case.
+     */
+    public static void main(String[] args) throws Exception {
+        System.setProperty("jdk.tls.namedGroups", args[0]);
+
+        for (index = 0; index < protocols.length; index++) {
+            (new SupportedGroups()).run();
+        }
+    }
+}