/* * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ssl; import java.io.IOException; import java.security.*; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.spec.NamedParameterSpec; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.ArrayList; import java.util.Objects; import java.util.Set; import javax.crypto.KeyAgreement; import javax.crypto.spec.DHParameterSpec; import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; import sun.security.util.CurveDB; /** * An enum containing all known named groups for use in TLS. * * The enum also contains the required properties of each group and the * required functions (e.g. encoding/decoding). */ enum NamedGroup { // Elliptic Curves (RFC 4492) // // See sun.security.util.CurveDB for the OIDs // NIST K-163 SECT163_K1(0x0001, "sect163k1", 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", 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", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_12, CurveDB.lookup("sect233k1")), // NIST B-233 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", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_12, CurveDB.lookup("sect283k1")), // NIST B-283 SECT283_R1(0x000A, "sect283r1", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_12, CurveDB.lookup("sect283r1")), // NIST K-409 SECT409_K1(0x000B, "sect409k1", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_12, CurveDB.lookup("sect409k1")), // NIST B-409 SECT409_R1(0x000C, "sect409r1", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_12, CurveDB.lookup("sect409r1")), // NIST K-571 SECT571_K1(0x000D, "sect571k1", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_12, CurveDB.lookup("sect571k1")), // NIST B-571 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", 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", 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", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_13, CurveDB.lookup("secp256r1")), // NIST P-384 SECP384_R1(0x0018, "secp384r1", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_13, CurveDB.lookup("secp384r1")), // NIST P-521 SECP521_R1(0x0019, "secp521r1", NamedGroupSpec.NAMED_GROUP_ECDHE, ProtocolVersion.PROTOCOLS_TO_13, CurveDB.lookup("secp521r1")), // x25519 and x448 (RFC 8422/8446) 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", 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)), ML_KEM_512(0x0200, "MLKEM512", NamedGroupSpec.NAMED_GROUP_KEM, ProtocolVersion.PROTOCOLS_OF_13, null), ML_KEM_768(0x0201, "MLKEM768", NamedGroupSpec.NAMED_GROUP_KEM, ProtocolVersion.PROTOCOLS_OF_13, null), ML_KEM_1024(0x0202, "MLKEM1024", NamedGroupSpec.NAMED_GROUP_KEM, ProtocolVersion.PROTOCOLS_OF_13, null), X25519MLKEM768(0x11ec, "X25519MLKEM768", NamedGroupSpec.NAMED_GROUP_KEM, ProtocolVersion.PROTOCOLS_OF_13, Hybrid.X25519_MLKEM768, HybridProvider.PROVIDER), SECP256R1MLKEM768(0x11eb, "SecP256r1MLKEM768", NamedGroupSpec.NAMED_GROUP_KEM, ProtocolVersion.PROTOCOLS_OF_13, Hybrid.SECP256R1_MLKEM768, HybridProvider.PROVIDER), SECP384R1MLKEM1024(0x11ed, "SecP384r1MLKEM1024", NamedGroupSpec.NAMED_GROUP_KEM, ProtocolVersion.PROTOCOLS_OF_13, Hybrid.SECP384R1_MLKEM1024, HybridProvider.PROVIDER), // Elliptic Curves (RFC 4492) // // arbitrary prime and characteristic-2 curves 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 String name; // literal name final NamedGroupSpec spec; // group type final ProtocolVersion[] supportedProtocols; final String algorithm; // key exchange algorithm final AlgorithmParameterSpec keAlgParamSpec; final AlgorithmParameters keAlgParams; final boolean isAvailable; final Provider defaultProvider; // performance optimization private static final Set KEY_AGREEMENT_PRIMITIVE_SET = Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT)); NamedGroup(int id, String name, NamedGroupSpec namedGroupSpec, ProtocolVersion[] supportedProtocols, AlgorithmParameterSpec keAlgParamSpec) { this(id, name, namedGroupSpec, supportedProtocols, keAlgParamSpec, null); } // Constructor used for all NamedGroup types NamedGroup(int id, String name, NamedGroupSpec namedGroupSpec, ProtocolVersion[] supportedProtocols, AlgorithmParameterSpec keAlgParamSpec, Provider defaultProvider) { this.id = id; this.name = name; this.spec = namedGroupSpec; this.algorithm = namedGroupSpec.algorithm; this.supportedProtocols = supportedProtocols; this.keAlgParamSpec = keAlgParamSpec; this.defaultProvider = defaultProvider; // Check if it is a supported named group. AlgorithmParameters algParams = null; boolean mediator = (keAlgParamSpec != null); // An EC provider, for example the SunEC provider, may support // AlgorithmParameters but not KeyPairGenerator or KeyAgreement. // // Note: Please be careful if removing this block! if (mediator && (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_ECDHE)) { mediator = JsseJce.isEcAvailable(); } // Check the specific algorithm parameters. if (mediator) { try { // Skip AlgorithmParameters for KEMs (not supported) // Check KEM's availability via KeyFactory if (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_KEM) { if (defaultProvider == null) { KeyFactory.getInstance(name); } else { KeyFactory.getInstance(name, defaultProvider); } } else { // ECDHE or others: use AlgorithmParameters as before 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(SSLLogger.Opt.HANDSHAKE)) { SSLLogger.warning( "No AlgorithmParameters or KeyFactory for " + name, exp); } } else { // Please remove the following code if the XDH/X25519/X448 // AlgorithmParameters algorithms are supported in JDK. // // Note: Please be careful if removing this block! 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(SSLLogger.Opt.HANDSHAKE)) { SSLLogger.warning( "No AlgorithmParameters for " + name, nsae); } } } } } this.isAvailable = mediator; this.keAlgParams = mediator ? algParams : null; } Provider getProvider() { return defaultProvider; } // // The next set of methods search & retrieve NamedGroups. // static NamedGroup valueOf(int id) { for (NamedGroup group : NamedGroup.values()) { if (group.id == id) { return group; } } return null; } static NamedGroup valueOf(ECParameterSpec params) { for (NamedGroup ng : NamedGroup.values()) { if (ng.spec == NamedGroupSpec.NAMED_GROUP_ECDHE) { if ((params == ng.keAlgParamSpec) || (ng.keAlgParamSpec == CurveDB.lookup(params))) { return ng; } } } return null; } static NamedGroup valueOf(DHParameterSpec params) { for (NamedGroup ng : NamedGroup.values()) { if (ng.spec != NamedGroupSpec.NAMED_GROUP_FFDHE) { continue; } DHParameterSpec ngParams = (DHParameterSpec)ng.keAlgParamSpec; if (ngParams.getP().equals(params.getP()) && ngParams.getG().equals(params.getG())) { return ng; } } return null; } static NamedGroup nameOf(String name) { for (NamedGroup group : NamedGroup.values()) { if (group.name.equalsIgnoreCase(name)) { return group; } } return null; } static String nameOf(int id) { for (NamedGroup group : NamedGroup.values()) { if (group.id == id) { return group.name; } } return "UNDEFINED-NAMED-GROUP(" + id + ")"; } public static List namesOf(String[] namedGroups) { if (namedGroups == null) { return null; } if (namedGroups.length == 0) { return List.of(); } List ngs = new ArrayList<>(namedGroups.length); for (String ss : namedGroups) { NamedGroup ng = NamedGroup.nameOf(ss); if (ng == null || !ng.isAvailable) { if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE_VERBOSE)) { SSLLogger.finest( "Ignore the named group (" + ss + "), unsupported or unavailable"); } continue; } ngs.add(ng); } return Collections.unmodifiableList(ngs); } // Is there any supported group permitted by the constraints? static boolean isActivatable(SSLConfiguration sslConfig, AlgorithmConstraints constraints, NamedGroupSpec type) { boolean hasFFDHEGroups = false; for (NamedGroup namedGroup : SupportedGroups.getGroupsFromConfig(sslConfig)) { if (namedGroup.isAvailable && namedGroup.spec == type) { if (namedGroup.isPermitted(constraints)) { return true; } if (!hasFFDHEGroups && (type == NamedGroupSpec.NAMED_GROUP_FFDHE)) { hasFFDHEGroups = true; } } } // For compatibility, if no FFDHE groups are defined, the non-FFDHE // compatible mode (using DHE cipher suite without FFDHE extension) // is allowed. // // Note that the constraints checking on DHE parameters will be // performed during key exchanging in a handshake. return !hasFFDHEGroups && type == NamedGroupSpec.NAMED_GROUP_FFDHE; } // Is the named group permitted by the constraints? static boolean isActivatable( SSLConfiguration sslConfig, AlgorithmConstraints constraints, NamedGroup namedGroup) { if (!namedGroup.isAvailable || !isEnabled(sslConfig, namedGroup)) { return false; } return namedGroup.isPermitted(constraints); } // Is the named group supported? static boolean isEnabled(SSLConfiguration sslConfig, NamedGroup namedGroup) { for (NamedGroup ng : SupportedGroups.getGroupsFromConfig(sslConfig)) { if (namedGroup.equals(ng)) { return true; } } return false; } // Get preferred named group from the configured named groups for the // negotiated protocol and named group types. static NamedGroup getPreferredGroup( SSLConfiguration sslConfig, ProtocolVersion negotiatedProtocol, AlgorithmConstraints constraints, NamedGroupSpec[] types) { for (NamedGroup ng : SupportedGroups.getGroupsFromConfig(sslConfig)) { if (ng.isAvailable && NamedGroupSpec.arrayContains(types, ng.spec) && ng.isAvailable(negotiatedProtocol) && ng.isPermitted(constraints)) { return ng; } } return null; } // Get preferred named group from the requested and configured named // groups for the negotiated protocol and named group types. static NamedGroup getPreferredGroup( SSLConfiguration sslConfig, ProtocolVersion negotiatedProtocol, AlgorithmConstraints constraints, NamedGroupSpec[] types, List requestedNamedGroups) { for (NamedGroup namedGroup : requestedNamedGroups) { if ((namedGroup.isAvailable && NamedGroupSpec.arrayContains(types, namedGroup.spec)) && namedGroup.isAvailable(negotiatedProtocol) && isEnabled(sslConfig, namedGroup) && namedGroup.isPermitted(constraints)) { return namedGroup; } } return null; } // Is the NamedGroup available for the protocols desired? boolean isAvailable(List protocolVersions) { if (this.isAvailable) { for (ProtocolVersion pv : supportedProtocols) { if (protocolVersions.contains(pv)) { return true; } } } return false; } boolean isAvailable(ProtocolVersion protocolVersion) { 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 cipherSuites) { for (CipherSuite cs : cipherSuites) { boolean isMatch = isAvailable(cs.supportedProtocols); if (isMatch && ((cs.keyExchange == null) || (NamedGroupSpec.arrayContains( cs.keyExchange.groupTypes, spec)))) { return true; } } return false; } 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) { return spec.encodePossessionPublicKey(namedGroupPossession); } SSLCredentials decodeCredentials( byte[] encoded) throws IOException, GeneralSecurityException { return spec.decodeCredentials(this, encoded); } SSLPossession createPossession(boolean isClient, SecureRandom random) { return spec.createPossession(this, isClient, random); } SSLPossession createPossession(SecureRandom random) { return spec.createPossession(this, random); } SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { return spec.createKeyDerivation(hc); } // A list of operations related to named groups. private interface NamedGroupScheme { byte[] encodePossessionPublicKey( NamedGroupPossession namedGroupPossession); SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded) throws IOException, GeneralSecurityException; SSLPossession createPossession(NamedGroup ng, SecureRandom random); SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException; default SSLPossession createPossession(NamedGroup ng, boolean isClient, SecureRandom random) { return createPossession(ng, random); } } 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), // Post-Quantum Cryptography (PQC) KEM groups // Currently used for hybrid named groups NAMED_GROUP_KEM("KEM", KEMScheme.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 NamedGroupSpec(String algorithm, NamedGroupScheme scheme) { this.algorithm = algorithm; this.scheme = scheme; } boolean isSupported(List cipherSuites) { for (CipherSuite cs : cipherSuites) { if (cs.keyExchange == null || arrayContains(cs.keyExchange.groupTypes, this)) { return true; } } return false; } 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) throws IOException, GeneralSecurityException { if (scheme != null) { return scheme.decodeCredentials(ng, encoded); } return null; } public SSLPossession createPossession( NamedGroup ng, boolean isClient, SecureRandom random) { if (scheme != null) { return scheme.createPossession(ng, isClient, random); } 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; } } private static class FFDHEScheme implements NamedGroupScheme { private static final FFDHEScheme instance = new FFDHEScheme(); @Override public byte[] encodePossessionPublicKey( NamedGroupPossession namedGroupPossession) { return namedGroupPossession.encode(); } @Override public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded) throws IOException, GeneralSecurityException { return DHKeyExchange.DHECredentials.valueOf(ng, encoded); } @Override public SSLPossession createPossession( NamedGroup ng, SecureRandom random) { return new DHKeyExchange.DHEPossession(ng, random); } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { return DHKeyExchange.kaGenerator.createKeyDerivation(hc); } } private static class ECDHEScheme implements NamedGroupScheme { private static final ECDHEScheme instance = new ECDHEScheme(); @Override public byte[] encodePossessionPublicKey( NamedGroupPossession namedGroupPossession) { return ((ECDHEPossession)namedGroupPossession).encode(); } @Override public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded) throws IOException, GeneralSecurityException { return ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded); } @Override public SSLPossession createPossession( NamedGroup ng, SecureRandom random) { return new ECDHKeyExchange.ECDHEPossession(ng, random); } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc); } } private static class XDHScheme implements NamedGroupScheme { private static final XDHScheme instance = new XDHScheme(); @Override public byte[] encodePossessionPublicKey(NamedGroupPossession poss) { return ((XDHKeyExchange.XDHEPossession)poss).encode(); } @Override public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded) throws IOException, GeneralSecurityException { return XDHKeyExchange.XDHECredentials.valueOf(ng, encoded); } @Override public SSLPossession createPossession( NamedGroup ng, SecureRandom random) { return new XDHKeyExchange.XDHEPossession(ng, random); } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { return XDHKeyExchange.xdheKAGenerator.createKeyDerivation(hc); } } private static class KEMScheme implements NamedGroupScheme { private static final KEMScheme instance = new KEMScheme(); @Override public byte[] encodePossessionPublicKey(NamedGroupPossession poss) { return poss.encode(); } @Override public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded) throws IOException, GeneralSecurityException { return KEMKeyExchange.KEMCredentials.valueOf(ng, encoded); } @Override public SSLPossession createPossession(NamedGroup ng, SecureRandom random) { // Must call createPossession with isClient throw new UnsupportedOperationException(); } @Override public SSLPossession createPossession( NamedGroup ng, boolean isClient, SecureRandom random) { return isClient ? new KEMKeyExchange.KEMReceiverPossession(ng, random) : new KEMKeyExchange.KEMSenderPossession(ng, random); } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { return KEMKeyExchange.kemKAGenerator.createKeyDerivation(hc); } } // Inner class encapsulating supported named groups. static final class SupportedGroups { // Default named groups. private static final NamedGroup[] defaultGroups = new NamedGroup[]{ // Hybrid key agreement X25519MLKEM768, // Primary XDH (RFC 7748) curves X25519, // Primary NIST Suite B curves SECP256_R1, SECP384_R1, SECP521_R1, // Secondary XDH curves X448, // FFDHE (RFC 7919) FFDHE_2048, FFDHE_3072, FFDHE_4096 }; // Filter default groups names against default constraints. // Those are the values being displayed to the user with // "java -XshowSettings:security:tls" command. private static final String[] defaultNames = Arrays.stream( defaultGroups) .filter(ng -> ng.isAvailable) .filter(ng -> ng.isPermitted(SSLAlgorithmConstraints.DEFAULT)) .map(ng -> ng.name) .toArray(String[]::new); private static final NamedGroup[] customizedGroups = getCustomizedNamedGroups(); // Note: user-passed groups are not being filtered against default // algorithm constraints here. They will be displayed as-is. private static final String[] customizedNames = customizedGroups == null ? null : Arrays.stream(customizedGroups) .map(ng -> ng.name) .toArray(String[]::new); // Named group names for SSLConfiguration. static final String[] namedGroups; static { if (customizedNames != null) { namedGroups = customizedNames; } else { if (defaultNames.length == 0) { SSLLogger.logWarning(SSLLogger.Opt.SSL, "No default named groups"); } namedGroups = defaultNames; } } // Avoid the group lookup for default and customized groups. static NamedGroup[] getGroupsFromConfig(SSLConfiguration sslConfig) { if (sslConfig.namedGroups == defaultNames) { return defaultGroups; } else if (sslConfig.namedGroups == customizedNames) { return customizedGroups; } else { return Arrays.stream(sslConfig.namedGroups) .map(NamedGroup::nameOf) .filter(Objects::nonNull) .toArray(NamedGroup[]::new); } } // The value of the System Property defines a list of enabled named // groups in preference order, separated with comma. For example: // // jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048" // // If the System Property is not defined or the value is empty, the // default groups and preferences will be used. private static NamedGroup[] getCustomizedNamedGroups() { String property = System.getProperty("jdk.tls.namedGroups"); if (property != null && !property.isEmpty()) { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && property.charAt(property.length() - 1) == '"') { property = property.substring(1, property.length() - 1); } } if (property != null && !property.isEmpty()) { NamedGroup[] ret = Arrays.stream(property.split(",")) .map(String::trim) .map(NamedGroup::nameOf) .filter(Objects::nonNull) .filter(ng -> ng.isAvailable) .toArray(NamedGroup[]::new); if (ret.length == 0) { throw new IllegalArgumentException( "System property jdk.tls.namedGroups(" + property + ") contains no supported named groups"); } return ret; } return null; } } }