# HG changeset patch # User xuelei # Date 1605714766 0 # Wed Nov 18 15:52:46 2020 +0000 # Node ID 48bd8224c3040c0cc90d0f9764733e4c881d6f39 # Parent adb72d8697d2af3a521cd222c3b5439b5048680a 8226374: Restrict TLS signature schemes and named groups Reviewed-by: mullan diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -1055,6 +1055,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) { diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -735,6 +735,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) { diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java --- a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java @@ -588,6 +588,7 @@ ClientHandshakeContext chc = (ClientHandshakeContext)context; Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + chc.algorithmConstraints, chc.peerRequestedSignatureSchemes, x509Possession, chc.negotiatedProtocol); @@ -897,6 +898,7 @@ Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + context.algorithmConstraints, context.peerRequestedSignatureSchemes, x509Possession, context.negotiatedProtocol); diff --git a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java --- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java +++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java @@ -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. @@ -1055,12 +1055,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; @@ -1074,8 +1074,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; diff --git a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java @@ -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 = JsseJce.getKeyFactory("DiffieHellman"); DHPublicKeySpec spec = new DHPublicKeySpec( new BigInteger(1, encodedPublic), @@ -110,9 +106,7 @@ try { KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("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, diff --git a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java @@ -127,6 +127,7 @@ if (useExplicitSigAlgorithm) { Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + shc.algorithmConstraints, shc.peerRequestedSignatureSchemes, x509Possession, shc.negotiatedProtocol); diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java @@ -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,10 +97,7 @@ } ECParameterSpec parameters = - JsseJce.getECParameterSpec(namedGroup.oid); - if (parameters == null) { - return null; - } + (ECParameterSpec)namedGroup.keAlgParamSpec; ECPoint point = JsseJce.decodePoint( encodedPoint, parameters.getCurve()); @@ -120,9 +116,7 @@ ECDHEPossession(NamedGroup namedGroup, SecureRandom random) { try { KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("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 +242,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 +302,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 +475,7 @@ } String alg; - switch (namedGroup.type) { + switch (namedGroup.spec) { case NAMED_GROUP_ECDHE: alg = "ECDH"; break; diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java @@ -39,6 +39,7 @@ import java.text.MessageFormat; import java.util.Locale; import java.util.Map; +import sun.security.ssl.NamedGroup.NamedGroupSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.ssl.X509Authentication.X509Credentials; @@ -111,13 +112,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) { @@ -132,6 +138,7 @@ if (useExplicitSigAlgorithm) { Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + shc.algorithmConstraints, shc.peerRequestedSignatureSchemes, x509Possession, shc.negotiatedProtocol); diff --git a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java @@ -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}; diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -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 { @@ -282,8 +282,8 @@ } boolean found = false; - Map cachedStatus = - new EnumMap<>(NamedGroupType.class); + Map cachedStatus = + new EnumMap<>(NamedGroupSpec.class); for (CipherSuite suite : enabledCipherSuites) { if (suite.isAvailable() && suite.supports(protocol)) { if (isActivatable(suite, @@ -322,8 +322,8 @@ List suites = new LinkedList<>(); if (enabledProtocols != null && !enabledProtocols.isEmpty()) { - Map cachedStatus = - new EnumMap<>(NamedGroupType.class); + Map cachedStatus = + new EnumMap<>(NamedGroupSpec.class); for (CipherSuite suite : enabledCipherSuites) { if (!suite.isAvailable()) { continue; @@ -508,7 +508,7 @@ private static boolean isActivatable(CipherSuite suite, AlgorithmConstraints algorithmConstraints, - Map cachedStatus) { + Map cachedStatus) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { @@ -519,8 +519,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) { diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -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,188 +51,254 @@ // See sun.security.util.CurveDB for the OIDs // NIST K-163 - SECT163_K1(0x0001, "sect163k1", "1.3.132.0.1", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT163_R1(0x0002, "sect163r1", "1.3.132.0.2", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT163_K1(0x0001, "sect163k1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect163k1")), + SECT163_R1(0x0002, "sect163r1", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect163r1")), // NIST B-163 - SECT163_R2(0x0003, "sect163r2", "1.3.132.0.15", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT193_R1(0x0004, "sect193r1", "1.3.132.0.24", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT193_R2(0x0005, "sect193r2", "1.3.132.0.25", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT163_R2(0x0003, "sect163r2", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect163r2")), + SECT193_R1(0x0004, "sect193r1", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect193r1")), + SECT193_R2(0x0005, "sect193r2", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect193r2")), // NIST K-233 - SECT233_K1(0x0006, "sect233k1", "1.3.132.0.26", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT233_K1(0x0006, "sect233k1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect233k1")), // NIST B-233 - SECT233_R1(0x0007, "sect233r1", "1.3.132.0.27", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT239_K1(0x0008, "sect239k1", "1.3.132.0.3", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT233_R1(0x0007, "sect233r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect233r1")), + SECT239_K1(0x0008, "sect239k1", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect239k1")), // NIST K-283 - SECT283_K1(0x0009, "sect283k1", "1.3.132.0.16", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT283_K1(0x0009, "sect283k1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect163k1")), // NIST B-283 - SECT283_R1(0x000A, "sect283r1", "1.3.132.0.17", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT283_R1(0x000A, "sect283r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect283r1")), // NIST K-409 - SECT409_K1(0x000B, "sect409k1", "1.3.132.0.36", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT409_K1(0x000B, "sect409k1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect409k1")), // NIST B-409 - SECT409_R1(0x000C, "sect409r1", "1.3.132.0.37", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT409_R1(0x000C, "sect409r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect409r1")), // NIST K-571 - SECT571_K1(0x000D, "sect571k1", "1.3.132.0.38", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT571_K1(0x000D, "sect571k1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect571k1")), // NIST B-571 - SECT571_R1(0x000E, "sect571r1", "1.3.132.0.39", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP160_K1(0x000F, "secp160k1", "1.3.132.0.9", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP160_R1(0x0010, "secp160r1", "1.3.132.0.8", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP160_R2(0x0011, "secp160r2", "1.3.132.0.30", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP192_K1(0x0012, "secp192k1", "1.3.132.0.31", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECT571_R1(0x000E, "sect571r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("sect571r1")), + SECP160_K1(0x000F, "secp160k1", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("secp160k1")), + SECP160_R1(0x0010, "secp160r1", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("secp160r1")), + SECP160_R2(0x0011, "secp160r2", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("secp160r2")), + SECP192_K1(0x0012, "secp192k1", false, + 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", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP224_K1(0x0014, "secp224k1", "1.3.132.0.32", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECP192_R1(0x0013, "secp192r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("secp192r1")), + SECP224_K1(0x0014, "secp224k1", false, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("secp224k1")), // NIST P-224 - SECP224_R1(0x0015, "secp224r1", "1.3.132.0.33", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP256_K1(0x0016, "secp256k1", "1.3.132.0.10", false, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), + SECP224_R1(0x0015, "secp224r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12, + CurveDB.lookup("secp224r1")), + SECP256_K1(0x0016, "secp256k1", false, + 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", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13), + SECP256_R1(0x0017, "secp256r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_13, + CurveDB.lookup("secp256r1")), // NIST P-384 - SECP384_R1(0x0018, "secp384r1", "1.3.132.0.34", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13), + SECP384_R1(0x0018, "secp384r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_13, + CurveDB.lookup("secp384r1")), // NIST P-521 - SECP521_R1(0x0019, "secp521r1", "1.3.132.0.35", true, - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13), + SECP521_R1(0x0019, "secp521r1", true, + NamedGroupSpec.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_13, + CurveDB.lookup("secp521r1")), // x25519 and x448 (RFC 8422/8446) - X25519(0x001D, "x25519", "1.3.101.110", true, - NamedGroupType.NAMED_GROUP_XDH, - ProtocolVersion.PROTOCOLS_TO_13), - X448(0x001E, "x448", "1.3.101.111", true, - NamedGroupType.NAMED_GROUP_XDH, - ProtocolVersion.PROTOCOLS_TO_13), + X25519(0x001D, "x25519", true, + NamedGroupSpec.NAMED_GROUP_XDH, + ProtocolVersion.PROTOCOLS_TO_13, + NamedParameterSpec.X25519), + X448(0x001E, "x448", true, + NamedGroupSpec.NAMED_GROUP_XDH, + ProtocolVersion.PROTOCOLS_TO_13, + NamedParameterSpec.X448), // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919) - FFDHE_2048(0x0100, "ffdhe2048", null, true, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_3072(0x0101, "ffdhe3072", null, true, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_4096(0x0102, "ffdhe4096", null, true, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_6144(0x0103, "ffdhe6144", null, true, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_8192(0x0104, "ffdhe8192", null, true, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_2048(0x0100, "ffdhe2048", true, + NamedGroupSpec.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13, + PredefinedDHParameterSpecs.ffdheParams.get(2048)), + FFDHE_3072(0x0101, "ffdhe3072", true, + NamedGroupSpec.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13, + PredefinedDHParameterSpecs.ffdheParams.get(3072)), + FFDHE_4096(0x0102, "ffdhe4096", true, + NamedGroupSpec.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13, + PredefinedDHParameterSpecs.ffdheParams.get(4096)), + FFDHE_6144(0x0103, "ffdhe6144", true, + NamedGroupSpec.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13, + PredefinedDHParameterSpecs.ffdheParams.get(6144)), + FFDHE_8192(0x0104, "ffdhe8192", true, + 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, false, - NamedGroupType.NAMED_GROUP_ARBITRARY, - ProtocolVersion.PROTOCOLS_TO_12), - ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves", null, false, - NamedGroupType.NAMED_GROUP_ARBITRARY, - ProtocolVersion.PROTOCOLS_TO_12); + ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves", false, + NamedGroupSpec.NAMED_GROUP_ARBITRARY, + ProtocolVersion.PROTOCOLS_TO_12, + null), + ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves", false, + 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 boolean isFips; // can be used in FIPS mode? + 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 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, boolean isFips, - NamedGroupType namedGroupType, - ProtocolVersion[] supportedProtocols) { + private NamedGroup(int id, String name, boolean isFips, + NamedGroupSpec namedGroupSpec, + ProtocolVersion[] supportedProtocols, + AlgorithmParameterSpec keAlgParamSpec) { this.id = id; this.name = name; - this.oid = oid; - this.type = namedGroupType; this.isFips = isFips; + 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 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) { @@ -245,12 +310,11 @@ } static NamedGroup valueOf(ECParameterSpec params) { - String oid = JsseJce.getNamedCurveOid(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; } } } @@ -260,25 +324,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); - if (aps == null) - continue; - 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; @@ -308,118 +358,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 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 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 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 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 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 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) { - - Optional ngf = getFunctions(); - if (ngf.isEmpty()) { - return null; - } - return ngf.get().createPossession(this, random); + return spec.createPossession(this, random); } - SSLKeyDerivation createKeyDerivation(HandshakeContext hc) - throws IOException { - - Optional ngf = getFunctions(); - if (ngf.isEmpty()) { - return null; - } - return ngf.get().createKeyDerivation(hc); - + SSLKeyDerivation createKeyDerivation( + HandshakeContext hc) throws IOException { + return spec.createKeyDerivation(hc); } - boolean isAvailableGroup() { - Optional ngfOpt = getFunctions(); - if (ngfOpt.isEmpty()) { - return false; - } - NamedGroupFunctions ngf = ngfOpt.get(); - return ngf.isAvailable(this); + interface ExceptionSupplier { + void apply(String s) throws SSLException; } - 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 + // 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"); + } + } + + 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; + } + + 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; + } boolean isSupported(List cipherSuites) { for (CipherSuite cs : cipherSuites) { @@ -432,92 +492,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 - 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 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 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( @@ -528,8 +559,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); @@ -547,111 +578,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 getParametersImpl( - NamedGroup ng) { - try { - AlgorithmParameters params - = JsseJce.getAlgorithmParameters("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( @@ -662,8 +597,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); @@ -681,52 +616,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 getParametersImpl( - NamedGroup ng) { - try { - AlgorithmParameters params - = JsseJce.getAlgorithmParameters("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) { @@ -736,8 +633,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); @@ -755,31 +652,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 { - JsseJce.getKeyAgreement(ng.algorithm); - return true; - } catch (NoSuchAlgorithmException ex) { - return false; - } - } - - @Override - protected Optional getParametersImpl( - NamedGroup ng) { - return Optional.empty(); - } } } diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -40,7 +40,7 @@ import java.util.List; import java.util.Map; 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; @@ -151,7 +151,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. @@ -186,21 +186,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 = JsseJce.getSignature("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( @@ -211,10 +215,7 @@ this.isAvailable = mediator; this.parameterSpec = mediator ? pssParamSpec : null; - } - - AlgorithmParameterSpec getParameterSpec() { - return parameterSpec; + this.parameters = mediator ? pssParams : null; } } @@ -257,7 +258,7 @@ private SignatureScheme(int id, String name, String algorithm, String keyAlgorithm, - SigAlgParamSpec signAlgParamSpec, + SigAlgParamSpec signAlgParams, NamedGroup namedGroup, int minimalKeySize, ProtocolVersion[] supportedProtocols, ProtocolVersion[] handshakeSupportedProtocols) { @@ -265,8 +266,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); @@ -284,8 +284,8 @@ // Check the specific algorithm and parameters. if (mediator) { - if (signAlgParamSpec != null) { - mediator = signAlgParamSpec.isAvailable; + if (signAlgParams != null) { + mediator = signAlgParams.isAvailable; } else { try { JsseJce.getSignature(algorithm); @@ -355,6 +355,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 getSupportedAlgorithms( @@ -383,8 +395,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")) { @@ -418,8 +429,7 @@ ss.supportedProtocols.contains(protocolVersion) && (config.signatureSchemes.isEmpty() || config.signatureSchemes.contains(ss)) && - constraints.permits(SIGNATURE_PRIMITIVE_SET, - ss.algorithm, null)) { + ss.isPermitted(constraints)) { supported.add(ss); } else { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { @@ -433,6 +443,7 @@ } static SignatureScheme getPreferableAlgorithm( + AlgorithmConstraints constraints, List schemes, SignatureScheme certScheme, ProtocolVersion version) { @@ -440,8 +451,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; } } @@ -450,6 +461,7 @@ } static Map.Entry getSignerOfPreferableAlgorithm( + AlgorithmConstraints constraints, List schemes, X509Possession x509Possession, ProtocolVersion version) { @@ -467,9 +479,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 && @@ -552,7 +565,9 @@ } Signature verifier = Signature.getInstance(algorithm); - SignatureUtil.initVerifyWithParam(verifier, publicKey, signAlgParameter); + SignatureUtil.initVerifyWithParam(verifier, publicKey, + (signAlgParams != null ? + signAlgParams.parameterSpec : null)); return verifier; } @@ -569,8 +584,9 @@ try { Signature signer = Signature.getInstance(algorithm); SignatureUtil.initSignWithParam(signer, privateKey, - signAlgParameter, - null); + (signAlgParams != null ? + signAlgParams.parameterSpec : null), + null); return signer; } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException nsae) { diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java @@ -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; @@ -192,7 +187,7 @@ NamedGroup namedGroup = NamedGroup.nameOf(group); if (namedGroup != null && (!requireFips || namedGroup.isFips)) { - if (isAvailableGroup(namedGroup)) { + if (namedGroup.isAvailable) { groupList.add(namedGroup); } } // ignore unknown groups @@ -247,7 +242,7 @@ groupList = new ArrayList<>(groups.length); for (NamedGroup group : groups) { - if (isAvailableGroup(group)) { + if (group.isAvailable) { groupList.add(group); } } @@ -265,48 +260,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; } } @@ -318,20 +284,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? @@ -347,16 +310,13 @@ static NamedGroup getPreferredGroup( ProtocolVersion negotiatedProtocol, - AlgorithmConstraints constraints, NamedGroupType[] types, + AlgorithmConstraints constraints, NamedGroupSpec[] types, List 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; } } @@ -366,14 +326,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; } } @@ -413,15 +370,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( @@ -569,15 +524,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( diff --git a/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java @@ -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 = JsseJce.getKeyPairGenerator(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(); diff --git a/src/java.base/share/classes/sun/security/util/CurveDB.java b/src/java.base/share/classes/sun/security/util/CurveDB.java --- a/src/java.base/share/classes/sun/security/util/CurveDB.java +++ b/src/java.base/share/classes/sun/security/util/CurveDB.java @@ -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; } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -560,9 +560,9 @@ # include # # 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 @@ -725,8 +725,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. @@ -747,7 +748,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, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \ DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ include jdk.disabled.namedCurves @@ -792,8 +794,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 diff --git a/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java new file mode 100644 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java @@ -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"); + } + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/RestrictSignatureScheme.java b/test/jdk/sun/security/ssl/CipherSuite/RestrictSignatureScheme.java new file mode 100644 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/RestrictSignatureScheme.java @@ -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; + } +}