changeset 6397:f7ea2b8a1b7c

8023069: Enhance TLS connections Summary: Also reviewed by Alexander Fomin and Andrew Gross Reviewed-by: wetmore
author xuelei
date Thu, 05 Sep 2013 18:17:49 -0700
parents 071079886c88
children 9716ade5079d
files src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java src/share/classes/sun/security/pkcs11/P11RSACipher.java src/share/classes/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java src/share/classes/sun/security/rsa/RSAPadding.java src/share/classes/sun/security/ssl/Handshaker.java src/share/classes/sun/security/ssl/RSAClientKeyExchange.java
diffstat 7 files changed, 211 insertions(+), 218 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, 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
@@ -72,13 +72,17 @@
             throw new IllegalStateException(
                 "TlsRsaPremasterSecretGenerator must be initialized");
         }
-        if (random == null) {
-            random = new SecureRandom();
+        byte[] b = spec.getEncodedSecret();
+        if (b == null) {
+            if (random == null) {
+                random = new SecureRandom();
+            }
+            b = new byte[48];
+            random.nextBytes(b);
+            b[0] = (byte)spec.getMajorVersion();
+            b[1] = (byte)spec.getMinorVersion();
         }
-        byte[] b = new byte[48];
-        random.nextBytes(b);
-        b[0] = (byte)spec.getMajorVersion();
-        b[1] = (byte)spec.getMinorVersion();
+
         return new SecretKeySpec(b, "TlsRsaPremasterSecret");
     }
 
--- a/src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, 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
@@ -45,11 +45,12 @@
 
     private final int majorVersion;
     private final int minorVersion;
+    private final byte[] encodedSecret;
 
     /**
      * Constructs a new TlsRsaPremasterSecretParameterSpec.
-     *
-     * <p>The version numbers will be placed inside the premaster secret to
+     * <P>
+     * The version numbers will be placed inside the premaster secret to
      * detect version rollbacks attacks as described in the TLS specification.
      * Note that they do not indicate the protocol version negotiated for
      * the handshake.
@@ -65,7 +66,42 @@
         this.majorVersion =
             TlsMasterSecretParameterSpec.checkVersion(majorVersion);
         this.minorVersion =
-            TlsMasterSecretParameterSpec.checkVersion(minorVersion); }
+            TlsMasterSecretParameterSpec.checkVersion(minorVersion);
+        this.encodedSecret = null;
+    }
+
+    /**
+     * Constructs a new TlsRsaPremasterSecretParameterSpec.
+     * <P>
+     * The version numbers will be placed inside the premaster secret to
+     * detect version rollbacks attacks as described in the TLS specification.
+     * Note that they do not indicate the protocol version negotiated for
+     * the handshake.
+     * <P>
+     * Usually, the encoded secret key is a random number that acts as
+     * dummy pre_master_secret to avoid vulnerabilities described by
+     * section 7.4.7.1, RFC 5246.
+     *
+     * @param majorVersion the major number of the protocol version
+     * @param minorVersion the minor number of the protocol version
+     * @param encodedSecret the encoded secret key
+     *
+     * @throws IllegalArgumentException if minorVersion or majorVersion are
+     *   negative or larger than 255, or encodedSecret is not exactly 48 bytes.
+     */
+    public TlsRsaPremasterSecretParameterSpec(int majorVersion,
+            int minorVersion, byte[] encodedSecret) {
+        this.majorVersion =
+            TlsMasterSecretParameterSpec.checkVersion(majorVersion);
+        this.minorVersion =
+            TlsMasterSecretParameterSpec.checkVersion(minorVersion);
+
+        if (encodedSecret == null || encodedSecret.length != 48) {
+            throw new IllegalArgumentException(
+                        "Encoded secret is not exactly 48 bytes");
+        }
+        this.encodedSecret = encodedSecret.clone();
+    }
 
     /**
      * Returns the major version.
@@ -84,4 +120,13 @@
     public int getMinorVersion() {
         return minorVersion;
     }
+
+    /**
+     * Returns the encoded secret.
+     *
+     * @return the encoded secret, may be null if no encoded secret.
+     */
+    public byte[] getEncodedSecret() {
+        return encodedSecret == null ? null : encodedSecret.clone();
+    }
 }
