7192392: Better validation of client keys
authorxuelei
Mon, 22 Oct 2012 07:28:51 -0700
changeset 16080 0e6266b88242
parent 16079 7252530399e2
child 16081 452341da6f0b
7192392: Better validation of client keys Summary: Also reviewed by Andrew Gross<Andrew.Gross@Oracle.COM> Reviewed-by: vinnie
jdk/src/share/classes/com/sun/crypto/provider/DHKeyAgreement.java
jdk/src/share/classes/sun/security/pkcs11/P11KeyAgreement.java
jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java
jdk/src/share/classes/sun/security/ssl/DHClientKeyExchange.java
jdk/src/share/classes/sun/security/ssl/DHCrypt.java
jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java
jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java
jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java
jdk/src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java
jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java
jdk/src/share/classes/sun/security/util/KeyLength.java
jdk/src/share/classes/sun/security/util/KeyUtil.java
jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java
--- a/jdk/src/share/classes/com/sun/crypto/provider/DHKeyAgreement.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/com/sun/crypto/provider/DHKeyAgreement.java	Mon Oct 22 07:28:51 2012 -0700
@@ -41,6 +41,8 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.*;
 
+import sun.security.util.KeyUtil;
+
 /**
  * This class implements the Diffie-Hellman key agreement protocol between
  * any number of parties.
@@ -200,6 +202,9 @@
             throw new InvalidKeyException("Incompatible parameters");
         }
 
+        // validate the Diffie-Hellman public key
+        KeyUtil.validate(dhPubKey);
+
         // store the y value
         this.y = dhPubKey.getY();
 
--- a/jdk/src/share/classes/sun/security/pkcs11/P11KeyAgreement.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11KeyAgreement.java	Mon Oct 22 07:28:51 2012 -0700
@@ -37,6 +37,7 @@
 import static sun.security.pkcs11.TemplateManager.*;
 import sun.security.pkcs11.wrapper.*;
 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+import sun.security.util.KeyUtil;
 
 /**
  * KeyAgreement implementation class. This class currently supports
@@ -134,6 +135,10 @@
         BigInteger p, g, y;
         if (key instanceof DHPublicKey) {
             DHPublicKey dhKey = (DHPublicKey)key;
+
+            // validate the Diffie-Hellman public key
+            KeyUtil.validate(dhKey);
+
             y = dhKey.getY();
             DHParameterSpec params = dhKey.getParams();
             p = params.getP();
@@ -145,6 +150,10 @@
             try {
                 DHPublicKeySpec spec = kf.engineGetKeySpec(
                         key, DHPublicKeySpec.class);
+
+                // validate the Diffie-Hellman public key
+                KeyUtil.validate(spec);
+
                 y = spec.getY();
                 p = spec.getP();
                 g = spec.getG();
--- a/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java	Mon Oct 22 07:28:51 2012 -0700
@@ -192,8 +192,12 @@
                 }
                 break;
             case K_DH_ANON:
-                this.serverKeyExchange(new DH_ServerKeyExchange(
+                try {
+                    this.serverKeyExchange(new DH_ServerKeyExchange(
                                                 input, protocolVersion));
+                } catch (GeneralSecurityException e) {
+                    throwSSLException("Server key", e);
+                }
                 break;
             case K_DHE_DSS:
             case K_DHE_RSA:
@@ -921,7 +925,7 @@
         case K_DHE_RSA:
         case K_DHE_DSS:
         case K_DH_ANON:
-            preMasterSecret = dh.getAgreedSecret(serverDH);
+            preMasterSecret = dh.getAgreedSecret(serverDH, true);
             break;
         case K_ECDHE_RSA:
         case K_ECDHE_ECDSA:
--- a/jdk/src/share/classes/sun/security/ssl/DHClientKeyExchange.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/DHClientKeyExchange.java	Mon Oct 22 07:28:51 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -29,7 +29,7 @@
 import java.io.IOException;
 import java.io.PrintStream;
 import java.math.BigInteger;
-
+import javax.net.ssl.SSLHandshakeException;
 
 /*
  * Message used by clients to send their Diffie-Hellman public
@@ -50,7 +50,7 @@
     private byte dh_Yc[];               // 1 to 2^16 -1 bytes
 
     BigInteger getClientPublicKey() {
-        return new BigInteger(1, dh_Yc);
+        return dh_Yc == null ? null : new BigInteger(1, dh_Yc);
     }
 
     /*
@@ -72,7 +72,14 @@
      * but that's what the protocol spec requires.)
      */
     DHClientKeyExchange(HandshakeInStream input) throws IOException {
-        dh_Yc = input.getBytes16();
+        if (input.available() >= 2) {
+            dh_Yc = input.getBytes16();
+        } else {
+            // currently, we don't support cipher suites that requires
+            // implicit public key of client.
+            throw new SSLHandshakeException(
+                    "Unsupported implicit client DiffieHellman public key");
+        }
     }
 
     int messageLength() {
@@ -84,7 +91,9 @@
     }
 
     void send(HandshakeOutStream s) throws IOException {
-        s.putBytes16(dh_Yc);
+        if (dh_Yc != null && dh_Yc.length != 0) {
+            s.putBytes16(dh_Yc);
+        }
     }
 
     void print(PrintStream s) throws IOException {
--- a/jdk/src/share/classes/sun/security/ssl/DHCrypt.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/DHCrypt.java	Mon Oct 22 07:28:51 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -28,12 +28,15 @@
 
 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
@@ -54,7 +57,8 @@
  *  . 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
@@ -83,6 +87,9 @@
     // 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.
      */
@@ -90,9 +97,12 @@
         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();
@@ -115,20 +125,25 @@
             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");
@@ -166,17 +181,32 @@
      * <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
-     *  the same size as the Diffie-Hellman modulus.
+     * @param  peerPublicKey the peer's public key.
+     * @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");
@@ -185,4 +215,33 @@
         }
     }
 
