jdk/src/share/classes/sun/security/ssl/DHCrypt.java

Print this page
rev 5679 : 7192392: Better validation of client keys
Summary: Also reviewed by Andrew Gross<Andrew.Gross@Oracle.COM>
Reviewed-by: vinnie

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -26,16 +26,19 @@
 
 package sun.security.ssl;
 
 import java.math.BigInteger;
 import java.security.*;
-
+import java.io.IOException;
+import javax.net.ssl.SSLHandshakeException;
 import javax.crypto.SecretKey;
 import javax.crypto.KeyAgreement;
 import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.*;
 
+import sun.security.util.KeyUtil;
+
 /**
  * This class implements the Diffie-Hellman key exchange algorithm.
  * D-H means combining your private key with your partners public key to
  * generate a number. The peer does the same with its private key and our
  * public key. Through the magic of Diffie-Hellman we both come up with the

@@ -52,11 +55,12 @@
  *
  * General usage of this class (TLS DHE case):
  *  . if we are server, call DHCrypt(keyLength,random). This generates
  *    an ephemeral keypair of the request length.
  *  . if we are client, call DHCrypt(modulus, base, random). This
- *    generates an ephemeral keypair using the parameters specified by the server.
+ *    generates an ephemeral keypair using the parameters specified by
+ *    the server.
  *  . send parameters and public value to remote peer
  *  . receive peers ephemeral public key
  *  . call getAgreedSecret() to calculate the shared secret
  *
  * In TLS the server chooses the parameter values itself, the client must use

@@ -81,20 +85,26 @@
     private PrivateKey privateKey;
 
     // public component of our key, X = (g ^ x) mod p
     private BigInteger publicValue;             // X (aka y)
 
+    // the times to recove from failure if public key validation
+    private static int MAX_FAILOVER_TIMES = 2;
+
     /**
      * Generate a Diffie-Hellman keypair of the specified size.
      */
     DHCrypt(int keyLength, SecureRandom random) {
         try {
             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
             kpg.initialize(keyLength, random);
-            KeyPair kp = kpg.generateKeyPair();
-            privateKey = kp.getPrivate();
-            DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
+
+            DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
+            if (spec == null) {
+                throw new RuntimeException("Could not generate DH keypair");
+            }
+
             publicValue = spec.getY();
             modulus = spec.getP();
             base = spec.getG();
         } catch (GeneralSecurityException e) {
             throw new RuntimeException("Could not generate DH keypair", e);

@@ -113,24 +123,29 @@
         this.base = base;
         try {
             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
             DHParameterSpec params = new DHParameterSpec(modulus, base);
             kpg.initialize(params, random);
-            KeyPair kp = kpg.generateKeyPair();
-            privateKey = kp.getPrivate();
-            DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
+
+            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;
             DHParameterSpec params = dhKey.getParams();
-            return new DHPublicKeySpec(dhKey.getY(), params.getP(), params.getG());
+            return new DHPublicKeySpec(dhKey.getY(),
+                                    params.getP(), params.getG());
         }
         try {
             KeyFactory factory = JsseJce.getKeyFactory("DH");
             return factory.getKeySpec(key, DHPublicKeySpec.class);
         } catch (Exception e) {

@@ -165,24 +180,68 @@
      *
      * <P>It is illegal to call this member function if the private key
      * has not been set (or generated).
      *
      * @param peerPublicKey the peer's public key.
-     * @returns the secret, which is an unsigned big-endian integer
+     * @param  keyIsValidated whether the {@code peerPublicKey} has beed
+     *         validated
+     * @return the secret, which is an unsigned big-endian integer
      *  the same size as the Diffie-Hellman modulus.
      */
-    SecretKey getAgreedSecret(BigInteger peerPublicValue) {
+    SecretKey getAgreedSecret(BigInteger peerPublicValue,
+            boolean keyIsValidated) throws IOException {
         try {
             KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
             DHPublicKeySpec spec =
                         new DHPublicKeySpec(peerPublicValue, modulus, base);
             PublicKey publicKey = kf.generatePublic(spec);
             KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
+
+            // validate the Diffie-Hellman public key
+            if (!keyIsValidated &&
+                    !KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) {
+                try {
+                    KeyUtil.validate(spec);
+                } catch (InvalidKeyException ike) {
+                    // prefer handshake_failure alert to internal_error alert
+                    throw new SSLHandshakeException(ike.getMessage());
+                }
+            }
+
             ka.init(privateKey);
             ka.doPhase(publicKey, true);
             return ka.generateSecret("TlsPremasterSecret");
         } catch (GeneralSecurityException e) {
             throw new RuntimeException("Could not generate secret", e);
         }
     }
 
+    // Generate and validate DHPublicKeySpec
+    private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg)
+            throws GeneralSecurityException {
+
+        boolean doExtraValiadtion =
+                    (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
+        for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) {
+            KeyPair kp = kpg.generateKeyPair();
+            privateKey = kp.getPrivate();
+            DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
+
+            // validate the Diffie-Hellman public key
+            if (doExtraValiadtion) {
+                try {
+                    KeyUtil.validate(spec);
+                } catch (InvalidKeyException ivke) {
+                    if (i == MAX_FAILOVER_TIMES) {
+                        throw ivke;
+                    }
+                    // otherwise, ignore the exception and try the next one
+                    continue;
+                }
+            }
+
+            return spec;
+        }
+
+        return null;
+    }
 }