8081760: Better group dynamics
authorxuelei
Mon, 20 Jul 2015 01:45:23 +0000
changeset 33295 052d130b84ed
parent 33294 1e3fd79b0265
child 33296 6fb299910fb8
8081760: Better group dynamics Summary: Allows user to specify custom DH groups. Also reviewed by Alexander Fomin <alexander.fomin@oracle.com>. Reviewed-by: coffeys, mullan, weijun, jnimeh, ahgross, asmotrak
jdk/src/java.base/share/classes/sun/security/ssl/DHCrypt.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java
jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java
jdk/src/java.base/share/conf/security/java.security
jdk/test/sun/security/ssl/DHKeyExchange/DHEKeySizing.java
--- a/jdk/src/java.base/share/classes/sun/security/ssl/DHCrypt.java	Tue Jul 14 17:06:41 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DHCrypt.java	Mon Jul 20 01:45:23 2015 +0000
@@ -26,6 +26,11 @@
 
 package sun.security.ssl;
 
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
 import java.math.BigInteger;
 import java.security.*;
 import java.io.IOException;
@@ -95,9 +100,35 @@
      * Generate a Diffie-Hellman keypair of the specified size.
      */
     DHCrypt(int keyLength, SecureRandom random) {
+        this(keyLength,
+                ParametersHolder.definedParams.get(keyLength), random);
+    }
+
+    /**
+     * Generate a Diffie-Hellman keypair using the specified parameters.
+     *
+     * @param modulus the Diffie-Hellman modulus P
+     * @param base the Diffie-Hellman base G
+     */
+    DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
+        this(modulus.bitLength(),
+                new DHParameterSpec(modulus, base), random);
+    }
+
+    /**
+     * Generate a Diffie-Hellman keypair using the specified size and
+     * parameters.
+     */
+    private DHCrypt(int keyLength,
+            DHParameterSpec params, SecureRandom random) {
+
         try {
             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
-            kpg.initialize(keyLength, random);
+            if (params != null) {
+                kpg.initialize(params, random);
+            } else {
+                kpg.initialize(keyLength, random);
+            }
 
             DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
             if (spec == null) {
@@ -112,33 +143,6 @@
         }
     }
 
-
-    /**
-     * Generate a Diffie-Hellman keypair using the specified parameters.
-     *
-     * @param modulus the Diffie-Hellman modulus P
-     * @param base the Diffie-Hellman base G
-     */
-    DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
-        this.modulus = modulus;
-        this.base = base;
-        try {
-            KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
-            DHParameterSpec params = new DHParameterSpec(modulus, base);
-            kpg.initialize(params, random);
-
-            DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
-            if (spec == null) {
-                throw new RuntimeException("Could not generate DH keypair");
-            }
-
-            publicValue = spec.getY();
-        } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate DH keypair", e);
-        }
-    }
-
-
     static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
         if (key instanceof DHPublicKey) {
             DHPublicKey dhKey = (DHPublicKey)key;
@@ -268,4 +272,141 @@
 
         return null;
     }