+    // 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;
+    }
 }
--- a/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java	Mon Oct 22 07:28:51 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2011, 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
@@ -41,12 +41,14 @@
 
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.DHPublicKeySpec;
 
 import javax.net.ssl.*;
 
 import sun.security.internal.spec.TlsPrfParameterSpec;
 import sun.security.ssl.CipherSuite.*;
 import static sun.security.ssl.CipherSuite.PRF.*;
+import sun.security.util.KeyUtil;
 
 /**
  * Many data structures are involved in the handshake messages.  These
@@ -702,6 +704,7 @@
         this.protocolVersion = protocolVersion;
         this.preferableSignatureAlgorithm = null;
 
+        // The DH key has been validated in the constructor of DHCrypt.
         setValues(obj);
         signature = null;
     }
@@ -718,6 +721,7 @@
 
         this.protocolVersion = protocolVersion;
 
+        // The DH key has been validated in the constructor of DHCrypt.
         setValues(obj);
 
         Signature sig;
@@ -744,7 +748,8 @@
      * DH_anon key exchange
      */
     DH_ServerKeyExchange(HandshakeInStream input,
-            ProtocolVersion protocolVersion) throws IOException {
+            ProtocolVersion protocolVersion)
+            throws IOException, GeneralSecurityException {
 
         this.protocolVersion = protocolVersion;
         this.preferableSignatureAlgorithm = null;
@@ -752,6 +757,10 @@
         dh_p = input.getBytes16();
         dh_g = input.getBytes16();
         dh_Ys = input.getBytes16();
+        KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys),
+                                             new BigInteger(1, dh_p),
+                                             new BigInteger(1, dh_g)));
+
         signature = null;
     }
 
@@ -772,6 +781,9 @@
         dh_p = input.getBytes16();
         dh_g = input.getBytes16();
         dh_Ys = input.getBytes16();
