8226374: Restrict TLS signature schemes and named groups
authorxuelei
Mon, 12 Aug 2019 21:36:29 -0700
changeset 57718 a93b7b28f644
parent 57716 bfcdcd00e4fb
child 57720 75375b6617e6
8226374: Restrict TLS signature schemes and named groups Reviewed-by: mullan
src/java.base/share/classes/sun/security/ssl/CertificateMessage.java
src/java.base/share/classes/sun/security/ssl/CertificateRequest.java
src/java.base/share/classes/sun/security/ssl/CertificateVerify.java
src/java.base/share/classes/sun/security/ssl/CipherSuite.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/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/NamedGroup.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/XDHKeyExchange.java
src/java.base/share/classes/sun/security/util/CurveDB.java
src/java.base/share/conf/security/java.security
test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java
test/jdk/sun/security/ssl/CipherSuite/RestrictSignatureScheme.java
--- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java	Mon Aug 12 21:36:29 2019 -0700
@@ -1034,6 +1034,7 @@
                 // Don't select a signature scheme unless we will be able to
                 // produce a CertificateVerify message later
                 if (SignatureScheme.getPreferableAlgorithm(
+                        hc.algorithmConstraints,
                         hc.peerRequestedSignatureSchemes,
                         ss, hc.negotiatedProtocol) == null) {
 
--- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java	Mon Aug 12 21:36:29 2019 -0700
@@ -736,6 +736,7 @@
                 // Don't select a signature scheme unless we will be able to
                 // produce a CertificateVerify message later
                 if (SignatureScheme.getPreferableAlgorithm(
+                        hc.algorithmConstraints,
                         hc.peerRequestedSignatureSchemes,
                         ss, hc.negotiatedProtocol) == null) {
 
--- a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java	Mon Aug 12 21:36:29 2019 -0700
@@ -564,6 +564,7 @@
             // This happens in client side only.
             ClientHandshakeContext chc = (ClientHandshakeContext)context;
             this.signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                    chc.algorithmConstraints,
                     chc.peerRequestedSignatureSchemes,
                     x509Possession,
                     chc.negotiatedProtocol);
@@ -865,6 +866,7 @@
             super(context);
 
             this.signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                    context.algorithmConstraints,
                     context.peerRequestedSignatureSchemes,
                     x509Possession,
                     context.negotiatedProtocol);
--- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Mon Aug 12 21:36:29 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.NamedGroup.NamedGroupType;
-import static sun.security.ssl.NamedGroup.NamedGroupType.*;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
+import static sun.security.ssl.NamedGroup.NamedGroupSpec.*;
 
 /**
  * Enum for SSL/(D)TLS cipher suites.
@@ -1125,12 +1125,12 @@
         // name of the key exchange algorithm, e.g. DHE_DSS
         final String name;
         final boolean allowed;
-        final NamedGroupType[] groupTypes;
+        final NamedGroupSpec[] groupTypes;
         private final boolean alwaysAvailable;
         private final boolean isAnonymous;
 
         KeyExchange(String name, boolean allowed,
-                boolean isAnonymous, NamedGroupType... groupTypes) {
+                boolean isAnonymous, NamedGroupSpec... groupTypes) {
             this.name = name;
             this.groupTypes = groupTypes;
             this.allowed = allowed;
@@ -1144,8 +1144,8 @@
                 return true;
             }
 
-            if (NamedGroupType.arrayContains(
-                    groupTypes, NamedGroupType.NAMED_GROUP_ECDHE)) {
+            if (NamedGroupSpec.arrayContains(groupTypes,
+                    NamedGroupSpec.NAMED_GROUP_ECDHE)) {
                 return (allowed && JsseJce.isEcAvailable());
             } else {
                 return allowed;
--- a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java	Mon Aug 12 21:36:29 2019 -0700
@@ -41,7 +41,7 @@
 import javax.crypto.spec.DHParameterSpec;
 import javax.crypto.spec.DHPublicKeySpec;
 import sun.security.action.GetPropertyAction;
-import sun.security.ssl.NamedGroup.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Possession;
 import sun.security.util.KeyUtil;
@@ -76,7 +76,7 @@
         static DHECredentials valueOf(NamedGroup ng,
             byte[] encodedPublic) throws IOException, GeneralSecurityException {
 
-            if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+            if (ng.spec != NamedGroupSpec.NAMED_GROUP_FFDHE) {
                 throw new RuntimeException(
                         "Credentials decoding:  Not FFDHE named group");
             }
@@ -85,11 +85,7 @@
                 return null;
             }
 
-            DHParameterSpec params = (DHParameterSpec)ng.getParameterSpec();
-            if (params == null) {
-                return null;
-            }
-
+            DHParameterSpec params = (DHParameterSpec)ng.keAlgParamSpec;
             KeyFactory kf = KeyFactory.getInstance("DiffieHellman");
             DHPublicKeySpec spec = new DHPublicKeySpec(
                     new BigInteger(1, encodedPublic),
@@ -110,9 +106,7 @@
             try {
                 KeyPairGenerator kpg =
                         KeyPairGenerator.getInstance("DiffieHellman");
-                DHParameterSpec params =
-                        (DHParameterSpec)namedGroup.getParameterSpec();
-                kpg.initialize(params, random);
+                kpg.initialize(namedGroup.keAlgParamSpec, random);
                 KeyPair kp = generateDHKeyPair(kpg);
                 if (kp == null) {
                     throw new RuntimeException("Could not generate DH keypair");
@@ -321,11 +315,10 @@
                     (context.clientRequestedNamedGroups != null) &&
                     (!context.clientRequestedNamedGroups.isEmpty())) {
                 preferableNamedGroup =
-                        SupportedGroups.getPreferredGroup(
-                                context.negotiatedProtocol,
+                        SupportedGroups.getPreferredGroup(context.negotiatedProtocol,
                                 context.algorithmConstraints,
-                                new NamedGroupType [] {
-                                    NamedGroupType.NAMED_GROUP_FFDHE },
+                                new NamedGroupSpec [] {
+                                    NamedGroupSpec.NAMED_GROUP_FFDHE },
                                 context.clientRequestedNamedGroups);
                 if (preferableNamedGroup != null) {
                     return new DHEPossession(preferableNamedGroup,
--- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java	Mon Aug 12 21:36:29 2019 -0700
@@ -125,6 +125,7 @@
                 Signature signer = null;
                 if (useExplicitSigAlgorithm) {
                     signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                            shc.algorithmConstraints,
                             shc.peerRequestedSignatureSchemes,
                             x509Possession,
                             shc.negotiatedProtocol);
--- a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java	Mon Aug 12 21:36:29 2019 -0700
@@ -36,7 +36,6 @@
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
 import java.security.spec.ECPublicKeySpec;
@@ -44,7 +43,7 @@
 import javax.crypto.KeyAgreement;
 import javax.crypto.SecretKey;
 import javax.net.ssl.SSLHandshakeException;
-import sun.security.ssl.NamedGroup.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Credentials;
 import sun.security.ssl.X509Authentication.X509Possession;
@@ -88,7 +87,7 @@
         static ECDHECredentials valueOf(NamedGroup namedGroup,
             byte[] encodedPoint) throws IOException, GeneralSecurityException {
 
-            if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
+            if (namedGroup.spec != NamedGroupSpec.NAMED_GROUP_ECDHE) {
                 throw new RuntimeException(
                     "Credentials decoding:  Not ECDHE named group");
             }
@@ -98,11 +97,7 @@
             }
 
             ECParameterSpec parameters =
-                    ECUtil.getECParameterSpec(null, namedGroup.oid);
-            if (parameters == null) {
-                return null;
-            }
-
+                    (ECParameterSpec)namedGroup.keAlgParamSpec;
             ECPoint point = ECUtil.decodePoint(
                     encodedPoint, parameters.getCurve());
             KeyFactory factory = KeyFactory.getInstance("EC");
@@ -120,9 +115,7 @@
         ECDHEPossession(NamedGroup namedGroup, SecureRandom random) {
             try {
                 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
-                ECGenParameterSpec params =
-                        (ECGenParameterSpec)namedGroup.getParameterSpec();
-                kpg.initialize(params, random);
+                kpg.initialize(namedGroup.keAlgParamSpec, random);
                 KeyPair kp = kpg.generateKeyPair();
                 privateKey = kp.getPrivate();
                 publicKey = (ECPublicKey)kp.getPublic();
@@ -248,17 +241,17 @@
                 preferableNamedGroup = SupportedGroups.getPreferredGroup(
                         context.negotiatedProtocol,
                         context.algorithmConstraints,
-                        new NamedGroupType[] {
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            NamedGroupType.NAMED_GROUP_XDH },
+                        new NamedGroupSpec[] {
+                            NamedGroupSpec.NAMED_GROUP_ECDHE,
+                            NamedGroupSpec.NAMED_GROUP_XDH },
                         context.clientRequestedNamedGroups);
             } else {
                 preferableNamedGroup = SupportedGroups.getPreferredGroup(
                         context.negotiatedProtocol,
                         context.algorithmConstraints,
-                        new NamedGroupType[] {
-                            NamedGroupType.NAMED_GROUP_ECDHE,
-                            NamedGroupType.NAMED_GROUP_XDH });
+                        new NamedGroupSpec[] {
+                            NamedGroupSpec.NAMED_GROUP_ECDHE,
+                            NamedGroupSpec.NAMED_GROUP_XDH });
             }
 
             if (preferableNamedGroup != null) {
@@ -308,7 +301,8 @@
 
                 NamedGroup ng = NamedGroup.valueOf(params);
                 if (ng == 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 ECDH key exchange");
                 }
@@ -480,7 +474,7 @@
             }
 
             String alg;
-            switch (namedGroup.type) {
+            switch (namedGroup.spec) {
                 case NAMED_GROUP_ECDHE:
                     alg = "ECDH";
                     break;
--- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java	Mon Aug 12 21:36:29 2019 -0700
@@ -38,6 +38,7 @@
 import java.security.SignatureException;
 import java.text.MessageFormat;
 import java.util.Locale;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Credentials;
@@ -110,13 +111,18 @@
 
             // Find the NamedGroup used for the ephemeral keys.
             namedGroup = namedGroupPossession.getNamedGroup();
+            if ((namedGroup == null) || (!namedGroup.isAvailable)) {
+                // unlikely
+                throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Missing or improper named group: " + namedGroup);
+            }
+
             publicPoint = namedGroup.encodePossessionPublicKey(
                     namedGroupPossession);
-
-            if ((namedGroup == null) || (namedGroup.oid == null) ) {
+            if (publicPoint == null) {
                 // unlikely
                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
-                    "Missing Named Group");
+                    "Missing public point for named group: " + namedGroup);
             }
 
             if (x509Possession == null) {
@@ -130,6 +136,7 @@
                 Signature signer = null;
                 if (useExplicitSigAlgorithm) {
                     signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                            shc.algorithmConstraints,
                             shc.peerRequestedSignatureSchemes,
                             x509Possession,
                             shc.negotiatedProtocol);
--- a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java	Mon Aug 12 21:36:29 2019 -0700
@@ -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.NamedGroup.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 
 /**
  * Pack of the "ec_point_formats" extensions [RFC 4492].
@@ -179,7 +179,7 @@
             // Produce the extension.
             //
             // produce the extension only if EC cipher suite is activated.
-            if (NamedGroupType.NAMED_GROUP_ECDHE.isSupported(
+            if (NamedGroupSpec.NAMED_GROUP_ECDHE.isSupported(
                     chc.activeCipherSuites)) {
                 // We are using uncompressed ECPointFormat only at present.
                 byte[] extData = new byte[] {0x01, 0x00};
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Mon Aug 12 21:36:29 2019 -0700
@@ -46,8 +46,8 @@
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLHandshakeException;
 import javax.security.auth.x500.X500Principal;
-import sun.security.ssl.NamedGroup.NamedGroupType;
-import static sun.security.ssl.NamedGroup.NamedGroupType.*;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
+import static sun.security.ssl.NamedGroup.NamedGroupSpec.*;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 
 abstract class HandshakeContext implements ConnectionContext {
@@ -283,8 +283,8 @@
             }
 
             boolean found = false;
-            Map<NamedGroupType, Boolean> cachedStatus =
-                    new EnumMap<>(NamedGroupType.class);
+            Map<NamedGroupSpec, Boolean> cachedStatus =
+                    new EnumMap<>(NamedGroupSpec.class);
             for (CipherSuite suite : enabledCipherSuites) {
                 if (suite.isAvailable() && suite.supports(protocol)) {
                     if (isActivatable(suite,
@@ -323,8 +323,8 @@
 
         List<CipherSuite> suites = new LinkedList<>();
         if (enabledProtocols != null && !enabledProtocols.isEmpty()) {
-            Map<NamedGroupType, Boolean> cachedStatus =
-                    new EnumMap<>(NamedGroupType.class);
+            Map<NamedGroupSpec, Boolean> cachedStatus =
+                    new EnumMap<>(NamedGroupSpec.class);
             for (CipherSuite suite : enabledCipherSuites) {
                 if (!suite.isAvailable()) {
                     continue;
@@ -509,7 +509,7 @@
 
     private static boolean isActivatable(CipherSuite suite,
             AlgorithmConstraints algorithmConstraints,
-            Map<NamedGroupType, Boolean> cachedStatus) {
+            Map<NamedGroupSpec, Boolean> cachedStatus) {
 
         if (algorithmConstraints.permits(
                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
@@ -520,8 +520,8 @@
 
             // Is at least one of the group types available?
             boolean groupAvailable, retval = false;
-            NamedGroupType[] groupTypes = suite.keyExchange.groupTypes;
-            for (NamedGroupType groupType : groupTypes) {
+            NamedGroupSpec[] groupTypes = suite.keyExchange.groupTypes;
+            for (NamedGroupSpec groupType : groupTypes) {
                 if (groupType != NAMED_GROUP_NONE) {
                     Boolean checkedStatus = cachedStatus.get(groupType);
                     if (checkedStatus == null) {
--- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java	Mon Aug 12 21:36:29 2019 -0700
@@ -29,16 +29,15 @@
 import java.io.IOException;
 import java.security.*;
 import java.security.spec.*;
+import java.util.Collections;
 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 java.util.Set;
+import javax.crypto.KeyAgreement;
 import sun.security.ssl.DHKeyExchange.DHEPossession;
 import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+import sun.security.util.CurveDB;
 
-import sun.security.util.ECUtil;
 
 /**
  * An enum containing all known named groups for use in TLS.
@@ -52,186 +51,253 @@
     // 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),
+    SECT163_K1(0x0001, "sect163k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect163k1")),
+    SECT163_R1(0x0002, "sect163r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect163r1")),
 
     // 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),
+    SECT163_R2(0x0003, "sect163r2",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect163r2")),
+    SECT193_R1(0x0004, "sect193r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect193r1")),
+    SECT193_R2(0x0005, "sect193r2",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect193r2")),
 
     // NIST K-233
-    SECT233_K1(0x0006, "sect233k1", "1.3.132.0.26",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT233_K1(0x0006, "sect233k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect233k1")),
 
     // 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),
+    SECT233_R1(0x0007, "sect233r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect233r1")),
+    SECT239_K1(0x0008, "sect239k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect239k1")),
 
     // NIST K-283
-    SECT283_K1(0x0009, "sect283k1", "1.3.132.0.16",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT283_K1(0x0009, "sect283k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect283k1")),
 
     // NIST B-283
-    SECT283_R1(0x000A, "sect283r1", "1.3.132.0.17",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT283_R1(0x000A, "sect283r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect283r1")),
 
     // NIST K-409
-    SECT409_K1(0x000B, "sect409k1", "1.3.132.0.36",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT409_K1(0x000B, "sect409k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect409k1")),
 
     // NIST B-409
-    SECT409_R1(0x000C, "sect409r1", "1.3.132.0.37",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT409_R1(0x000C, "sect409r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect409r1")),
 
     // NIST K-571
-    SECT571_K1(0x000D, "sect571k1", "1.3.132.0.38",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_12),
+    SECT571_K1(0x000D, "sect571k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect571k1")),
 
     // 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),
+    SECT571_R1(0x000E, "sect571r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("sect571r1")),
+    SECP160_K1(0x000F, "secp160k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp160k1")),
+    SECP160_R1(0x0010, "secp160r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp160r1")),
+    SECP160_R2(0x0011, "secp160r2",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp160r2")),
+    SECP192_K1(0x0012, "secp192k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp192k1")),
 
     // 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),
+    SECP192_R1(0x0013, "secp192r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp192r1")),
+    SECP224_K1(0x0014, "secp224k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp224k1")),
 
     // 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),
+    SECP224_R1(0x0015, "secp224r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp224r1")),
+    SECP256_K1(0x0016, "secp256k1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            CurveDB.lookup("secp256k1")),
 
     // NIST P-256
-    SECP256_R1(0x0017, "secp256r1", "1.2.840.10045.3.1.7",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_13),
+    SECP256_R1(0x0017, "secp256r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            CurveDB.lookup("secp256r1")),
 
     // NIST P-384
-    SECP384_R1(0x0018, "secp384r1", "1.3.132.0.34",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_13),
+    SECP384_R1(0x0018, "secp384r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            CurveDB.lookup("secp384r1")),
 
     // NIST P-521
-    SECP521_R1(0x0019, "secp521r1", "1.3.132.0.35",
-            NamedGroupType.NAMED_GROUP_ECDHE,
-            ProtocolVersion.PROTOCOLS_TO_13),
+    SECP521_R1(0x0019, "secp521r1",
+            NamedGroupSpec.NAMED_GROUP_ECDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            CurveDB.lookup("secp521r1")),
 
     // 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),
+    X25519(0x001D, "x25519",
+            NamedGroupSpec.NAMED_GROUP_XDH,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            NamedParameterSpec.X25519),
+    X448(0x001E, "x448",
+            NamedGroupSpec.NAMED_GROUP_XDH,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            NamedParameterSpec.X448),
 
     // 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),
+    FFDHE_2048(0x0100, "ffdhe2048",
+            NamedGroupSpec.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            PredefinedDHParameterSpecs.ffdheParams.get(2048)),
+
+    FFDHE_3072(0x0101, "ffdhe3072",
+            NamedGroupSpec.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            PredefinedDHParameterSpecs.ffdheParams.get(3072)),
+    FFDHE_4096(0x0102, "ffdhe4096",
+            NamedGroupSpec.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            PredefinedDHParameterSpecs.ffdheParams.get(4096)),
+    FFDHE_6144(0x0103, "ffdhe6144",
+            NamedGroupSpec.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            PredefinedDHParameterSpecs.ffdheParams.get(6144)),
+    FFDHE_8192(0x0104, "ffdhe8192",
+            NamedGroupSpec.NAMED_GROUP_FFDHE,
+            ProtocolVersion.PROTOCOLS_TO_13,
+            PredefinedDHParameterSpecs.ffdheParams.get(8192)),
 
     // 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);
+    ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves",
+            NamedGroupSpec.NAMED_GROUP_ARBITRARY,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            null),
+    ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves",
+            NamedGroupSpec.NAMED_GROUP_ARBITRARY,
+            ProtocolVersion.PROTOCOLS_TO_12,
+            null);
 
     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 NamedGroupSpec spec;  // group type
     final ProtocolVersion[] supportedProtocols;
-    private final NamedGroupFunctions functions;    // may be null
+    final String algorithm;     // key exchange algorithm
+    final AlgorithmParameterSpec keAlgParamSpec;
+    final AlgorithmParameters keAlgParams;
+    final boolean isAvailable;
+
+    // performance optimization
+    private static final Set<CryptoPrimitive> KEY_AGREEMENT_PRIMITIVE_SET =
+        Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT));
 
     // Constructor used for all NamedGroup types
-    private NamedGroup(int id, String name, String oid,
-            NamedGroupType namedGroupType,
-            ProtocolVersion[] supportedProtocols) {
+    private NamedGroup(int id, String name,
+            NamedGroupSpec namedGroupSpec,
+            ProtocolVersion[] supportedProtocols,
+            AlgorithmParameterSpec keAlgParamSpec) {
         this.id = id;
         this.name = name;
-        this.oid = oid;
-        this.type = namedGroupType;
+        this.spec = namedGroupSpec;
+        this.algorithm = namedGroupSpec.algorithm;
         this.supportedProtocols = supportedProtocols;
+        this.keAlgParamSpec = keAlgParamSpec;
 
-        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");
+        AlgorithmParameters algParams = null;
+        boolean mediator = (keAlgParamSpec != null);
+        if (mediator) {
+            try {
+                algParams =
+                    AlgorithmParameters.getInstance(namedGroupSpec.algorithm);
+                algParams.init(keAlgParamSpec);
+            } catch (InvalidParameterSpecException
+                    | NoSuchAlgorithmException exp) {
+                if (namedGroupSpec != NamedGroupSpec.NAMED_GROUP_XDH) {
+                    mediator = false;
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "No AlgorithmParameters for " + name, exp);
+                    }
+                } else {
+                    // HACK CODE
+                    //
+                    // Please remove the following code if the XDH/X25519/X448
+                    // AlgorithmParameters algorithms are supported in JDK.
+                    algParams = null;
+                    try {
+                        KeyAgreement.getInstance(name);
+
+                        // The following service is also needed.  But for
+                        // performance, check the KeyAgreement impl only.
+                        //
+                        // KeyFactory.getInstance(name);
+                        // KeyPairGenerator.getInstance(name);
+                        // AlgorithmParameters.getInstance(name);
+                    } catch (NoSuchAlgorithmException nsae) {
+                        mediator = false;
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.warning(
+                                "No AlgorithmParameters for " + name, nsae);
+                        }
+                    }
+                }
+            }
         }
+
+        this.isAvailable = mediator;
+        this.keAlgParams = mediator ? algParams : null;
     }
 
-    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) {
@@ -243,12 +309,11 @@
     }
 
     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;
+        for (NamedGroup ng : NamedGroup.values()) {
+            if (ng.spec == NamedGroupSpec.NAMED_GROUP_ECDHE) {
+                if ((params == ng.keAlgParamSpec) ||
+                        (ng.keAlgParamSpec == CurveDB.lookup(params))) {
+                    return ng;
                 }
             }
         }
@@ -258,23 +323,11 @@
 
     static NamedGroup valueOf(DHParameterSpec params) {
         for (NamedGroup ng : NamedGroup.values()) {
-            if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+            if (ng.spec != NamedGroupSpec.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;
-            }
-
+            DHParameterSpec ngParams = (DHParameterSpec)ng.keAlgParamSpec;
             if (ngParams.getP().equals(params.getP())
                     && ngParams.getG().equals(params.getG())) {
                 return ng;
@@ -304,118 +357,128 @@
         return "UNDEFINED-NAMED-GROUP(" + id + ")";
     }
 
-    // Are the NamedGroups available for the protocol desired?
-
+    // Is the NamedGroup available for the protocols desired?
     boolean isAvailable(List<ProtocolVersion> protocolVersions) {
-        for (ProtocolVersion pv : supportedProtocols) {
-            if (protocolVersions.contains(pv)) {
-                return true;
+        if (this.isAvailable) {
+            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;
+        if (this.isAvailable) {
+            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)))) {
+                    || (NamedGroupSpec.arrayContains(
+                            cs.keyExchange.groupTypes, spec)))) {
                 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);
+    boolean isPermitted(AlgorithmConstraints constraints) {
+        return constraints.permits(KEY_AGREEMENT_PRIMITIVE_SET,
+                        this.name, null) &&
+                constraints.permits(KEY_AGREEMENT_PRIMITIVE_SET,
+                        this.algorithm, this.keAlgParams);
     }
 
     byte[] encodePossessionPublicKey(
             NamedGroupPossession namedGroupPossession) {
-
-        Optional<NamedGroupFunctions> ngf = getFunctions();
-        if (ngf.isEmpty()) {
-            return null;
-        }
-        return ngf.get().encodePossessionPublicKey(namedGroupPossession);
+        return spec.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);
+        return spec.decodeCredentials(
+                this, encoded, constraints, onConstraintFail);
     }
 
     SSLPossession createPossession(SecureRandom random) {
+        return spec.createPossession(this, random);
+    }
 
-        Optional<NamedGroupFunctions> ngf = getFunctions();
-        if (ngf.isEmpty()) {
-            return null;
-        }
-        return ngf.get().createPossession(this, random);
+    SSLKeyDerivation createKeyDerivation(
+            HandshakeContext hc) throws IOException {
+        return spec.createKeyDerivation(hc);
+    }
+
+    interface ExceptionSupplier {
+        void apply(String s) throws SSLException;
     }
 
-    SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
-            throws IOException {
+    // A list of operations related to named groups.
+    private interface NamedGroupScheme {
+        default 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");
+            }
+        }
 
-        Optional<NamedGroupFunctions> ngf = getFunctions();
-        if (ngf.isEmpty()) {
-            return null;
-        }
-        return ngf.get().createKeyDerivation(hc);
+        byte[] encodePossessionPublicKey(
+                NamedGroupPossession namedGroupPossession);
 
+        SSLCredentials decodeCredentials(
+                NamedGroup ng, byte[] encoded,
+                AlgorithmConstraints constraints,
+                ExceptionSupplier onConstraintFail
+            ) throws IOException, GeneralSecurityException;
+
+        SSLPossession createPossession(NamedGroup ng, SecureRandom random);
+
+        SSLKeyDerivation createKeyDerivation(
+                HandshakeContext hc) throws IOException;
     }
 
-    boolean isAvailableGroup() {
-        Optional<NamedGroupFunctions> ngfOpt = getFunctions();
-        if (ngfOpt.isEmpty()) {
-            return false;
+    enum NamedGroupSpec implements NamedGroupScheme {
+        // Elliptic Curve Groups (ECDHE)
+        NAMED_GROUP_ECDHE("EC", ECDHEScheme.instance),
+
+        // Finite Field Groups (DHE)
+        NAMED_GROUP_FFDHE("DiffieHellman", FFDHEScheme.instance),
+
+        // Finite Field Groups (XDH)
+        NAMED_GROUP_XDH("XDH", XDHScheme.instance),
+
+        // arbitrary prime and curves (ECDHE)
+        NAMED_GROUP_ARBITRARY("EC", null),
+
+        // Not predefined named group
+        NAMED_GROUP_NONE("", null);
+
+        private final String algorithm;     // key exchange name
+        private final NamedGroupScheme scheme;  // named group operations
+
+        private NamedGroupSpec(String algorithm, NamedGroupScheme scheme) {
+            this.algorithm = algorithm;
+            this.scheme = scheme;
         }
-        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) {
@@ -428,92 +491,63 @@
             return false;
         }
 
-        static boolean arrayContains(NamedGroupType[] namedGroupTypes,
-                NamedGroupType namedGroupType) {
-            for (NamedGroupType ng : namedGroupTypes) {
+        static boolean arrayContains(NamedGroupSpec[] namedGroupTypes,
+                NamedGroupSpec namedGroupType) {
+            for (NamedGroupSpec ng : namedGroupTypes) {
                 if (ng == namedGroupType) {
                     return true;
                 }
             }
+
             return false;
         }
+
+        @Override
+        public byte[] encodePossessionPublicKey(
+                NamedGroupPossession namedGroupPossession) {
+            if (scheme != null) {
+                return scheme.encodePossessionPublicKey(namedGroupPossession);
+            }
+
+            return null;
+        }
+
+        @Override
+        public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
+                    AlgorithmConstraints constraints,
+                    ExceptionSupplier onConstraintFail
+                ) throws IOException, GeneralSecurityException {
+            if (scheme != null) {
+                return scheme.decodeCredentials(
+                        ng, encoded, constraints, onConstraintFail);
+            }
+
+            return null;
+        }
+
+        @Override
+        public SSLPossession createPossession(
+                NamedGroup ng, SecureRandom random) {
+            if (scheme != null) {
+                return scheme.createPossession(ng, random);
+            }
+
+            return null;
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext hc) throws IOException {
+            if (scheme != null) {
+                return scheme.createKeyDerivation(hc);
+            }
+
+            return null;
+        }
     }
 
-    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;
-        }
+    private static class FFDHEScheme implements NamedGroupScheme {
+        private static final FFDHEScheme instance = new FFDHEScheme();
 
         @Override
         public byte[] encodePossessionPublicKey(
@@ -524,8 +558,8 @@
         @Override
         public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
                 AlgorithmConstraints constraints,
-                ExceptionSupplier onConstraintFail)
-                throws IOException, GeneralSecurityException {
+                ExceptionSupplier onConstraintFail
+            ) throws IOException, GeneralSecurityException {
 
             DHKeyExchange.DHECredentials result
                     = DHKeyExchange.DHECredentials.valueOf(ng, encoded);
@@ -543,111 +577,15 @@
         }
 
         @Override
-        public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
-                throws IOException {
+        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;
-        }
+    private static class ECDHEScheme implements NamedGroupScheme {
+        private static final ECDHEScheme instance = new ECDHEScheme();
 
         @Override
         public byte[] encodePossessionPublicKey(
@@ -658,8 +596,8 @@
         @Override
         public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
                 AlgorithmConstraints constraints,
-                ExceptionSupplier onConstraintFail)
-                throws IOException, GeneralSecurityException {
+                ExceptionSupplier onConstraintFail
+            ) throws IOException, GeneralSecurityException {
 
             ECDHKeyExchange.ECDHECredentials result
                     = ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded);
@@ -677,52 +615,14 @@
         }
 
         @Override
-        public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
-                throws IOException {
-
+        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;
-        }
+    private static class XDHScheme implements NamedGroupScheme {
+        private static final XDHScheme instance = new XDHScheme();
 
         @Override
         public byte[] encodePossessionPublicKey(NamedGroupPossession poss) {
@@ -732,8 +632,8 @@
         @Override
         public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
                 AlgorithmConstraints constraints,
-                ExceptionSupplier onConstraintFail)
-                throws IOException, GeneralSecurityException {
+                ExceptionSupplier onConstraintFail
+            ) throws IOException, GeneralSecurityException {
 
             XDHKeyExchange.XDHECredentials result
                     = XDHKeyExchange.XDHECredentials.valueOf(ng, encoded);
@@ -751,31 +651,9 @@
         }
 
         @Override
-        public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
-                throws IOException {
+        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();
-        }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Mon Aug 12 21:36:29 2019 -0700
@@ -38,7 +38,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import sun.security.ssl.NamedGroup.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
 import sun.security.ssl.X509Authentication.X509Possession;
 import sun.security.util.KeyUtil;
@@ -149,7 +149,7 @@
     final String name;                  // literal name
     private final String algorithm;     // signature algorithm
     final String keyAlgorithm;          // signature key algorithm
-    private final AlgorithmParameterSpec signAlgParameter;
+    private final SigAlgParamSpec signAlgParams;    // signature parameters
     private final NamedGroup namedGroup;    // associated named group
 
     // The minimal required key size in bits.
@@ -184,21 +184,25 @@
         RSA_PSS_SHA384 ("SHA-384", 48),
         RSA_PSS_SHA512 ("SHA-512", 64);
 
-        final private AlgorithmParameterSpec parameterSpec;
-        final boolean isAvailable;
+        private final AlgorithmParameterSpec parameterSpec;
+        private final AlgorithmParameters parameters;
+        private final boolean isAvailable;
 
         SigAlgParamSpec(String hash, int saltLength) {
             // See RFC 8017
             PSSParameterSpec pssParamSpec =
                     new PSSParameterSpec(hash, "MGF1",
                             new MGF1ParameterSpec(hash), saltLength, 1);
+            AlgorithmParameters pssParams = null;
 
             boolean mediator = true;
             try {
                 Signature signer = Signature.getInstance("RSASSA-PSS");
                 signer.setParameter(pssParamSpec);
+                pssParams = signer.getParameters();
             } catch (InvalidAlgorithmParameterException |
-                    NoSuchAlgorithmException exp) {
+                    NoSuchAlgorithmException | RuntimeException exp) {
+                // Signature.getParameters() may throw RuntimeException.
                 mediator = false;
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.warning(
@@ -209,10 +213,7 @@
 
             this.isAvailable = mediator;
             this.parameterSpec = mediator ? pssParamSpec : null;
-        }
-
-        AlgorithmParameterSpec getParameterSpec() {
-            return parameterSpec;
+            this.parameters = mediator ? pssParams : null;
         }
     }
 
@@ -255,7 +256,7 @@
 
     private SignatureScheme(int id, String name,
             String algorithm, String keyAlgorithm,
-            SigAlgParamSpec signAlgParamSpec,
+            SigAlgParamSpec signAlgParams,
             NamedGroup namedGroup, int minimalKeySize,
             ProtocolVersion[] supportedProtocols,
             ProtocolVersion[] handshakeSupportedProtocols) {
@@ -263,8 +264,7 @@
         this.name = name;
         this.algorithm = algorithm;
         this.keyAlgorithm = keyAlgorithm;
-        this.signAlgParameter =
-            signAlgParamSpec != null ? signAlgParamSpec.parameterSpec : null;
+        this.signAlgParams = signAlgParams;
         this.namedGroup = namedGroup;
         this.minimalKeySize = minimalKeySize;
         this.supportedProtocols = Arrays.asList(supportedProtocols);
@@ -272,8 +272,8 @@
                 Arrays.asList(handshakeSupportedProtocols);
 
         boolean mediator = true;
-        if (signAlgParamSpec != null) {
-            mediator = signAlgParamSpec.isAvailable;
+        if (signAlgParams != null) {
+            mediator = signAlgParams.isAvailable;
         } else {
             try {
                 Signature.getInstance(algorithm);
@@ -331,6 +331,18 @@
         return 2;
     }
 
+    private boolean isPermitted(AlgorithmConstraints constraints) {
+        return constraints.permits(SIGNATURE_PRIMITIVE_SET,
+                        this.name, null) &&
+               constraints.permits(SIGNATURE_PRIMITIVE_SET,
+                        this.keyAlgorithm, null) &&
+               constraints.permits(SIGNATURE_PRIMITIVE_SET,
+                        this.algorithm, (signAlgParams != null ?
+                                signAlgParams.parameters : null)) &&
+               (namedGroup != null ?
+                        namedGroup.isPermitted(constraints) : true);
+    }
+
     // Get local supported algorithm collection complying to algorithm
     // constraints.
     static List<SignatureScheme> getSupportedAlgorithms(
@@ -351,8 +363,7 @@
             }
 
             if (isMatch) {
-                if (constraints.permits(
-                        SIGNATURE_PRIMITIVE_SET, ss.algorithm, null)) {
+                if (ss.isPermitted(constraints)) {
                     supported.add(ss);
                 } else if (SSLLogger.isOn &&
                         SSLLogger.isOn("ssl,handshake,verbose")) {
@@ -383,8 +394,7 @@
                 }
             } else if (ss.isAvailable &&
                     ss.supportedProtocols.contains(protocolVersion) &&
-                    constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                           ss.algorithm, null)) {
+                    ss.isPermitted(constraints)) {
                 supported.add(ss);
             } else {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -398,6 +408,7 @@
     }
 
     static SignatureScheme getPreferableAlgorithm(
+            AlgorithmConstraints constraints,
             List<SignatureScheme> schemes,
             SignatureScheme certScheme,
             ProtocolVersion version) {
@@ -405,8 +416,8 @@
         for (SignatureScheme ss : schemes) {
             if (ss.isAvailable &&
                     ss.handshakeSupportedProtocols.contains(version) &&
-                    certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
-
+                    certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) &&
+                    ss.isPermitted(constraints)) {
                 return ss;
             }
         }
@@ -415,6 +426,7 @@
     }
 
     static SignatureScheme getPreferableAlgorithm(
+            AlgorithmConstraints constraints,
             List<SignatureScheme> schemes,
             X509Possession x509Possession,
             ProtocolVersion version) {
@@ -432,9 +444,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)) {
+                    keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) &&
+                    ss.isPermitted(constraints)) {
+                if ((ss.namedGroup != null) && (ss.namedGroup.spec ==
+                        NamedGroupSpec.NAMED_GROUP_ECDHE)) {
                     ECParameterSpec params =
                             x509Possession.getECParameterSpec();
                     if (params != null &&
@@ -505,10 +518,13 @@
         Signature signer = Signature.getInstance(algorithm);
         if (key instanceof PublicKey) {
             SignatureUtil.initVerifyWithParam(signer, (PublicKey)key,
-                    signAlgParameter);
+                    (signAlgParams != null ?
+                            signAlgParams.parameterSpec : null));
         } else {
             SignatureUtil.initSignWithParam(signer, (PrivateKey)key,
-                    signAlgParameter, null);
+                    (signAlgParams != null ?
+                            signAlgParams.parameterSpec : null),
+                    null);
         }
 
         return signer;
--- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Mon Aug 12 21:36:29 2019 -0700
@@ -28,20 +28,15 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.security.AlgorithmConstraints;
-import java.security.AlgorithmParameters;
-import java.security.CryptoPrimitive;
-import java.security.spec.ECGenParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import javax.net.ssl.SSLProtocolException;
 import sun.security.action.GetPropertyAction;
-import sun.security.ssl.NamedGroup.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
 import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
 import sun.security.ssl.SSLExtension.ExtensionConsumer;
@@ -188,7 +183,7 @@
                     if (!group.isEmpty()) {
                         NamedGroup namedGroup = NamedGroup.nameOf(group);
                         if (namedGroup != null) {
-                            if (isAvailableGroup(namedGroup)) {
+                            if (namedGroup.isAvailable) {
                                 groupList.add(namedGroup);
                             }
                         }   // ignore unknown groups
@@ -235,7 +230,7 @@
 
                 groupList = new ArrayList<>(groups.length);
                 for (NamedGroup group : groups) {
-                    if (isAvailableGroup(group)) {
+                    if (group.isAvailable) {
                         groupList.add(group);
                     }
                 }
@@ -253,48 +248,19 @@
             }
         }
 
-        // check whether the group is supported by the underlying providers
-        private static boolean isAvailableGroup(NamedGroup namedGroup) {
-            return namedGroup.isAvailableGroup();
-        }
-
-        static ECGenParameterSpec getECGenParamSpec(NamedGroup ng) {
-            if (ng.type != NamedGroupType.NAMED_GROUP_ECDHE) {
-                 throw new RuntimeException(
-                         "Not a named EC group: " + ng);
-            }
-
-            // parameters are non-null
-            AlgorithmParameters params = ng.getParameters();
-            try {
-                return params.getParameterSpec(ECGenParameterSpec.class);
-            } catch (InvalidParameterSpecException ipse) {
-                // should be unlikely
-                return new ECGenParameterSpec(ng.oid);
-            }
-        }
-
-        static AlgorithmParameters getParameters(NamedGroup ng) {
-            return ng.getParameters();
-        }
-
         // Is there any supported group permitted by the constraints?
         static boolean isActivatable(
-                AlgorithmConstraints constraints, NamedGroupType type) {
+                AlgorithmConstraints constraints, NamedGroupSpec type) {
 
             boolean hasFFDHEGroups = false;
             for (NamedGroup namedGroup : supportedNamedGroups) {
-                if (namedGroup.type == type) {
-                    if (constraints.permits(
-                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                            namedGroup.algorithm,
-                            getParameters(namedGroup))) {
-
+                if (namedGroup.isAvailable && namedGroup.spec == type) {
+                    if (namedGroup.isPermitted(constraints)) {
                         return true;
                     }
 
                     if (!hasFFDHEGroups &&
-                            (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                            (type == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
                         hasFFDHEGroups = true;
                     }
                 }
@@ -306,20 +272,17 @@
             //
             // Note that the constraints checking on DHE parameters will be
             // performed during key exchanging in a handshake.
-            return !hasFFDHEGroups && type == NamedGroupType.NAMED_GROUP_FFDHE;
+            return !hasFFDHEGroups && type == NamedGroupSpec.NAMED_GROUP_FFDHE;
         }
 
         // Is the named group permitted by the constraints?
         static boolean isActivatable(
                 AlgorithmConstraints constraints, NamedGroup namedGroup) {
-            if (!isSupported(namedGroup)) {
+            if (!namedGroup.isAvailable || !isSupported(namedGroup)) {
                 return false;
             }
 
-            return constraints.permits(
-                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                            namedGroup.algorithm,
-                            getParameters(namedGroup));
+            return namedGroup.isPermitted(constraints);
         }
 
         // Is the named group supported?
@@ -335,16 +298,13 @@
 
         static NamedGroup getPreferredGroup(
                 ProtocolVersion negotiatedProtocol,
-                AlgorithmConstraints constraints, NamedGroupType[] types,
+                AlgorithmConstraints constraints, NamedGroupSpec[] types,
                 List<NamedGroup> requestedNamedGroups) {
             for (NamedGroup namedGroup : requestedNamedGroups) {
-                if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
+                if ((NamedGroupSpec.arrayContains(types, namedGroup.spec)) &&
                         namedGroup.isAvailable(negotiatedProtocol) &&
                         isSupported(namedGroup) &&
-                        constraints.permits(
-                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                namedGroup.algorithm,
-                                getParameters(namedGroup))) {
+                        namedGroup.isPermitted(constraints)) {
                     return namedGroup;
                 }
             }
@@ -354,14 +314,11 @@
 
         static NamedGroup getPreferredGroup(
                 ProtocolVersion negotiatedProtocol,
-                AlgorithmConstraints constraints, NamedGroupType[] types) {
+                AlgorithmConstraints constraints, NamedGroupSpec[] types) {
             for (NamedGroup namedGroup : supportedNamedGroups) {
-                if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
+                if ((NamedGroupSpec.arrayContains(types, namedGroup.spec)) &&
                         namedGroup.isAvailable(negotiatedProtocol) &&
-                        constraints.permits(
-                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                                namedGroup.algorithm,
-                                getParameters(namedGroup))) {
+                        namedGroup.isPermitted(constraints)) {
                     return namedGroup;
                 }
             }
@@ -401,15 +358,13 @@
                 new ArrayList<>(SupportedGroups.supportedNamedGroups.length);
             for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
                 if ((!SupportedGroups.enableFFDHE) &&
-                    (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                    (ng.spec == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
                     continue;
                 }
 
                 if (ng.isAvailable(chc.activeProtocols) &&
                         ng.isSupported(chc.activeCipherSuites) &&
-                        chc.algorithmConstraints.permits(
-                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                            ng.algorithm, getParameters(ng))) {
+                        ng.isPermitted(chc.algorithmConstraints)) {
                     namedGroups.add(ng);
                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.fine(
@@ -528,15 +483,13 @@
                     SupportedGroups.supportedNamedGroups.length);
             for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
                 if ((!SupportedGroups.enableFFDHE) &&
-                    (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                    (ng.spec == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
                     continue;
                 }
 
                 if (ng.isAvailable(shc.activeProtocols) &&
                         ng.isSupported(shc.activeCipherSuites) &&
-                        shc.algorithmConstraints.permits(
-                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                            ng.algorithm, getParameters(ng))) {
+                        ng.isPermitted(shc.algorithmConstraints)) {
                     namedGroups.add(ng);
                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.fine(
--- a/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java	Mon Aug 12 21:36:29 2019 -0700
@@ -29,7 +29,7 @@
 import java.security.*;
 import java.security.interfaces.XECPublicKey;
 import java.security.spec.*;
-import sun.security.ssl.NamedGroup.NamedGroupType;
+import sun.security.ssl.NamedGroup.NamedGroupSpec;
 import sun.security.util.*;
 
 /**
@@ -68,7 +68,7 @@
                 byte[] encodedPoint) throws IOException,
                 GeneralSecurityException {
 
-            if (namedGroup.type != NamedGroupType.NAMED_GROUP_XDH) {
+            if (namedGroup.spec != NamedGroupSpec.NAMED_GROUP_XDH) {
                 throw new RuntimeException(
                         "Credentials decoding:  Not XDH named group");
             }
@@ -101,8 +101,7 @@
             try {
                 KeyPairGenerator kpg
                         = KeyPairGenerator.getInstance(namedGroup.algorithm);
-                AlgorithmParameterSpec params = namedGroup.getParameterSpec();
-                kpg.initialize(params, random);
+                kpg.initialize(namedGroup.keAlgParamSpec, random);
                 KeyPair kp = kpg.generateKeyPair();
                 privateKey = kp.getPrivate();
                 publicKey = (XECPublicKey) kp.getPublic();
--- a/src/java.base/share/classes/sun/security/util/CurveDB.java	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/classes/sun/security/util/CurveDB.java	Mon Aug 12 21:36:29 2019 -0700
@@ -62,7 +62,7 @@
     }
 
     // Return a NamedCurve for the specified OID/name or null if unknown.
-    static NamedCurve lookup(String name) {
+    public static NamedCurve lookup(String name) {
         NamedCurve spec = oidMap.get(name);
         if (spec != null) {
             return spec;
@@ -83,7 +83,7 @@
 
     // Convert the given ECParameterSpec object to a NamedCurve object.
     // If params does not represent a known named curve, return null.
-    static NamedCurve lookup(ECParameterSpec params) {
+    public static NamedCurve lookup(ECParameterSpec params) {
         if ((params instanceof NamedCurve) || (params == null)) {
             return (NamedCurve)params;
         }
--- a/src/java.base/share/conf/security/java.security	Mon Aug 12 23:37:48 2019 +0200
+++ b/src/java.base/share/conf/security/java.security	Mon Aug 12 21:36:29 2019 -0700
@@ -541,9 +541,9 @@
 #       usage [TLSServer] [TLSClient] [SignedJAR]
 #
 # The "AlgorithmName" is the standard algorithm name of the disabled
-# algorithm. See "Java Cryptography Architecture Standard Algorithm Name
-# Documentation" for information about Standard Algorithm Names.  Matching
-# is performed using a case-insensitive sub-element matching rule.  (For
+# algorithm. See the Java Security Standard Algorithm Names Specification
+# for information about Standard Algorithm Names.  Matching is
+# performed using a case-insensitive sub-element matching rule.  (For
 # example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and
 # "ECDSA" for signatures.)  If the assertion "AlgorithmName" is a
 # sub-element of the certificate algorithm name, the algorithm will be
@@ -677,8 +677,9 @@
 # In some environments, certain algorithms or key lengths may be undesirable
 # when using SSL/TLS/DTLS.  This section describes the mechanism for disabling
 # algorithms during SSL/TLS/DTLS security parameters negotiation, including
-# protocol version negotiation, cipher suites selection, peer authentication
-# and key exchange mechanisms.
+# protocol version negotiation, cipher suites selection, named groups
+# selection, signature schemes selection, peer authentication and key
+# exchange mechanisms.
 #
 # Disabled algorithms will not be negotiated for SSL/TLS connections, even
 # if they are enabled explicitly in an application.
@@ -699,7 +700,8 @@
 # It is not guaranteed to be examined and used by other implementations.
 #
 # Example:
-#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
+#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048, \
+#       rsa_pkcs1_sha1, secp224r1
 jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \
     EC keySize < 224, 3DES_EDE_CBC, anon, NULL
 
@@ -743,8 +745,8 @@
 #     3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC
 #     4. JSSE message digest algorithm name, e.g., SHA
 #
-# See SSL/TLS specifications and "Java Cryptography Architecture Standard
-# Algorithm Name Documentation" for information about the algorithm names.
+# See SSL/TLS specifications and the Java Security Standard Algorithm Names
+# Specification for information about the algorithm names.
 #
 # Note: If a legacy algorithm is also restricted through the
 # jdk.tls.disabledAlgorithms property or the
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java	Mon Aug 12 21:36:29 2019 -0700
@@ -0,0 +1,105 @@
+/*
+ * 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 8226374
+ * @library /javax/net/ssl/templates
+ * @summary Restrict signature algorithms and named groups
+ * @run main/othervm RestrictNamedGroup x25519
+ * @run main/othervm RestrictNamedGroup x448
+ * @run main/othervm RestrictNamedGroup secp256r1
+ * @run main/othervm RestrictNamedGroup secp384r1
+ * @run main/othervm RestrictNamedGroup secp521r1
+ * @run main/othervm RestrictNamedGroup ffdhe2048
+ * @run main/othervm RestrictNamedGroup ffdhe3072
+ * @run main/othervm RestrictNamedGroup ffdhe4096
+ * @run main/othervm RestrictNamedGroup ffdhe6144
+ * @run main/othervm RestrictNamedGroup ffdhe8192
+ */
+
+import java.security.Security;
+import java.util.Arrays;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLException;
+
+public class RestrictNamedGroup extends SSLSocketTemplate {
+
+    private static volatile int index;
+    private static final String[][][] protocols = {
+        {{"TLSv1.3"}, {"TLSv1.3"}},
+        {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}},
+        {{"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);
+        socket.setEnabledCipherSuites(new String[] {
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
+    }
+
+    @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);
+        serverSocket.setEnabledCipherSuites(new String[] {
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
+    }
+
+    /*
+     * Run the test case.
+     */
+    public static void main(String[] args) throws Exception {
+        Security.setProperty("jdk.tls.disabledAlgorithms", args[0]);
+        System.setProperty("jdk.tls.namedGroups", args[0]);
+
+        for (index = 0; index < protocols.length; index++) {
+            try {
+                (new RestrictNamedGroup()).run();
+            } catch (SSLException | IllegalStateException ssle) {
+                // The named group should be restricted.
+                continue;
+            }
+
+            throw new Exception("The test case should be disabled");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/CipherSuite/RestrictSignatureScheme.java	Mon Aug 12 21:36:29 2019 -0700
@@ -0,0 +1,232 @@
+/*
+ * 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 8226374
+ * @library /javax/net/ssl/templates
+ * @summary Restrict signature algorithms and named groups
+ * @run main/othervm RestrictSignatureScheme
+ */
+import java.io.ByteArrayInputStream;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Base64;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.SSLException;
+
+public class RestrictSignatureScheme extends SSLSocketTemplate {
+
+    private static volatile int index;
+    private static final String[][][] protocols = {
+        {{"TLSv1.3"}, {"TLSv1.3"}},
+        {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}},
+        {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}},
+        {{"TLSv1.2"}, {"TLSv1.3", "TLSv1.2"}},
+        {{"TLSv1.2"}, {"TLSv1.2"}}
+    };
+
+    private final SSLContext context;
+    RestrictSignatureScheme() throws Exception {
+        this.context = createSSLContext();
+    }
+
+    @Override
+    protected SSLContext createClientSSLContext() throws Exception {
+        return context;
+    }
+
+    @Override
+    protected SSLContext createServerSSLContext() throws Exception {
+        return context;
+    }
+
+    // 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);
+        socket.setEnabledCipherSuites(new String[] {
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
+    }
+
+    @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);
+        serverSocket.setEnabledCipherSuites(new String[] {
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
+    }
+
+    /*
+     * Run the test case.
+     */
+    public static void main(String[] args) throws Exception {
+        Security.setProperty("jdk.tls.disabledAlgorithms", "RSASSA-PSS");
+
+        for (index = 0; index < protocols.length; index++) {
+            try {
+                (new RestrictSignatureScheme()).run();
+            } catch (SSLException | IllegalStateException ssle) {
+                // The named group should be restricted.
+                continue;
+            }
+
+            throw new Exception("The test case should be disabled");
+        }
+    }
+
+
+    private static final String trustedCertStr =
+        /**
+        * Signature Algorithm: rsassaPss
+        * Issuer: CN = localhost
+        * Validity Not Before: Jun 6 07:11:00 2018 GMT
+        * Not After : Jun 1 07:11:00 2038 GMT
+        * Subject: CN = localhost
+        * Public Key Algorithm: rsassaPss
+        */
+       "-----BEGIN CERTIFICATE-----\n"
+       + "MIIDZjCCAh2gAwIBAgIUHxwPs3eAgJ057nJwiLgWZWeNqdgwPgYJKoZIhvcNAQEK\n"
+       + "MDGgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQC\n"
+       + "AgDeMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xODA2MDYwNzExMDBaFw0zODA2\n"
+       + "MDEwNzExMDBaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASAwCwYJKoZIhvcNAQEK\n"
+       + "A4IBDwAwggEKAoIBAQCl8r4Qrg27BYUO/1Va2Ix8QPGzN/lvzmKvP5Ff26ovNW4v\n"
+       + "RUx68HzAhhiWtcl+PwLSbJqJreEkTlle7PnRAypby3fO7ZAK0Y3YiHquaBg7d+7Y\n"
+       + "FhhHwv8gG0lZcyA0BkXFJHqdq76qar0xHC6DVezXm0K3mcceymGtFR9BzWmAj+7D\n"
+       + "YsSwvtTQ7WNoQmf0cdDMSM71IwaTwIwvT2wzX1vv5hcdDyXdr64WFqWSA9sNJ2K6\n"
+       + "arxaaU1klwKSgDokF6njafWQ4UxdR67d5W1MYoiioDs2Yy3utsMpO2OUzZVBZNdT\n"
+       + "gkr1jsJhIurpz/5K51lwJIRQBezEFSb+60AFVoMJAgMBAAGjUDBOMB0GA1UdDgQW\n"
+       + "BBQfFit5ilWJmZgCX4QY0HsaI9iIDDAfBgNVHSMEGDAWgBQfFit5ilWJmZgCX4QY\n"
+       + "0HsaI9iIDDAMBgNVHRMEBTADAQH/MD4GCSqGSIb3DQEBCjAxoA0wCwYJYIZIAWUD\n"
+       + "BAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQCAaIEAgIA3gOCAQEAa4yUQ3gh\n"
+       + "d1YWPdEa1sv2hdkhtenw6m5yxbmaQl2+nIKSpk4RfpXC7K1EYwBF8TdfFbD8hGGh\n"
+       + "5n81BT0/dn1R9SRGCv7KTxx4lfQt31frlsw/tVciwyXQtcUZ6DqfnLP0/aRVLNgx\n"
+       + "zaP542JUHFYLTC3EGz2zUgv70ZUTlIsPG3/p8YO1iXdnYGQyzOuQPUBpI7nS7UtR\n"
+       + "Ug8VE9ACpBxxI3qChMahFZGHlXCCSjSmxpQa6UO4SQl8q5tPNnqdzWwvAW8qkCy4\n"
+       + "6barRQ4sMcGayhHh/uSTx7bcl0FMJpcI1ygbw7/Pc03zKtw0gMTBMns7q4yXjb/u\n"
+       + "ef47nW0t+LRAAg==\n"
+       + "-----END CERTIFICATE-----\n";
+
+    private static final String keyCertStr = trustedCertStr;
+
+    private static final String privateKey =
+        "MIIEuwIBADALBgkqhkiG9w0BAQoEggSnMIIEowIBAAKCAQEApfK+EK4NuwWFDv9V\n"
+        + "WtiMfEDxszf5b85irz+RX9uqLzVuL0VMevB8wIYYlrXJfj8C0myaia3hJE5ZXuz5\n"
+        + "0QMqW8t3zu2QCtGN2Ih6rmgYO3fu2BYYR8L/IBtJWXMgNAZFxSR6nau+qmq9MRwu\n"
+        + "g1Xs15tCt5nHHsphrRUfQc1pgI/uw2LEsL7U0O1jaEJn9HHQzEjO9SMGk8CML09s\n"
+        + "M19b7+YXHQ8l3a+uFhalkgPbDSdiumq8WmlNZJcCkoA6JBep42n1kOFMXUeu3eVt\n"
+        + "TGKIoqA7NmMt7rbDKTtjlM2VQWTXU4JK9Y7CYSLq6c/+SudZcCSEUAXsxBUm/utA\n"
+        + "BVaDCQIDAQABAoIBAAc4vRS0vlw5LUUtz2UYr2Ro3xvRf8Vh0eGWfpkRUiKjzJu6\n"
+        + "BE4FUSh/rWpBlvcrfs/xcfgz3OxbjIAZB/YUkS9Vd21F4VLXM7kMl2onlYZg/b/h\n"
+        + "lkTpM3kONu7xl6Er9LVTlRJveuinpHwSoeONRbVMSGb9BjFM1VtW4/lVGxZBG05D\n"
+        + "y9i/o4vCZqULn9cAumOwicKuCyTcS58XcMJ+puSPfRA71PYLxqFkASAoJsUwCXpo\n"
+        + "gs39lLsIFgrfO8mBO1ux/SE+QaRc+9XqFSHHKD1XqF/9zSYBgWjE910EcpdYEdZx\n"
+        + "GEkwea7Fn4brO5OpIrHY/45naqbUOBzv6gufMAECgYEAz7PHCdcrQvmOb8EiNbQH\n"
+        + "uvSimwObWJFeN1ykp6mfRbSnkXw7p8+M4Tc8HFi8QLpoq63Ev2AwoaQCQvHbFC2Y\n"
+        + "1Cz0EkC0aOp+tZP7U2AUBdkcDesZAJQTad0zV6KesyIUXdxZXDG8JJ1XSNWfTJV4\n"
+        + "QD+BjLZ0jiAyCIfVYvWQqYkCgYEAzIln1nKTixLMPr5CldSmR7ZarEtPJU+hHwVg\n"
+        + "dV/Lc6d2Yy9JgunOXRo4BXB1TEo8JFbK3HBQH6tS8li4qDr7WK5wyYfh8qb4WZyu\n"
+        + "lc562f2WVYntcN8/Ojb+Vyrt7lk9sq/8KoVHxEAWd6mqL9VTPYuAu1Vw9fTGIZfB\n"
+        + "lDeELYECgYAvdzU4UXzofGGJtohb332YwwlaBZP9xJLUcg6K5l+orWVSASMc8XiP\n"
+        + "i3DoRXsYC8GZ4kdBOPlEJ1gA9oaLcPQpIPDSLwlLpLM6Scw4vI822uvnXl/DWxOo\n"
+        + "sM1n7Jj59QLUhGPDhvYpI+/rjC4wcUQe4qR3hMbUKBVnD6u7RsU9iQKBgQCQ17VK\n"
+        + "7bSCRfuRaxaoGADww7gOTv5rQ6qr1xjpxb7D1hFGR9Rc+smCsPB/GZZXQjK44SWj\n"
+        + "WX3ED4Ubzaxmpe4cbNu+O5XMSmWQwB36RFBHUwdE5/nXdqDFzu/qNqJrqZLBmVKP\n"
+        + "ofaiiWffsaytVvotmT6+atElvAMbAua42V+nAQKBgHtIn3mYMHLriYGhQzpkFEA2\n"
+        + "8YcAMlKppueOMAKVy8nLu2r3MidmLAhMiKJQKG45I3Yg0/t/25tXLiOPJlwrOebh\n"
+        + "xQqUBI/JUOIpGAEnr48jhOXnCS+i+z294G5U/RgjXrlR4bCPvrtCmwzWwe0h79w2\n"
+        + "Q2hO5ZTW6UD9CVA85whf";
+
+    private static SSLContext createSSLContext() throws Exception {
+        // Generate certificate from cert string
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        // Create a key store
+        KeyStore ts = KeyStore.getInstance("PKCS12");
+        KeyStore ks = KeyStore.getInstance("PKCS12");
+        ts.load(null, null);
+        ks.load(null, null);
+        char passphrase[] = "passphrase".toCharArray();
+
+        // Import the trusted cert
+        ts.setCertificateEntry("trusted-cert-RSASSA-PSS",
+                cf.generateCertificate(new ByteArrayInputStream(
+                        trustedCertStr.getBytes())));
+
+        boolean hasKeyMaterials = keyCertStr != null && privateKey != null;
+        if (hasKeyMaterials) {
+
+            // Generate the private key.
+            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
+                    Base64.getMimeDecoder().decode(privateKey));
+            KeyFactory kf = KeyFactory.getInstance("RSASSA-PSS");
+            PrivateKey priKey = kf.generatePrivate(priKeySpec);
+
+            // Generate certificate chain
+            Certificate keyCert = cf.generateCertificate(
+                    new ByteArrayInputStream(keyCertStr.getBytes()));
+            Certificate[] chain = new Certificate[]{keyCert};
+
+            // Import the key entry.
+            ks.setKeyEntry("cert-RSASSA-PSS", priKey, passphrase, chain);
+        }
+
+        // Create SSL context
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+        tmf.init(ts);
+
+        SSLContext context = SSLContext.getInstance("TLS");
+        if (hasKeyMaterials) {
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
+            kmf.init(ks, passphrase);
+            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+        } else {
+            context.init(null, tmf.getTrustManagers(), null);
+        }
+
+        return context;
+    }
+}