+
+    // lazy initialization holder class idiom for static default parameters
+    //
+    // See Effective Java Second Edition: Item 71.
+    private static class ParametersHolder {
+        private final static boolean debugIsOn =
+                (Debug.getInstance("ssl") != null) && Debug.isOn("sslctx");
+
+        //
+        // Default DH ephemeral parameters
+        //
+        private static final BigInteger g2 = BigInteger.valueOf(2);
+
+        private static final BigInteger p512 = new BigInteger(   // generated
+                "D87780E15FF50B4ABBE89870188B049406B5BEA98AB23A02" +
+                "41D88EA75B7755E669C08093D3F0CA7FC3A5A25CF067DCB9" +
+                "A43DD89D1D90921C6328884461E0B6D3", 16);
+        private static final BigInteger p768 = new BigInteger(   // RFC 2409
+                "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
+                "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
+                "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
+                "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16);
+
+        private static final BigInteger p1024 = new BigInteger(  // RFC 2409
+                "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
+                "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
+                "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
+                "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
+                "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" +
+                "FFFFFFFFFFFFFFFF", 16);
+        private static final BigInteger p2048 = new BigInteger(  // TLS FEDHE
+                "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
+                "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
+                "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
+                "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
+                "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
+                "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
+                "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
+                "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
+                "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
+                "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
+                "886B423861285C97FFFFFFFFFFFFFFFF", 16);
+
+        private static final BigInteger[] supportedPrimes = {
+                p512, p768, p1024, p2048};
+
+        // a measure of the uncertainty that prime modulus p is not a prime
+        //
+        // see BigInteger.isProbablePrime(int certainty)
+        private final static int PRIME_CERTAINTY = 120;
+
+        // the known security property, jdk.tls.server.defaultDHEParameters
+        private final static String PROPERTY_NAME =
+                "jdk.tls.server.defaultDHEParameters";
+
+        private static final Pattern spacesPattern = Pattern.compile("\\s+");
+
+        private final static Pattern syntaxPattern = Pattern.compile(
+                "(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
+                "(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
+
+        private static final Pattern paramsPattern = Pattern.compile(
+                "\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
+
+        // cache of predefined default DH ephemeral parameters
+        private final static Map<Integer,DHParameterSpec> definedParams;
+
+        static {
+            String property = AccessController.doPrivileged(
+                new PrivilegedAction<String>() {
+                    public String run() {
+                        return Security.getProperty(PROPERTY_NAME);
+                    }
+                });
+
+            if (property != null && !property.isEmpty()) {
+                // remove double quote marks from beginning/end of the property
+                if (property.length() >= 2 && property.charAt(0) == '"' &&
+                        property.charAt(property.length() - 1) == '"') {
+                    property = property.substring(1, property.length() - 1);
+                }
+
+                property = property.trim();
+            }
+
+            if (property != null && !property.isEmpty()) {
+                Matcher spacesMatcher = spacesPattern.matcher(property);
+                property = spacesMatcher.replaceAll("");
+
+                if (debugIsOn) {
+                    System.out.println("The Security Property " +
+                            PROPERTY_NAME + ": " + property);
+                }
+            }
+
+            Map<Integer,DHParameterSpec> defaultParams = new HashMap<>();
+            if (property != null && !property.isEmpty()) {
+                Matcher syntaxMatcher = syntaxPattern.matcher(property);
+                if (syntaxMatcher.matches()) {
+                    Matcher paramsFinder = paramsPattern.matcher(property);
+                    while(paramsFinder.find()) {
+                        String primeModulus = paramsFinder.group(1);
+                        BigInteger p = new BigInteger(primeModulus, 16);
+                        if (!p.isProbablePrime(PRIME_CERTAINTY)) {
+                            if (debugIsOn) {
+                                System.out.println(
+                                    "Prime modulus p in Security Property, " +
+                                    PROPERTY_NAME + ", is not a prime: " +
+                                    primeModulus);
+                            }
+
+                            continue;
+                        }
+
+                        String baseGenerator = paramsFinder.group(2);
+                        BigInteger g = new BigInteger(baseGenerator, 16);
+
+                        DHParameterSpec spec = new DHParameterSpec(p, g);
+                        int primeLen = p.bitLength();
+                        defaultParams.put(primeLen, spec);
+                    }
+                } else if (debugIsOn) {
+                    System.out.println("Invalid Security Property, " +
+                            PROPERTY_NAME + ", definition");
+                }
+            }
+
+            for (BigInteger p : supportedPrimes) {
+                int primeLen = p.bitLength();
+                defaultParams.putIfAbsent(primeLen, new DHParameterSpec(p, g2));
+            }
+
+            definedParams =
+                    Collections.<Integer,DHParameterSpec>unmodifiableMap(
+                                                                defaultParams);
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java	Tue Jul 14 17:06:41 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java	Mon Jul 20 01:45:23 2015 +0000
@@ -284,7 +284,7 @@
                                        ((plaintext.get() & 0xFF) << 8) |
                                         (plaintext.get() & 0xFF);
                     plaintext.position(frgPos);
-                    if (remains < (handshakeLen + 1)) { // 1: handshake type
+                    if (remains < (handshakeLen + 4)) { // 4: handshake header
                         // This handshake message is fragmented.
                         prevType = handshakeType;
                         hsMsgOff = remains - 4;         // 4: handshake header
--- a/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java	Tue Jul 14 17:06:41 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java	Mon Jul 20 01:45:23 2015 +0000
@@ -54,13 +54,12 @@
         String[] algorithmsInProperty = null;
         if (property != null && !property.isEmpty()) {
             // remove double quote marks from beginning/end of the property
-            if (property.charAt(0) == '"'
-                    && property.charAt(property.length() - 1) == '"') {
+            if (property.length() >= 2 && property.charAt(0) == '"' &&
+                    property.charAt(property.length() - 1) == '"') {
                 property = property.substring(1, property.length() - 1);
             }
             algorithmsInProperty = property.split(",");
-            for (int i = 0; i < algorithmsInProperty.length;
-                    i++) {
+            for (int i = 0; i < algorithmsInProperty.length; i++) {
                 algorithmsInProperty[i] = algorithmsInProperty[i].trim();
             }
         }
--- a/jdk/src/java.base/share/conf/security/java.security	Tue Jul 14 17:06:41 2015 -0500
+++ b/jdk/src/java.base/share/conf/security/java.security	Mon Jul 20 01:45:23 2015 +0000
@@ -634,3 +634,60 @@
         DH_RSA_EXPORT, RSA_EXPORT, \
         DH_anon, ECDH_anon, \
         RC4_128, RC4_40, DES_CBC, DES40_CBC
+
+# The pre-defined default finite field Diffie-Hellman ephemeral (DHE)
+# parameters for Transport Layer Security (SSL/TLS/DTLS) processing.
+#
+# In traditional SSL/TLS/DTLS connections where finite field DHE parameters
+# negotiation mechanism is not used, the server offers the client group
+# parameters, base generator g and prime modulus p, for DHE key exchange.
+# It is recommended to use dynamic group parameters.  This property defines
+# a mechanism that allows you to specify custom group parameters.
+#
+# The syntax of this property string is described as this Java BNF-style:
+#   DefaultDHEParameters:
+#       DefinedDHEParameters { , DefinedDHEParameters }
+#
+#   DefinedDHEParameters:
+#       "{" DHEPrimeModulus , DHEBaseGenerator "}"
+#
+#   DHEPrimeModulus:
+#       HexadecimalDigits
+#
+#   DHEBaseGenerator:
+#       HexadecimalDigits
+#
+#   HexadecimalDigits:
+#       HexadecimalDigit { HexadecimalDigit }
+#
+#   HexadecimalDigit: one of
+#       0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
+#
+# Whitespace characters are ignored.
+#
+# The "DefinedDHEParameters" defines the custom group parameters, prime
+# modulus p and base generator g, for a particular size of prime modulus p.
+# The "DHEPrimeModulus" defines the hexadecimal prime modulus p, and the
+# "DHEBaseGenerator" defines the hexadecimal base generator g of a group
+# parameter.  It is recommended to use safe primes for the custom group
+# parameters.
+#
+# If this property is not defined or the value is empty, the underlying JSSE
+# provider's default group parameter is used for each connection.
+#
+# If the property value does not follow the grammar, or a particular group
+# parameter is not valid, the connection will fall back and use the
+# underlying JSSE provider's default group parameter.
+#
+# Note: This property is currently used by OpenJDK's JSSE implementation. It
+# is not guaranteed to be examined and used by other implementations.
+#
+# Example:
+#   jdk.tls.server.defaultDHEParameters=
+#       { \
+#       FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 \
+#       29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD \
+#       EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 \
+#       E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED \
+#       EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 \
+#       FFFFFFFF FFFFFFFF, 2}
--- a/jdk/test/sun/security/ssl/DHKeyExchange/DHEKeySizing.java	Tue Jul 14 17:06:41 2015 -0500
+++ b/jdk/test/sun/security/ssl/DHKeyExchange/DHEKeySizing.java	Mon Jul 20 01:45:23 2015 +0000
@@ -31,34 +31,34 @@
  * @bug 6956398
  * @summary make ephemeral DH key match the length of the certificate key
  * @run main/othervm
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1318 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=matched
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1318 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1318 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=1024
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1318 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
  *
  * @run main/othervm
- *      DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 292 75
+ *      DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 229 75
  *
  * @run main/othervm
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1510 139
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1383 139
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1414 107
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1319 107
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=matched
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1894 267
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1639 267
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=1024
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1510 139
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1383 139
  *
  * @run main/othervm
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 484 139
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=legacy
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 388 107
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 293 107
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=matched
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 484 139
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
  * @run main/othervm -Djdk.tls.ephemeralDHKeySize=1024
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 484 139
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
  */
 
 /*
@@ -90,10 +90,10 @@
  * Here is a summary of the record length in the test case.
  *
  *            |  ServerHello Series  |  ClientKeyExchange | ServerHello Anon
- *   512-bit  |          1318 bytes  |           75 bytes |        292 bytes
- *   768-bit  |          1414 bytes  |          107 bytes |        388 bytes
- *  1024-bit  |          1510 bytes  |          139 bytes |        484 bytes
- *  2048-bit  |          1894 bytes  |          267 bytes |        484 bytes
+ *   512-bit  |          1255 bytes  |           75 bytes |        229 bytes
+ *   768-bit  |          1319 bytes  |          107 bytes |        293 bytes
+ *  1024-bit  |          1383 bytes  |          139 bytes |        357 bytes
+ *  2048-bit  |          1639 bytes  |          267 bytes |        357 bytes
  */
 
 import javax.net.ssl.*;