--- a/jdk/src/share/classes/sun/security/ssl/Handshaker.java Fri Oct 29 12:35:07 2010 +0200
+++ b/jdk/src/share/classes/sun/security/ssl/Handshaker.java Sat Oct 30 18:39:17 2010 +0800
@@ -71,11 +71,31 @@
byte[] clientVerifyData;
byte[] serverVerifyData;
- // is it an initial negotiation or a renegotiation?
+ // Is it an initial negotiation or a renegotiation?
boolean isInitialHandshake;
- // list of enabled protocols
- ProtocolList enabledProtocols;
+ // List of enabled protocols
+ private ProtocolList enabledProtocols;
+
+ // List of enabled CipherSuites
+ private CipherSuiteList enabledCipherSuites;
+
+ /*
+ * List of active protocols
+ *
+ * Active protocols is a subset of enabled protocols, and will
+ * contain only those protocols that have vaild cipher suites
+ * enabled.
+ */
+ private ProtocolList activeProtocols;
+
+ /*
+ * List of active cipher suites
+ *
+ * Active cipher suites is a subset of enabled cipher suites, and will
+ * contain only those cipher suites available for the active protocols.
+ */
+ private CipherSuiteList activeCipherSuites;
private boolean isClient;
@@ -94,9 +114,6 @@
// in reset state after use.
private MessageDigest md5Tmp, shaTmp;
- // list of enabled CipherSuites
- CipherSuiteList enabledCipherSuites;
-
// current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
CipherSuite cipherSuite;
@@ -233,7 +250,7 @@
// client's cert verify, those constants are in a convenient
// order to drastically simplify state machine checking.
//
- state = -1;
+ state = -2; // initialized but not activated
}
/*
@@ -345,26 +362,69 @@
void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
setVersionSE(protocolVersion);
+
output.r.setVersion(protocolVersion);
}
/**
* Set the enabled protocols. Called from the constructor or
- * SSLSocketImpl.setEnabledProtocols() (if the handshake is not yet
- * in progress).
+ * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the
+ * handshake is not yet in progress).
*/
void setEnabledProtocols(ProtocolList enabledProtocols) {
+ activeCipherSuites = null;
+ activeProtocols = null;
+
this.enabledProtocols = enabledProtocols;
+ }
+
+ /**
+ * Set the enabled cipher suites. Called from
+ * SSLSocketImpl/SSLEngineImpl.setEnabledCipherSuites() (if the
+ * handshake is not yet in progress).
+ */
+ void setEnabledCipherSuites(CipherSuiteList enabledCipherSuites) {
+ activeCipherSuites = null;
+ activeProtocols = null;
+ this.enabledCipherSuites = enabledCipherSuites;
+ }
+
+
+ /**
+ * Prior to handshaking, activate the handshake and initialize the version,
+ * input stream and output stream.
+ */
+ void activate(ProtocolVersion helloVersion) throws IOException {
+ if (activeProtocols == null) {
+ activeProtocols = getActiveProtocols();
+ }
+
+ if (activeProtocols.collection().isEmpty() ||
+ activeProtocols.max.v == ProtocolVersion.NONE.v) {
+ throw new SSLHandshakeException("No appropriate protocol");
+ }
+
+ if (activeCipherSuites == null) {
+ activeCipherSuites = getActiveCipherSuites();
+ }
+
+ if (activeCipherSuites.collection().isEmpty()) {
+ throw new SSLHandshakeException("No appropriate cipher suite");
+ }
// temporary protocol version until the actual protocol version
// is negotiated in the Hello exchange. This affects the record
- // version we sent with the ClientHello. Using max() as the record
- // version is not really correct but some implementations fail to
- // correctly negotiate TLS otherwise.
- protocolVersion = enabledProtocols.max;
+ // version we sent with the ClientHello.
+ if (!isInitialHandshake) {
+ protocolVersion = activeProtocolVersion;
+ } else {
+ protocolVersion = activeProtocols.max;
+ }
- ProtocolVersion helloVersion = enabledProtocols.helloVersion;
+ if (helloVersion == null || helloVersion.v == ProtocolVersion.NONE.v) {
+ helloVersion = activeProtocols.helloVersion;
+ }
input = new HandshakeInStream(handshakeHash);
@@ -372,12 +432,16 @@
output = new HandshakeOutStream(protocolVersion, helloVersion,
handshakeHash, conn);
conn.getAppInputStream().r.setHelloVersion(helloVersion);
+ conn.getAppOutputStream().r.setHelloVersion(helloVersion);
} else {
output = new HandshakeOutStream(protocolVersion, helloVersion,
handshakeHash, engine);
+ engine.inputRecord.setHelloVersion(helloVersion);
engine.outputRecord.setHelloVersion(helloVersion);
}
+ // move state to activated
+ state = -1;
}
/**
@@ -392,20 +456,127 @@
/**
* Check if the given ciphersuite is enabled and available.
- * (Enabled ciphersuites are always available unless the status has
- * changed due to change in JCE providers since it was enabled).
* Does not check if the required server certificates are available.
*/
boolean isNegotiable(CipherSuite s) {
- return enabledCipherSuites.contains(s) && s.isNegotiable();
+ if (activeCipherSuites == null) {
+ activeCipherSuites = getActiveCipherSuites();
+ }
+
+ return activeCipherSuites.contains(s) && s.isNegotiable();
+ }
+
+ /**
+ * Check if the given protocol version is enabled and available.
+ */
+ boolean isNegotiable(ProtocolVersion protocolVersion) {
+ if (activeProtocols == null) {
+ activeProtocols = getActiveProtocols();
+ }
+
+ return activeProtocols.contains(protocolVersion);
+ }
+
+ /**
+ * Select a protocol version from the list. Called from
+ * ServerHandshaker to negotiate protocol version.
+ *
+ * Return the lower of the protocol version suggested in the
+ * clien hello and the highest supported by the server.
+ */
+ ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
+ if (activeProtocols == null) {
+ activeProtocols = getActiveProtocols();
+ }
+
+ return activeProtocols.selectProtocolVersion(protocolVersion);
}
/**
- * As long as handshaking has not started, we can
+ * Get the active cipher suites.
+ *
+ * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted,
+ * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT
+ * negotiate these cipher suites in TLS 1.1 or later mode.
+ *
+ * Therefore, when the active protocols only include TLS 1.1 or later,
+ * the client cannot request to negotiate those obsoleted cipher
+ * suites, that's, the obsoleted suites should not be included in the
+ * client hello. So we need to create a subset of the enabled cipher
+ * suites, the active cipher suites, which does not contain obsoleted
+ * cipher suites of the minimum active protocol.
+ *
+ * Return empty list instead of null if no active cipher suites.
+ */
+ CipherSuiteList getActiveCipherSuites() {
+ if (activeCipherSuites == null) {
+ if (activeProtocols == null) {
+ activeProtocols = getActiveProtocols();
+ }
+
+ ArrayList<CipherSuite> suites = new ArrayList<CipherSuite>();
+ if (!(activeProtocols.collection().isEmpty()) &&
+ activeProtocols.min.v != ProtocolVersion.NONE.v) {
+ for (CipherSuite suite : enabledCipherSuites.collection()) {
+ if (suite.obsoleted > activeProtocols.min.v) {
+ suites.add(suite);
+ } else if (debug != null && Debug.isOn("handshake")) {
+ System.out.println(
+ "Ignoring obsoleted cipher suite: " + suite);
+ }
+ }
+ }
+ activeCipherSuites = new CipherSuiteList(suites);
+ }
+
+ return activeCipherSuites;
+ }
+
+ /*
+ * Get the active protocol versions.
+ *
+ * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted,
+ * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT
+ * negotiate these cipher suites in TLS 1.1 or later mode.
+ *
+ * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the
+ * only enabled cipher suite, the client cannot request TLS 1.1 or
+ * later, even though TLS 1.1 or later is enabled. We need to create a
+ * subset of the enabled protocols, called the active protocols, which
+ * contains protocols appropriate to the list of enabled Ciphersuites.
+ *
+ * Return empty list instead of null if no active protocol versions.
+ */
+ ProtocolList getActiveProtocols() {
+ if (activeProtocols == null) {
+ ArrayList<ProtocolVersion> protocols =
+ new ArrayList<ProtocolVersion>(3);
+ for (ProtocolVersion protocol : enabledProtocols.collection()) {
+ boolean found = false;
+ for (CipherSuite suite : enabledCipherSuites.collection()) {
+ if (suite.isAvailable() && suite.obsoleted > protocol.v) {
+ protocols.add(protocol);
+ found = true;
+ break;
+ }
+ }
+ if (!found && (debug != null) && Debug.isOn("handshake")) {
+ System.out.println(
+ "No available cipher suite for " + protocol);
+ }
+ }
+ activeProtocols = new ProtocolList(protocols);
+ }
+
+ return activeProtocols;
+ }
+
+ /**
+ * As long as handshaking has not activated, we can
* change whether session creations are allowed.
*
* Callers should do their own checking if handshaking
- * has started.
+ * has activated.
*/
void setEnableSessionCreation(boolean newSessions) {
enableNewSession = newSessions;
@@ -419,12 +590,12 @@
CipherBox box;
if (isClient) {
box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV,
- false);
+ sslContext.getSecureRandom(), false);
svrWriteKey = null;
svrWriteIV = null;
} else {
box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV,
- false);
+ sslContext.getSecureRandom(), false);
clntWriteKey = null;
clntWriteIV = null;
}
@@ -439,12 +610,12 @@
CipherBox box;
if (isClient) {
box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV,
- true);
+ sslContext.getSecureRandom(), true);
clntWriteKey = null;
clntWriteIV = null;
} else {
box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV,
- true);
+ sslContext.getSecureRandom(), true);
svrWriteKey = null;
svrWriteIV = null;
}
@@ -614,13 +785,20 @@
/**
+ * Returns true iff the handshaker has been activated.
+ *
+ * In activated state, the handshaker may not send any messages out.
+ */
+ boolean activated() {
+ return state >= -1;
+ }
+
+ /**
* Returns true iff the handshaker has sent any messages.
- * Server kickstarting is not as neat as it should be; we
- * need to create a new handshaker, this method lets us
- * know if we should.
*/
boolean started() {
- return state >= 0;
+ return state >= 0; // 0: HandshakeMessage.ht_hello_request
+ // 1: HandshakeMessage.ht_hello_request
}
@@ -633,6 +811,7 @@
if (state >= 0) {
return;
}
+
HandshakeMessage m = getKickstartMessage();
if (debug != null && Debug.isOn("handshake")) {
@@ -746,6 +925,7 @@
*/
private SecretKey calculateMasterSecret(SecretKey preMasterSecret,
ProtocolVersion requestedVersion) {
+
TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec
(preMasterSecret, protocolVersion.major, protocolVersion.minor,
clnt_random.random_bytes, svr_random.random_bytes);
@@ -773,22 +953,37 @@
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);
System.out.println("Generating new random premaster secret");
}
- preMasterSecret = RSAClientKeyExchange.generateDummySecret(protocolVersion);
+
+ if (requestedVersion != null) {
+ preMasterSecret =
+ RSAClientKeyExchange.generateDummySecret(requestedVersion);
+ } else {
+ preMasterSecret =
+ RSAClientKeyExchange.generateDummySecret(protocolVersion);
+ }
+
// 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),
+ // 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)) {
+ 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();
@@ -800,13 +995,21 @@
// 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
- // NOTE that we may be comparing two unsupported version numbers in
- // the second case, which is why we cannot use object reference
- // equality in this special case
- ProtocolVersion premasterVersion = ProtocolVersion.valueOf(major, minor);
- boolean versionMismatch = (premasterVersion != protocolVersion) &&
- (premasterVersion.v != requestedVersion.v);
+ // 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
@@ -823,7 +1026,9 @@
+ premasterVersion);
System.out.println("Generating new random premaster secret");
}
- preMasterSecret = RSAClientKeyExchange.generateDummySecret(protocolVersion);
+ preMasterSecret =
+ RSAClientKeyExchange.generateDummySecret(requestedVersion);
+
// recursive call with new premaster secret
return calculateMasterSecret(preMasterSecret, null);
}
@@ -849,8 +1054,6 @@
int hashSize = cipherSuite.macAlg.size;
boolean is_exportable = cipherSuite.exportable;
BulkCipher cipher = cipherSuite.cipher;
- int keySize = cipher.keySize;
- int ivSize = cipher.ivSize;
int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0;
TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec
@@ -867,6 +1070,8 @@
clntWriteKey = keySpec.getClientCipherKey();
svrWriteKey = keySpec.getServerCipherKey();
+ // Return null if IVs are not supposed to be generated.
+ // e.g. TLS 1.1+.
clntWriteIV = keySpec.getClientIv();
svrWriteIV = keySpec.getServerIv();
@@ -914,7 +1119,12 @@
System.out.println("Server write IV:");
printHex(dump, svrWriteIV.getIV());
} else {
- System.out.println("... no IV used for this cipher");
+ if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ System.out.println(
+ "... no IV derived for this protocol");
+ } else {
+ System.out.println("... no IV used for this cipher");
+ }
}
System.out.flush();
}