--- a/src/share/classes/sun/security/pkcs11/P11RSACipher.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/sun/security/pkcs11/P11RSACipher.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -451,30 +451,7 @@
     // see JCE spec
     protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
             int type) throws InvalidKeyException, NoSuchAlgorithmException {
-        if (algorithm.equals("TlsRsaPremasterSecret")) {
-            // the instance variable "session" has been initialized for
-            // decrypt mode, so use a local variable instead.
-            Session s = null;
-            try {
-                s = token.getObjSession();
-                long keyType = CKK_GENERIC_SECRET;
-                CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
-                    new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
-                    new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
-                };
-                attributes = token.getAttributes
-                    (O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
-                long keyID = token.p11.C_UnwrapKey(s.id(),
-                        new CK_MECHANISM(mechanism), p11Key.keyID, wrappedKey,
-                        attributes);
-                return P11Key.secretKey(s, keyID, algorithm, 48 << 3,
-                        attributes);
-            } catch (PKCS11Exception e) {
-                throw new InvalidKeyException("unwrap() failed", e);
-            } finally {
-                token.releaseSession(s);
-            }
-        }
+
         // XXX implement unwrap using C_Unwrap() for all keys
         implInit(Cipher.DECRYPT_MODE, p11Key);
         if (wrappedKey.length > maxInputSize) {
--- a/src/share/classes/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, 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
@@ -88,23 +88,33 @@
             throw new IllegalStateException
                         ("TlsRsaPremasterSecretGenerator must be initialized");
         }
-        CK_VERSION version =
-                new CK_VERSION(spec.getMajorVersion(), spec.getMinorVersion());
-        Session session = null;
-        try {
-            session = token.getObjSession();
-            CK_ATTRIBUTE[] attributes = token.getAttributes
-                (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
-            long keyID = token.p11.C_GenerateKey
-                (session.id(), new CK_MECHANISM(mechanism, version), attributes);
-            SecretKey key = P11Key.secretKey
-                (session, keyID, "TlsRsaPremasterSecret", 48 << 3, attributes);
-            return key;
-        } catch (PKCS11Exception e) {
-            throw new ProviderException("Could not generate premaster secret", e);
-        } finally {
-            token.releaseSession(session);
+
+        byte[] b = spec.getEncodedSecret();
+        if (b == null) {
+            CK_VERSION version = new CK_VERSION(
+                        spec.getMajorVersion(), spec.getMinorVersion());
+            Session session = null;
+            try {
+                session = token.getObjSession();
+                CK_ATTRIBUTE[] attributes = token.getAttributes(
+                        O_GENERATE, CKO_SECRET_KEY,
+                        CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
+                long keyID = token.p11.C_GenerateKey(session.id(),
+                        new CK_MECHANISM(mechanism, version), attributes);
+                SecretKey key = P11Key.secretKey(session,
+                        keyID, "TlsRsaPremasterSecret", 48 << 3, attributes);
+                return key;
+            } catch (PKCS11Exception e) {
+                throw new ProviderException(
+                        "Could not generate premaster secret", e);
+            } finally {
+                token.releaseSession(session);
+            }
         }
+
+        // Won't worry, the TlsRsaPremasterSecret will be soon converted to
+        // TlsMasterSecret.
+        return new SecretKeySpec(b, "TlsRsaPremasterSecret");
     }
 
 }
--- a/src/share/classes/sun/security/rsa/RSAPadding.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/sun/security/rsa/RSAPadding.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -318,33 +318,53 @@
 
     /**
      * PKCS#1 v1.5 unpadding (blocktype 1 and 2).
+     *
+     * Note that we want to make it a constant-time operation
      */
     private byte[] unpadV15(byte[] padded) throws BadPaddingException {
         int k = 0;
+        BadPaddingException bpe = null;
+
         if (padded[k++] != 0) {
-            throw new BadPaddingException("Data must start with zero");
+            bpe = new BadPaddingException("Data must start with zero");
         }
-        if (padded[k++] != type) {
-            throw new BadPaddingException("Blocktype mismatch: " + padded[1]);
+        if (padded[k++] != type && bpe == null) {
+            bpe = new BadPaddingException("Blocktype mismatch: " + padded[1]);
         }
-        while (true) {
+        int p = 0;
+        while (k < padded.length) {
             int b = padded[k++] & 0xff;
-            if (b == 0) {
-                break;
+            if (b == 0 && p == 0) {
+                p = k;
             }
-            if (k == padded.length) {
-                throw new BadPaddingException("Padding string not terminated");
+            if (k == padded.length && p == 0 && bpe == null) {
+                bpe = new BadPaddingException("Padding string not terminated");
             }
-            if ((type == PAD_BLOCKTYPE_1) && (b != 0xff)) {
-                throw new BadPaddingException("Padding byte not 0xff: " + b);
+            if ((type == PAD_BLOCKTYPE_1) && (b != 0xff) &&
+                    p == 0 && bpe == null) {
+                bpe = new BadPaddingException("Padding byte not 0xff: " + b);
             }
         }
-        int n = padded.length - k;
-        if (n > maxDataSize) {
-            throw new BadPaddingException("Padding string too short");
+        int n = padded.length - p;
+        if (n > maxDataSize && bpe == null) {
+            bpe = new BadPaddingException("Padding string too short");
         }
+
+        // copy useless padding array for a constant-time method
+        //
+        // Is it necessary?
+        byte[] padding = new byte[p];
+        System.arraycopy(padded, 0, padding, 0, p);
+
         byte[] data = new byte[n];
-        System.arraycopy(padded, padded.length - n, data, 0, n);
+        System.arraycopy(padded, p, data, 0, n);
+
+        if (bpe == null) {
+            bpe = new BadPaddingException("Unused exception");
+        } else {
+            throw bpe;
+        }
+
         return data;
     }
 
--- a/src/share/classes/sun/security/ssl/Handshaker.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/sun/security/ssl/Handshaker.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, 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
@@ -1047,94 +1047,22 @@
                 clnt_random.random_bytes, svr_random.random_bytes,
                 prfHashAlg, prfHashLength, prfBlockSize);
 
-        SecretKey masterSecret;
         try {
             KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
             kg.init(spec);
-            masterSecret = kg.generateKey();
-        } catch (GeneralSecurityException e) {
+            return kg.generateKey();
+        } catch (InvalidAlgorithmParameterException |
+                NoSuchAlgorithmException iae) {
+            // unlikely to happen, otherwise, must be a provider exception
+            //
             // For RSA premaster secrets, do not signal a protocol error
             // due to the Bleichenbacher attack. See comments further down.
-            if (!preMasterSecret.getAlgorithm().equals(
-                    "TlsRsaPremasterSecret")) {
-                throw new ProviderException(e);
-            }
-
             if (debug != null && Debug.isOn("handshake")) {
                 System.out.println("RSA master secret generation error:");
-                e.printStackTrace(System.out);
-            }
-
-            if (requestedVersion != null) {
-                preMasterSecret =
-                    RSAClientKeyExchange.generateDummySecret(requestedVersion);
-            } else {
-                preMasterSecret =
-                    RSAClientKeyExchange.generateDummySecret(protocolVersion);
+                iae.printStackTrace(System.out);
             }
-
-            // recursive call with new premaster secret
-            return calculateMasterSecret(preMasterSecret, null);
-        }
-
-        // if no version check requested (client side handshake), or version
-        // information is not available (not an RSA premaster secret),
-        // return master secret immediately.
-        if ((requestedVersion == null) ||
-                !(masterSecret instanceof TlsMasterSecret)) {
-            return masterSecret;
-        }
-
-        // we have checked the ClientKeyExchange message when reading TLS
-        // record, the following check is necessary to ensure that
-        // JCE provider does not ignore the checking, or the previous
-        // checking process bypassed the premaster secret version checking.
-        TlsMasterSecret tlsKey = (TlsMasterSecret)masterSecret;
-        int major = tlsKey.getMajorVersion();
-        int minor = tlsKey.getMinorVersion();
-        if ((major < 0) || (minor < 0)) {
-            return masterSecret;
+            throw new ProviderException(iae);
         }
-
-        // check if the premaster secret version is ok
-        // the specification says that it must be the maximum version supported
-        // by the client from its ClientHello message. However, many
-        // implementations send the negotiated version, so accept both
-        // for SSL v3.0 and TLS v1.0.
-        // NOTE that we may be comparing two unsupported version numbers, which
-        // is why we cannot use object reference equality in this special case.
-        ProtocolVersion premasterVersion =
-                                    ProtocolVersion.valueOf(major, minor);
-        boolean versionMismatch = (premasterVersion.v != requestedVersion.v);
-
-        /*
-         * we never checked the client_version in server side
-         * for TLS v1.0 and SSL v3.0. For compatibility, we
-         * maintain this behavior.
-         */
-        if (versionMismatch && requestedVersion.v <= ProtocolVersion.TLS10.v) {
-            versionMismatch = (premasterVersion.v != protocolVersion.v);
-        }
-
-        if (versionMismatch == false) {
-            // check passed, return key
-            return masterSecret;
-        }
-
-        // Due to the Bleichenbacher attack, do not signal a protocol error.
-        // Generate a random premaster secret and continue with the handshake,
-        // which will fail when verifying the finished messages.
-        // For more information, see comments in PreMasterSecret.
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println("RSA PreMasterSecret version error: expected"
-                + protocolVersion + " or " + requestedVersion + ", decrypted: "
-                + premasterVersion);
-        }
-        preMasterSecret =
-            RSAClientKeyExchange.generateDummySecret(requestedVersion);
-
-        // recursive call with new premaster secret
-        return calculateMasterSecret(preMasterSecret, null);
     }
 
     /*
--- a/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Mon Aug 19 22:43:32 2013 +0800
+++ b/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Thu Sep 05 18:17:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, 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
@@ -135,26 +135,37 @@
         } else {
             encrypted = new byte [messageSize];
             if (input.read(encrypted) != messageSize) {
-                throw new SSLProtocolException
-                        ("SSL: read PreMasterSecret: short read");
+                throw new SSLProtocolException(
+                        "SSL: read PreMasterSecret: short read");
             }
         }
 
+        Exception failover = null;
+        byte[] encoded = null;
         try {
             Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
-            cipher.init(Cipher.UNWRAP_MODE, privateKey);
-            preMaster = (SecretKey)cipher.unwrap(encrypted,
-                                "TlsRsaPremasterSecret", Cipher.SECRET_KEY);
+            // Cannot generate key here, please don't use Cipher.UNWRAP_MODE!
+            cipher.init(Cipher.DECRYPT_MODE, privateKey);
+            encoded = cipher.doFinal(encrypted);
+        } catch (BadPaddingException bpe) {
+            failover = bpe;
+            encoded = null;
+        } catch (IllegalBlockSizeException ibse) {
+            // the message it too big to process with RSA
+            throw new SSLProtocolException(
+                "Unable to process PreMasterSecret, may be too big");
+        } catch (Exception e) {
+            // unlikely to happen, otherwise, must be a provider exception
+            if (debug != null && Debug.isOn("handshake")) {
+                System.out.println("RSA premaster secret decryption error:");
+                e.printStackTrace(System.out);
+            }
+            throw new RuntimeException("Could not generate dummy secret", e);
+        }
 
-            // polish the premaster secret
-            preMaster = polishPreMasterSecretKey(currentVersion, maxVersion,
-                                                generator, preMaster, null);
-        } catch (Exception e) {
-            // polish the premaster secret
-            preMaster =
-                    polishPreMasterSecretKey(currentVersion, maxVersion,
-                                                generator, null, e);
-        }
+        // polish the premaster secret
+        preMaster = polishPreMasterSecretKey(
+                    currentVersion, maxVersion, generator, encoded, failover);
     }
 
     /**
@@ -165,85 +176,74 @@
      *
      * RFC 5246 describes the approach as :
      *
-     *  1. Generate a string R of 46 random bytes
+     *  1. Generate a string R of 48 random bytes
      *
      *  2. Decrypt the message to recover the plaintext M
      *
      *  3. If the PKCS#1 padding is not correct, or the length of message
      *     M is not exactly 48 bytes:
-     *        pre_master_secret = ClientHello.client_version || R
+     *        pre_master_secret = R
      *     else If ClientHello.client_version <= TLS 1.0, and version
      *     number check is explicitly disabled:
-     *        pre_master_secret = M
+     *        premaster secret = M
+     *     else If M[0..1] != ClientHello.client_version:
+     *        premaster secret = R
      *     else:
-     *        pre_master_secret = ClientHello.client_version || M[2..47]
+     *        premaster secret = M
+     *
+     * Note that #2 has completed before the call of this method.
      */
     private SecretKey polishPreMasterSecretKey(ProtocolVersion currentVersion,
             ProtocolVersion clientHelloVersion, SecureRandom generator,
-            SecretKey secretKey, Exception failoverException) {
+            byte[] encoded, Exception failoverException) {
 
         this.protocolVersion = clientHelloVersion;
+        if (generator == null) {
+            generator = new SecureRandom();
+        }
+        byte[] random = new byte[48];
+        generator.nextBytes(random);
 
-        if (failoverException == null && secretKey != null) {
+        if (failoverException == null && encoded != null) {
             // check the length
-            byte[] encoded = secretKey.getEncoded();
-            if (encoded == null) {      // unable to get the encoded key
+            if (encoded.length != 48) {
                 if (debug != null && Debug.isOn("handshake")) {
                     System.out.println(
-                        "unable to get the plaintext of the premaster secret");
+                        "incorrect length of premaster secret: " +
+                        encoded.length);
                 }
 
-                int keySize = KeyUtil.getKeySize(secretKey);
-                if (keySize > 0 && keySize != 384) {       // 384 = 48 * 8
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println(
-                            "incorrect length of premaster secret: " +
-                            (keySize/8));
-                    }
-
-                    return generateDummySecret(clientHelloVersion);
-                }
+                return generatePreMasterSecret(
+                        clientHelloVersion, random, generator);
+            }
 
-                // The key size is exactly 48 bytes or not accessible.
-                //
-                // Conservatively, pass the checking to master secret
-                // calculation.
-                return secretKey;
-            } else if (encoded.length == 48) {
-                // check the version
-                if (clientHelloVersion.major == encoded[0] &&
-                    clientHelloVersion.minor == encoded[1]) {
+            if (clientHelloVersion.major != encoded[0] ||
+                        clientHelloVersion.minor != encoded[1]) {
 
-                    return secretKey;
-                } else if (clientHelloVersion.v <= ProtocolVersion.TLS10.v &&
-                           currentVersion.major == encoded[0] &&
-                           currentVersion.minor == encoded[1]) {
+                if (clientHelloVersion.v <= ProtocolVersion.TLS10.v &&
+                       currentVersion.major == encoded[0] &&
+                       currentVersion.minor == encoded[1]) {
                     /*
                      * For compatibility, we maintain the behavior that the
                      * version in pre_master_secret can be the negotiated
                      * version for TLS v1.0 and SSL v3.0.
                      */
                     this.protocolVersion = currentVersion;
-                    return secretKey;
-                }
-
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Mismatching Protocol Versions, " +
-                        "ClientHello.client_version is " + clientHelloVersion +
-                        ", while PreMasterSecret.client_version is " +
-                        ProtocolVersion.valueOf(encoded[0], encoded[1]));
-                }
+                } else {
+                    if (debug != null && Debug.isOn("handshake")) {
+                        System.out.println("Mismatching Protocol Versions, " +
+                            "ClientHello.client_version is " +
+                            clientHelloVersion +
+                            ", while PreMasterSecret.client_version is " +
+                            ProtocolVersion.valueOf(encoded[0], encoded[1]));
+                    }
 
-                return generateDummySecret(clientHelloVersion);
-            } else {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println(
-                        "incorrect length of premaster secret: " +
-                        encoded.length);
+                    encoded = random;
                 }
+            }
 
-                return generateDummySecret(clientHelloVersion);
-            }
+            return generatePreMasterSecret(
+                    clientHelloVersion, encoded, generator);
         }
 
         if (debug != null && Debug.isOn("handshake") &&
@@ -252,11 +252,14 @@
             failoverException.printStackTrace(System.out);
         }
 
-        return generateDummySecret(clientHelloVersion);
+        return generatePreMasterSecret(clientHelloVersion, random, generator);
     }
 
     // generate a premaster secret with the specified version number
-    static SecretKey generateDummySecret(ProtocolVersion version) {
+    private static SecretKey generatePreMasterSecret(
+            ProtocolVersion version, byte[] encodedSecret,
+            SecureRandom generator) {
+
         if (debug != null && Debug.isOn("handshake")) {
             System.out.println("Generating a random fake premaster secret");
         }
@@ -265,11 +268,17 @@
             String s = ((version.v >= ProtocolVersion.TLS12.v) ?
                 "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
             KeyGenerator kg = JsseJce.getKeyGenerator(s);
-            kg.init(new TlsRsaPremasterSecretParameterSpec
-                    (version.major, version.minor));
+            kg.init(new TlsRsaPremasterSecretParameterSpec(
+                    version.major, version.minor, encodedSecret), generator);
             return kg.generateKey();
-        } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate dummy secret", e);
+        } catch (InvalidAlgorithmParameterException |
+                NoSuchAlgorithmException iae) {
+            // unlikely to happen, otherwise, must be a provider exception
+            if (debug != null && Debug.isOn("handshake")) {
+                System.out.println("RSA premaster secret generation error:");
+                iae.printStackTrace(System.out);
+            }
+            throw new RuntimeException("Could not generate dummy secret", iae);
         }
     }