+        KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys),
+                                             new BigInteger(1, dh_p),
+                                             new BigInteger(1, dh_g)));
 
         // read the signature and hash algorithm
         if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
--- a/jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Mon Oct 22 07:28:51 2012 -0700
@@ -36,7 +36,7 @@
 import javax.net.ssl.*;
 
 import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
-import sun.security.util.KeyLength;
+import sun.security.util.KeyUtil;
 
 /**
  * This is the client key exchange message (CLIENT --> SERVER) used with
@@ -193,7 +193,7 @@
                         "unable to get the plaintext of the premaster secret");
                 }
 
-                int keySize = KeyLength.getKeySize(secretKey);
+                int keySize = KeyUtil.getKeySize(secretKey);
                 if (keySize > 0 && keySize != 384) {       // 384 = 48 * 8
                     if (debug != null && Debug.isOn("handshake")) {
                         System.out.println(
--- a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java	Mon Oct 22 07:28:51 2012 -0700
@@ -1365,7 +1365,7 @@
         if (debug != null && Debug.isOn("handshake")) {
             mesg.print(System.out);
         }
-        return dh.getAgreedSecret(mesg.getClientPublicKey());
+        return dh.getAgreedSecret(mesg.getClientPublicKey(), false);
     }
 
     private SecretKey clientKeyExchange(ECDHClientKeyExchange mesg)
--- a/jdk/src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java	Mon Oct 22 07:28:51 2012 -0700
@@ -38,7 +38,7 @@
 import java.util.Collections;
 import java.util.ArrayList;
 
-import sun.security.util.KeyLength;
+import sun.security.util.KeyUtil;
 
 /**
  * Signature and hash algorithm.
@@ -279,7 +279,7 @@
              * If key size is less than 512, the  digest length should be
              * less than or equal to 20 bytes.
              */
-            int keySize = KeyLength.getKeySize(signingKey);
+            int keySize = KeyUtil.getKeySize(signingKey);
             if (keySize >= 768) {
                 maxDigestLength = HashAlgorithm.SHA512.length;
             } else if ((keySize >= 512) && (keySize < 768)) {
--- a/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java	Mon Oct 22 07:28:51 2012 -0700
@@ -440,7 +440,7 @@
 
         // Does this key constraint disable the specified key?
         public boolean disables(Key key) {
-            int size = KeyLength.getKeySize(key);
+            int size = KeyUtil.getKeySize(key);
 
             if (size == 0) {
                 return true;    // we don't allow any key of size 0.
--- a/jdk/src/share/classes/sun/security/util/KeyLength.java	Wed Aug 22 21:40:19 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 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
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.util;
-
-import java.security.Key;
-import java.security.PrivilegedAction;
-import java.security.AccessController;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.RSAKey;
-import java.security.interfaces.DSAKey;
-import javax.crypto.SecretKey;
-import javax.crypto.interfaces.DHKey;
-
-/**
- * A utility class to get key length
- */
-public final class KeyLength {
-
-    /**
-     * Returns the key size of the given key object in bits.
-     *
-     * @param key the key object, cannot be null
-     * @return the key size of the given key object in bits, or -1 if the
-     *       key size is not accessible
-     */
-    final public static int getKeySize(Key key) {
-        int size = -1;
-
-        if (key instanceof Length) {
-            try {
-                Length ruler = (Length)key;
-                size = ruler.length();
-            } catch (UnsupportedOperationException usoe) {
-                // ignore the exception
-            }
-
-            if (size >= 0) {
-                return size;
-            }
-        }
-
-        // try to parse the length from key specification
-        if (key instanceof SecretKey) {
-            SecretKey sk = (SecretKey)key;
-            String format = sk.getFormat();
-            if ("RAW".equals(format) && sk.getEncoded() != null) {
-                size = (sk.getEncoded().length * 8);
-            }   // Otherwise, it may be a unextractable key of PKCS#11, or
-                // a key we are not able to handle.
-        } else if (key instanceof RSAKey) {
-            RSAKey pubk = (RSAKey)key;
-            size = pubk.getModulus().bitLength();
-        } else if (key instanceof ECKey) {
-            ECKey pubk = (ECKey)key;
-            size = pubk.getParams().getOrder().bitLength();
-        } else if (key instanceof DSAKey) {
-            DSAKey pubk = (DSAKey)key;
-            size = pubk.getParams().getP().bitLength();
-        } else if (key instanceof DHKey) {
-            DHKey pubk = (DHKey)key;
-            size = pubk.getParams().getP().bitLength();
-        }   // Otherwise, it may be a unextractable key of PKCS#11, or
-            // a key we are not able to handle.
-
-        return size;
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/util/KeyUtil.java	Mon Oct 22 07:28:51 2012 -0700
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 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
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+import java.security.Key;
+import java.security.PrivilegedAction;
+import java.security.AccessController;
+import java.security.InvalidKeyException;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.DSAKey;
+import java.security.spec.KeySpec;
+import javax.crypto.SecretKey;
+import javax.crypto.interfaces.DHKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import java.math.BigInteger;
+
+/**
+ * A utility class to get key length, valiate keys, etc.
+ */
+public final class KeyUtil {
+
+    /**
+     * Returns the key size of the given key object in bits.
+     *
+     * @param key the key object, cannot be null
+     * @return the key size of the given key object in bits, or -1 if the
+     *       key size is not accessible
+     */
+    public static final int getKeySize(Key key) {
+        int size = -1;
+
+        if (key instanceof Length) {
+            try {
+                Length ruler = (Length)key;
+                size = ruler.length();
+            } catch (UnsupportedOperationException usoe) {
+                // ignore the exception
+            }
+
+            if (size >= 0) {
+                return size;
+            }
+        }
+
+        // try to parse the length from key specification
+        if (key instanceof SecretKey) {
+            SecretKey sk = (SecretKey)key;
+            String format = sk.getFormat();
+            if ("RAW".equals(format) && sk.getEncoded() != null) {
+                size = (sk.getEncoded().length * 8);
+            }   // Otherwise, it may be a unextractable key of PKCS#11, or
+                // a key we are not able to handle.
+        } else if (key instanceof RSAKey) {
+            RSAKey pubk = (RSAKey)key;
+            size = pubk.getModulus().bitLength();
+        } else if (key instanceof ECKey) {
+            ECKey pubk = (ECKey)key;
+            size = pubk.getParams().getOrder().bitLength();
+        } else if (key instanceof DSAKey) {
+            DSAKey pubk = (DSAKey)key;
+            size = pubk.getParams().getP().bitLength();
+        } else if (key instanceof DHKey) {
+            DHKey pubk = (DHKey)key;
+            size = pubk.getParams().getP().bitLength();
+        }   // Otherwise, it may be a unextractable key of PKCS#11, or
+            // a key we are not able to handle.
+
+        return size;
+    }
+
+    /**
+     * Returns whether the key is valid or not.
+     * <P>
+     * Note that this method is only apply to DHPublicKey at present.
+     *
+     * @param  publicKey
+     *         the key object, cannot be null
+     *
+     * @throws NullPointerException if {@code publicKey} is null
+     * @throws InvalidKeyException if {@code publicKey} is invalid
+     */
+    public static final void validate(Key key)
+            throws InvalidKeyException {
+        if (key == null) {
+            throw new NullPointerException(
+                "The key to be validated cannot be null");
+        }
+
+        if (key instanceof DHPublicKey) {
+            validateDHPublicKey((DHPublicKey)key);
+        }
+    }
+
+
+    /**
+     * Returns whether the key spec is valid or not.
+     * <P>
+     * Note that this method is only apply to DHPublicKeySpec at present.
+     *
+     * @param  keySpec
+     *         the key spec object, cannot be null
+     *
+     * @throws NullPointerException if {@code keySpec} is null
+     * @throws InvalidKeyException if {@code keySpec} is invalid
+     */
+    public static final void validate(KeySpec keySpec)
+            throws InvalidKeyException {
+        if (keySpec == null) {
+            throw new NullPointerException(
+                "The key spec to be validated cannot be null");
+        }
+
+        if (keySpec instanceof DHPublicKeySpec) {
+            validateDHPublicKey((DHPublicKeySpec)keySpec);
+        }
+    }
+
+    /**
+     * Returns whether the specified provider is Oracle provider or not.
+     * <P>
+     * Note that this method is only apply to SunJCE and SunPKCS11 at present.
+     *
+     * @param  providerName
+     *         the provider name
+     * @return true if, and only if, the provider of the specified
+     *         {@code providerName} is Oracle provider
+     */
+    public static final boolean isOracleJCEProvider(String providerName) {
+        return providerName != null && (providerName.equals("SunJCE") ||
+                                        providerName.startsWith("SunPKCS11"));
+    }
+
+    /**
+     * Returns whether the Diffie-Hellman public key is valid or not.
+     *
+     * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to
+     * validate Diffie-Hellman public keys:
+     * 1. Verify that y lies within the interval [2,p-1]. If it does not,
+     *    the key is invalid.
+     * 2. Compute y^q mod p. If the result == 1, the key is valid.
+     *    Otherwise the key is invalid.
+     */
+    private static void validateDHPublicKey(DHPublicKey publicKey)
+            throws InvalidKeyException {
+        DHParameterSpec paramSpec = publicKey.getParams();
+
+        BigInteger p = paramSpec.getP();
+        BigInteger g = paramSpec.getG();
+        BigInteger y = publicKey.getY();
+
+        validateDHPublicKey(p, g, y);
+    }
+
+    private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec)
+            throws InvalidKeyException {
+        validateDHPublicKey(publicKeySpec.getP(),
+            publicKeySpec.getG(), publicKeySpec.getY());
+    }
+
+    private static void validateDHPublicKey(BigInteger p,
+            BigInteger g, BigInteger y) throws InvalidKeyException {
+
+        // For better interoperability, the interval is limited to [2, p-2].
+        BigInteger leftOpen = BigInteger.ONE;
+        BigInteger rightOpen = p.subtract(BigInteger.ONE);
+        if (y.compareTo(leftOpen) <= 0) {
+            throw new InvalidKeyException(
+                    "Diffie-Hellman public key is too small");
+        }
+        if (y.compareTo(rightOpen) >= 0) {
+            throw new InvalidKeyException(
+                    "Diffie-Hellman public key is too large");
+        }
+
+        // Don't bother to check against the y^q mod p if safe primes are used.
+    }
+}
+
--- a/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java	Wed Aug 22 21:40:19 2012 -0400
+++ b/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java	Mon Oct 22 07:28:51 2012 -0700
@@ -29,7 +29,7 @@
 import javax.net.ssl.*;
 import java.lang.reflect.*;
 
-import sun.security.util.KeyLength;
+import sun.security.util.KeyUtil;
 
 public class ShortRSAKeyWithinTLS {
 
@@ -175,13 +175,13 @@
             privateKey = (PrivateKey)ks.getKey(keyAlias, null);
             publicKey = (PublicKey)ks.getCertificate(keyAlias).getPublicKey();
 
-            int privateKeySize = KeyLength.getKeySize(privateKey);
+            int privateKeySize = KeyUtil.getKeySize(privateKey);
             if (privateKeySize != keySize) {
                 throw new Exception("Expected key size is " + keySize +
                         ", but the private key size is " + privateKeySize);
             }
 
-            int publicKeySize = KeyLength.getKeySize(publicKey);
+            int publicKeySize = KeyUtil.getKeySize(publicKey);
             if (publicKeySize != keySize) {
                 throw new Exception("Expected key size is " + keySize +
                         ", but the public key size is " + publicKeySize);