# HG changeset patch # User xuelei # Date 1276328571 25200 # Node ID 533f4ad71f886a5522ac7c80fe6db886191cb081 # Parent e565c553e9fc6cfea97b602c4c79336d804f6390 6914943: Implement final TLS renegotiation fix Summary: RFC 5746 implementation Reviewed-by: wetmore, weijun diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/Alerts.java --- a/jdk/src/share/classes/sun/security/ssl/Alerts.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/Alerts.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -74,7 +74,7 @@ static final byte alert_insufficient_security = 71; static final byte alert_internal_error = 80; static final byte alert_user_canceled = 90; - static final byte alert_no_negotiation = 100; + static final byte alert_no_renegotiation = 100; // from RFC 3546 (TLS Extensions) static final byte alert_unsupported_extension = 110; @@ -132,8 +132,8 @@ return "internal_error"; case alert_user_canceled: return "user_canceled"; - case alert_no_negotiation: - return "no_negotiation"; + case alert_no_renegotiation: + return "no_renegotiation"; case alert_unsupported_extension: return "unsupported_extension"; case alert_certificate_unobtainable: @@ -203,7 +203,7 @@ case alert_protocol_version: case alert_internal_error: case alert_user_canceled: - case alert_no_negotiation: + case alert_no_renegotiation: default: e = new SSLException(reason); break; diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/CipherSuite.java --- a/jdk/src/share/classes/sun/security/ssl/CipherSuite.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/CipherSuite.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -126,6 +126,8 @@ macAlg = M_SHA; } else if (name.endsWith("_NULL")) { macAlg = M_NULL; + } else if (name.endsWith("_SCSV")) { + macAlg = M_NULL; } else { throw new IllegalArgumentException ("Unknown MAC algorithm for ciphersuite " + name); @@ -160,6 +162,10 @@ return allowed && keyExchange.isAvailable() && cipher.isAvailable(); } + boolean isNegotiable() { + return this != C_SCSV && isAvailable(); + } + /** * Compares CipherSuites based on their priority. Has the effect of * sorting CipherSuites when put in a sorted collection, which is @@ -268,7 +274,10 @@ // Kerberos cipher suites K_KRB5 ("KRB5", true), - K_KRB5_EXPORT("KRB5_EXPORT", true); + K_KRB5_EXPORT("KRB5_EXPORT", true), + + // renegotiation protection request signaling cipher suite + K_SCSV ("SCSV", true); // name of the key exchange algorithm, e.g. DHE_DSS final String name; @@ -352,7 +361,8 @@ this.exportable = true; } - BulkCipher(String transformation, int keySize, int ivSize, boolean allowed) { + BulkCipher(String transformation, int keySize, + int ivSize, boolean allowed) { this.transformation = transformation; this.algorithm = transformation.split("/")[0]; this.description = this.algorithm + "/" + (keySize << 3); @@ -370,7 +380,8 @@ * * @exception NoSuchAlgorithmException if anything goes wrong */ - CipherBox newCipher(ProtocolVersion version, SecretKey key, IvParameterSpec iv, + CipherBox newCipher(ProtocolVersion version, + SecretKey key, IvParameterSpec iv, boolean encrypt) throws NoSuchAlgorithmException { return CipherBox.newCipherBox(version, this, key, iv, encrypt); } @@ -407,8 +418,9 @@ if (b == null) { try { SecretKey key = new SecretKeySpec - (new byte[cipher.expandedKeySize], cipher.algorithm); - IvParameterSpec iv = new IvParameterSpec(new byte[cipher.ivSize]); + (new byte[cipher.expandedKeySize], cipher.algorithm); + IvParameterSpec iv = + new IvParameterSpec(new byte[cipher.ivSize]); cipher.newCipher(ProtocolVersion.DEFAULT, key, iv, true); b = Boolean.TRUE; } catch (NoSuchAlgorithmException e) { @@ -460,18 +472,28 @@ } // export strength ciphers - final static BulkCipher B_NULL = new BulkCipher("NULL", 0, 0, 0, true); - final static BulkCipher B_RC4_40 = new BulkCipher(CIPHER_RC4, 5, 16, 0, true); - final static BulkCipher B_RC2_40 = new BulkCipher("RC2", 5, 16, 8, false); - final static BulkCipher B_DES_40 = new BulkCipher(CIPHER_DES, 5, 8, 8, true); + final static BulkCipher B_NULL = + new BulkCipher("NULL", 0, 0, 0, true); + final static BulkCipher B_RC4_40 = + new BulkCipher(CIPHER_RC4, 5, 16, 0, true); + final static BulkCipher B_RC2_40 = + new BulkCipher("RC2", 5, 16, 8, false); + final static BulkCipher B_DES_40 = + new BulkCipher(CIPHER_DES, 5, 8, 8, true); // domestic strength ciphers - final static BulkCipher B_RC4_128 = new BulkCipher(CIPHER_RC4, 16, 0, true); - final static BulkCipher B_DES = new BulkCipher(CIPHER_DES, 8, 8, true); - final static BulkCipher B_3DES = new BulkCipher(CIPHER_3DES, 24, 8, true); - final static BulkCipher B_IDEA = new BulkCipher("IDEA", 16, 8, false); - final static BulkCipher B_AES_128 = new BulkCipher(CIPHER_AES, 16, 16, true); - final static BulkCipher B_AES_256 = new BulkCipher(CIPHER_AES, 32, 16, true); + final static BulkCipher B_RC4_128 = + new BulkCipher(CIPHER_RC4, 16, 0, true); + final static BulkCipher B_DES = + new BulkCipher(CIPHER_DES, 8, 8, true); + final static BulkCipher B_3DES = + new BulkCipher(CIPHER_3DES, 24, 8, true); + final static BulkCipher B_IDEA = + new BulkCipher("IDEA", 16, 8, false); + final static BulkCipher B_AES_128 = + new BulkCipher(CIPHER_AES, 16, 16, true); + final static BulkCipher B_AES_256 = + new BulkCipher(CIPHER_AES, 32, 16, true); // MACs final static MacAlg M_NULL = new MacAlg("NULL", 0); @@ -487,93 +509,159 @@ // N: ciphersuites only allowed if we are not in FIPS mode final boolean N = (SunJSSE.isFIPS() == false); -add("SSL_NULL_WITH_NULL_NULL", 0x0000, 1, K_NULL, B_NULL, F); + add("SSL_NULL_WITH_NULL_NULL", + 0x0000, 1, K_NULL, B_NULL, F); // Definition of the CipherSuites that are enabled by default. // They are listed in preference order, most preferred first. int p = DEFAULT_SUITES_PRIORITY * 2; -add("SSL_RSA_WITH_RC4_128_MD5", 0x0004, --p, K_RSA, B_RC4_128, N); -add("SSL_RSA_WITH_RC4_128_SHA", 0x0005, --p, K_RSA, B_RC4_128, N); -add("TLS_RSA_WITH_AES_128_CBC_SHA", 0x002f, --p, K_RSA, B_AES_128, T); -add("TLS_RSA_WITH_AES_256_CBC_SHA", 0x0035, --p, K_RSA, B_AES_256, T); + add("SSL_RSA_WITH_RC4_128_MD5", + 0x0004, --p, K_RSA, B_RC4_128, N); + add("SSL_RSA_WITH_RC4_128_SHA", + 0x0005, --p, K_RSA, B_RC4_128, N); + add("TLS_RSA_WITH_AES_128_CBC_SHA", + 0x002f, --p, K_RSA, B_AES_128, T); + add("TLS_RSA_WITH_AES_256_CBC_SHA", + 0x0035, --p, K_RSA, B_AES_256, T); -add("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", 0xC002, --p, K_ECDH_ECDSA, B_RC4_128, N); -add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", 0xC004, --p, K_ECDH_ECDSA, B_AES_128, T); -add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", 0xC005, --p, K_ECDH_ECDSA, B_AES_256, T); -add("TLS_ECDH_RSA_WITH_RC4_128_SHA", 0xC00C, --p, K_ECDH_RSA, B_RC4_128, N); -add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", 0xC00E, --p, K_ECDH_RSA, B_AES_128, T); -add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", 0xC00F, --p, K_ECDH_RSA, B_AES_256, T); + add("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", + 0xC002, --p, K_ECDH_ECDSA, B_RC4_128, N); + add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", + 0xC004, --p, K_ECDH_ECDSA, B_AES_128, T); + add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", + 0xC005, --p, K_ECDH_ECDSA, B_AES_256, T); + add("TLS_ECDH_RSA_WITH_RC4_128_SHA", + 0xC00C, --p, K_ECDH_RSA, B_RC4_128, N); + add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", + 0xC00E, --p, K_ECDH_RSA, B_AES_128, T); + add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", + 0xC00F, --p, K_ECDH_RSA, B_AES_256, T); -add("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", 0xC007, --p, K_ECDHE_ECDSA,B_RC4_128, N); -add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 0xC009, --p, K_ECDHE_ECDSA,B_AES_128, T); -add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 0xC00A, --p, K_ECDHE_ECDSA,B_AES_256, T); -add("TLS_ECDHE_RSA_WITH_RC4_128_SHA", 0xC011, --p, K_ECDHE_RSA, B_RC4_128, N); -add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 0xC013, --p, K_ECDHE_RSA, B_AES_128, T); -add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 0xC014, --p, K_ECDHE_RSA, B_AES_256, T); + add("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + 0xC007, --p, K_ECDHE_ECDSA,B_RC4_128, N); + add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + 0xC009, --p, K_ECDHE_ECDSA,B_AES_128, T); + add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + 0xC00A, --p, K_ECDHE_ECDSA,B_AES_256, T); + add("TLS_ECDHE_RSA_WITH_RC4_128_SHA", + 0xC011, --p, K_ECDHE_RSA, B_RC4_128, N); + add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + 0xC013, --p, K_ECDHE_RSA, B_AES_128, T); + add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + 0xC014, --p, K_ECDHE_RSA, B_AES_256, T); -add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", 0x0033, --p, K_DHE_RSA, B_AES_128, T); -add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", 0x0039, --p, K_DHE_RSA, B_AES_256, T); -add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", 0x0032, --p, K_DHE_DSS, B_AES_128, T); -add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", 0x0038, --p, K_DHE_DSS, B_AES_256, T); + add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + 0x0033, --p, K_DHE_RSA, B_AES_128, T); + add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + 0x0039, --p, K_DHE_RSA, B_AES_256, T); + add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + 0x0032, --p, K_DHE_DSS, B_AES_128, T); + add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + 0x0038, --p, K_DHE_DSS, B_AES_256, T); -add("SSL_RSA_WITH_3DES_EDE_CBC_SHA", 0x000a, --p, K_RSA, B_3DES, T); -add("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", 0xC003, --p, K_ECDH_ECDSA, B_3DES, T); -add("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", 0xC00D, --p, K_ECDH_RSA, B_3DES, T); -add("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", 0xC008, --p, K_ECDHE_ECDSA,B_3DES, T); -add("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", 0xC012, --p, K_ECDHE_RSA, B_3DES, T); -add("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", 0x0016, --p, K_DHE_RSA, B_3DES, T); -add("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", 0x0013, --p, K_DHE_DSS, B_3DES, N); + add("SSL_RSA_WITH_3DES_EDE_CBC_SHA", + 0x000a, --p, K_RSA, B_3DES, T); + add("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", + 0xC003, --p, K_ECDH_ECDSA, B_3DES, T); + add("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", + 0xC00D, --p, K_ECDH_RSA, B_3DES, T); + add("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + 0xC008, --p, K_ECDHE_ECDSA,B_3DES, T); + add("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + 0xC012, --p, K_ECDHE_RSA, B_3DES, T); + add("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + 0x0016, --p, K_DHE_RSA, B_3DES, T); + add("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + 0x0013, --p, K_DHE_DSS, B_3DES, N); -add("SSL_RSA_WITH_DES_CBC_SHA", 0x0009, --p, K_RSA, B_DES, N); -add("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0015, --p, K_DHE_RSA, B_DES, N); -add("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0012, --p, K_DHE_DSS, B_DES, N); -add("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0003, --p, K_RSA_EXPORT, B_RC4_40, N); -add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0008, --p, K_RSA_EXPORT, B_DES_40, N); -add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0014, --p, K_DHE_RSA, B_DES_40, N); -add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0011, --p, K_DHE_DSS, B_DES_40, N); + add("SSL_RSA_WITH_DES_CBC_SHA", + 0x0009, --p, K_RSA, B_DES, N); + add("SSL_DHE_RSA_WITH_DES_CBC_SHA", + 0x0015, --p, K_DHE_RSA, B_DES, N); + add("SSL_DHE_DSS_WITH_DES_CBC_SHA", + 0x0012, --p, K_DHE_DSS, B_DES, N); + add("SSL_RSA_EXPORT_WITH_RC4_40_MD5", + 0x0003, --p, K_RSA_EXPORT, B_RC4_40, N); + add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", + 0x0008, --p, K_RSA_EXPORT, B_DES_40, N); + add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + 0x0014, --p, K_DHE_RSA, B_DES_40, N); + add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + 0x0011, --p, K_DHE_DSS, B_DES_40, N); + + // Renegotiation protection request Signalling Cipher Suite Value (SCSV) + add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", + 0x00ff, --p, K_SCSV, B_NULL, T); // Definition of the CipherSuites that are supported but not enabled // by default. // They are listed in preference order, preferred first. p = DEFAULT_SUITES_PRIORITY; -// Anonymous key exchange and the NULL ciphers -add("SSL_RSA_WITH_NULL_MD5", 0x0001, --p, K_RSA, B_NULL, N); -add("SSL_RSA_WITH_NULL_SHA", 0x0002, --p, K_RSA, B_NULL, N); -add("TLS_ECDH_ECDSA_WITH_NULL_SHA", 0xC001, --p, K_ECDH_ECDSA, B_NULL, N); -add("TLS_ECDH_RSA_WITH_NULL_SHA", 0xC00B, --p, K_ECDH_RSA, B_NULL, N); -add("TLS_ECDHE_ECDSA_WITH_NULL_SHA", 0xC006, --p, K_ECDHE_ECDSA,B_NULL, N); -add("TLS_ECDHE_RSA_WITH_NULL_SHA", 0xC010, --p, K_ECDHE_RSA, B_NULL, N); + // Anonymous key exchange and the NULL ciphers + add("SSL_RSA_WITH_NULL_MD5", + 0x0001, --p, K_RSA, B_NULL, N); + add("SSL_RSA_WITH_NULL_SHA", + 0x0002, --p, K_RSA, B_NULL, N); + add("TLS_ECDH_ECDSA_WITH_NULL_SHA", + 0xC001, --p, K_ECDH_ECDSA, B_NULL, N); + add("TLS_ECDH_RSA_WITH_NULL_SHA", + 0xC00B, --p, K_ECDH_RSA, B_NULL, N); + add("TLS_ECDHE_ECDSA_WITH_NULL_SHA", + 0xC006, --p, K_ECDHE_ECDSA,B_NULL, N); + add("TLS_ECDHE_RSA_WITH_NULL_SHA", + 0xC010, --p, K_ECDHE_RSA, B_NULL, N); -add("SSL_DH_anon_WITH_RC4_128_MD5", 0x0018, --p, K_DH_ANON, B_RC4_128, N); -add("TLS_DH_anon_WITH_AES_128_CBC_SHA", 0x0034, --p, K_DH_ANON, B_AES_128, N); -add("TLS_DH_anon_WITH_AES_256_CBC_SHA", 0x003a, --p, K_DH_ANON, B_AES_256, N); -add("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", 0x001b, --p, K_DH_ANON, B_3DES, N); -add("SSL_DH_anon_WITH_DES_CBC_SHA", 0x001a, --p, K_DH_ANON, B_DES, N); + add("SSL_DH_anon_WITH_RC4_128_MD5", + 0x0018, --p, K_DH_ANON, B_RC4_128, N); + add("TLS_DH_anon_WITH_AES_128_CBC_SHA", + 0x0034, --p, K_DH_ANON, B_AES_128, N); + add("TLS_DH_anon_WITH_AES_256_CBC_SHA", + 0x003a, --p, K_DH_ANON, B_AES_256, N); + add("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", + 0x001b, --p, K_DH_ANON, B_3DES, N); + add("SSL_DH_anon_WITH_DES_CBC_SHA", + 0x001a, --p, K_DH_ANON, B_DES, N); -add("TLS_ECDH_anon_WITH_RC4_128_SHA", 0xC016, --p, K_ECDH_ANON, B_RC4_128, N); -add("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", 0xC018, --p, K_ECDH_ANON, B_AES_128, T); -add("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", 0xC019, --p, K_ECDH_ANON, B_AES_256, T); -add("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", 0xC017, --p, K_ECDH_ANON, B_3DES, T); + add("TLS_ECDH_anon_WITH_RC4_128_SHA", + 0xC016, --p, K_ECDH_ANON, B_RC4_128, N); + add("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", + 0xC018, --p, K_ECDH_ANON, B_AES_128, T); + add("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", + 0xC019, --p, K_ECDH_ANON, B_AES_256, T); + add("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", + 0xC017, --p, K_ECDH_ANON, B_3DES, T); -add("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0017, --p, K_DH_ANON, B_RC4_40, N); -add("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0019, --p, K_DH_ANON, B_DES_40, N); + add("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", + 0x0017, --p, K_DH_ANON, B_RC4_40, N); + add("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + 0x0019, --p, K_DH_ANON, B_DES_40, N); -add("TLS_ECDH_anon_WITH_NULL_SHA", 0xC015, --p, K_ECDH_ANON, B_NULL, N); + add("TLS_ECDH_anon_WITH_NULL_SHA", + 0xC015, --p, K_ECDH_ANON, B_NULL, N); -// Supported Kerberos ciphersuites from RFC2712 -add("TLS_KRB5_WITH_RC4_128_SHA", 0x0020, --p, K_KRB5, B_RC4_128, N); -add("TLS_KRB5_WITH_RC4_128_MD5", 0x0024, --p, K_KRB5, B_RC4_128, N); -add("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", 0x001f, --p, K_KRB5, B_3DES, N); -add("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", 0x0023, --p, K_KRB5, B_3DES, N); -add("TLS_KRB5_WITH_DES_CBC_SHA", 0x001e, --p, K_KRB5, B_DES, N); -add("TLS_KRB5_WITH_DES_CBC_MD5", 0x0022, --p, K_KRB5, B_DES, N); -add("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0028, --p, K_KRB5_EXPORT, B_RC4_40, N); -add("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x002b, --p, K_KRB5_EXPORT, B_RC4_40, N); -add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0026, --p, K_KRB5_EXPORT, B_DES_40, N); -add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0029, --p, K_KRB5_EXPORT, B_DES_40, N); - + // Supported Kerberos ciphersuites from RFC2712 + add("TLS_KRB5_WITH_RC4_128_SHA", + 0x0020, --p, K_KRB5, B_RC4_128, N); + add("TLS_KRB5_WITH_RC4_128_MD5", + 0x0024, --p, K_KRB5, B_RC4_128, N); + add("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", + 0x001f, --p, K_KRB5, B_3DES, N); + add("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", + 0x0023, --p, K_KRB5, B_3DES, N); + add("TLS_KRB5_WITH_DES_CBC_SHA", + 0x001e, --p, K_KRB5, B_DES, N); + add("TLS_KRB5_WITH_DES_CBC_MD5", + 0x0022, --p, K_KRB5, B_DES, N); + add("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", + 0x0028, --p, K_KRB5_EXPORT, B_RC4_40, N); + add("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", + 0x002b, --p, K_KRB5_EXPORT, B_RC4_40, N); + add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", + 0x0026, --p, K_KRB5_EXPORT, B_DES_40, N); + add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", + 0x0029, --p, K_KRB5_EXPORT, B_DES_40, N); // Register the names of a few additional CipherSuites. // Makes them show up as names instead of numbers in @@ -618,4 +706,6 @@ // ciphersuite SSL_NULL_WITH_NULL_NULL final static CipherSuite C_NULL = CipherSuite.valueOf(0, 0); + // ciphersuite TLS_EMPTY_RENEGOTIATION_INFO_SCSV + final static CipherSuite C_SCSV = CipherSuite.valueOf(0x00, 0xff); } diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/CipherSuiteList.java --- a/jdk/src/share/classes/sun/security/ssl/CipherSuiteList.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/CipherSuiteList.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -51,8 +51,9 @@ // null if not yet checked. private volatile Boolean containsEC; - // for use by buildAvailableCache() only - private CipherSuiteList(Collection cipherSuites) { + // for use by buildAvailableCache() and + // Handshaker.getKickstartMessage() only + CipherSuiteList(Collection cipherSuites) { this.cipherSuites = cipherSuites; } @@ -221,15 +222,18 @@ // SortedSet automatically arranges ciphersuites in default // preference order Set cipherSuites = new TreeSet(); - Collection allowedCipherSuites = CipherSuite.allowedCipherSuites(); + Collection allowedCipherSuites = + CipherSuite.allowedCipherSuites(); for (CipherSuite c : allowedCipherSuites) { if ((c.allowed == false) || (c.priority < minPriority)) { continue; } + if (c.isAvailable()) { cipherSuites.add(c); } } + return new CipherSuiteList(cipherSuites); } diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java --- a/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -94,16 +94,24 @@ */ ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context, ProtocolList enabledProtocols, - ProtocolVersion activeProtocolVersion) { - super(socket, context, enabledProtocols, true, true); - this.activeProtocolVersion = activeProtocolVersion; + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + super(socket, context, enabledProtocols, true, true, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); } ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context, ProtocolList enabledProtocols, - ProtocolVersion activeProtocolVersion) { - super(engine, context, enabledProtocols, true, true); - this.activeProtocolVersion = activeProtocolVersion; + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + super(engine, context, enabledProtocols, true, true, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); } /* @@ -279,10 +287,11 @@ // sent the "client hello" but the server's not seen it. // if (state < HandshakeMessage.ht_client_hello) { - if (!renegotiable) { // renegotiation is not allowed. + if (!secureRenegotiation && !allowUnsafeRenegotiation) { + // renegotiation is not allowed. if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { - // response with a no_negotiation warning, - warningSE(Alerts.alert_no_negotiation); + // response with a no_renegotiation warning, + warningSE(Alerts.alert_no_renegotiation); // invalidate the handshake so that the caller can // dispose this object. @@ -293,26 +302,24 @@ // and the next handshake message will become incomplete. // // However, according to SSL/TLS specifications, no more - // handshake message could immediately follow ClientHello - // or HelloRequest. But in case of any improper messages, - // we'd better check to ensure there is no remaining bytes - // in the handshake input stream. - if (input.available() > 0) { - fatalSE(Alerts.alert_unexpected_message, - "HelloRequest followed by an unexpected " + - "handshake message"); - } - + // handshake message should immediately follow ClientHello + // or HelloRequest. So just let it be. } else { // For SSLv3, send the handshake_failure fatal error. - // Note that SSLv3 does not define a no_negotiation alert - // like TLSv1. However we cannot ignore the message + // Note that SSLv3 does not define a no_renegotiation + // alert like TLSv1. However we cannot ignore the message // simply, otherwise the other side was waiting for a // response that would never come. fatalSE(Alerts.alert_handshake_failure, - "renegotiation is not allowed"); + "Renegotiation is not allowed"); } } else { + if (!secureRenegotiation) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: continue with insecure renegotiation"); + } + } kickstart(); } } @@ -347,6 +354,68 @@ // Handshake streams setVersion(mesgVersion); + // check the "renegotiation_info" extension + RenegotiationInfoExtension serverHelloRI = (RenegotiationInfoExtension) + mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO); + if (serverHelloRI != null) { + if (isInitialHandshake) { + // verify the length of the "renegotiated_connection" field + if (!serverHelloRI.isEmpty()) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "The renegotiation_info field is not empty"); + } + + secureRenegotiation = true; + } else { + // For a legacy renegotiation, the client MUST verify that + // it does not contain the "renegotiation_info" extension. + if (!secureRenegotiation) { + fatalSE(Alerts.alert_handshake_failure, + "Unexpected renegotiation indication extension"); + } + + // verify the client_verify_data and server_verify_data values + byte[] verifyData = + new byte[clientVerifyData.length + serverVerifyData.length]; + System.arraycopy(clientVerifyData, 0, verifyData, + 0, clientVerifyData.length); + System.arraycopy(serverVerifyData, 0, verifyData, + clientVerifyData.length, serverVerifyData.length); + if (!Arrays.equals(verifyData, + serverHelloRI.getRenegotiatedConnection())) { + fatalSE(Alerts.alert_handshake_failure, + "Incorrect verify data in ServerHello " + + "renegotiation_info message"); + } + } + } else { + // no renegotiation indication extension + if (isInitialHandshake) { + if (!allowLegacyHelloMessages) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "Failed to negotiate the use of secure renegotiation"); + } + + secureRenegotiation = false; + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Warning: No renegotiation " + + "indication extension in ServerHello"); + } + } else { + // For a secure renegotiation, the client must abort the + // handshake if no "renegotiation_info" extension is present. + if (secureRenegotiation) { + fatalSE(Alerts.alert_handshake_failure, + "No renegotiation indication extension"); + } + + // we have already allowed unsafe renegotation before request + // the renegotiation. + } + } + // // Save server nonce, we always use it to compute connection // keys and it's also used to create the master secret if we're @@ -354,10 +423,11 @@ // svr_random = mesg.svr_random; - if (isEnabled(mesg.cipherSuite) == false) { + if (isNegotiable(mesg.cipherSuite) == false) { fatalSE(Alerts.alert_illegal_parameter, - "Server selected disabled ciphersuite " + cipherSuite); + "Server selected improper ciphersuite " + cipherSuite); } + setCipherSuite(mesg.cipherSuite); if (mesg.compression_method != 0) { @@ -452,7 +522,8 @@ for (HelloExtension ext : mesg.extensions.list()) { ExtensionType type = ext.type; if ((type != ExtensionType.EXT_ELLIPTIC_CURVES) - && (type != ExtensionType.EXT_EC_POINT_FORMATS)) { + && (type != ExtensionType.EXT_EC_POINT_FORMATS) + && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) { fatalSE(Alerts.alert_unsupported_extension, "Server sent an unsupported extension: " + type); } @@ -869,6 +940,13 @@ } /* + * save server verify data for secure renegotiation + */ + if (secureRenegotiation) { + serverVerifyData = mesg.getVerifyData(); + } + + /* * OK, it verified. If we're doing the fast handshake, add that * "Finished" message to the hash of handshake messages, then send * our own change_cipher_spec and Finished message for the server @@ -921,6 +999,13 @@ sendChangeCipherSpec(mesg, finishedTag); /* + * save client verify data for secure renegotiation + */ + if (secureRenegotiation) { + clientVerifyData = mesg.getVerifyData(); + } + + /* * Update state machine so server MUST send 'finished' next. * (In "long" handshake case; in short case, we're responding * to its message.) @@ -933,12 +1018,15 @@ * Returns a ClientHello message to kickstart renegotiations */ HandshakeMessage getKickstartMessage() throws SSLException { - ClientHello mesg = new ClientHello(sslContext.getSecureRandom(), - protocolVersion); + // session ID of the ClientHello message + SessionId sessionId = SSLSessionImpl.nullSession.getSessionId(); + + // a list of cipher suites sent by the client + CipherSuiteList cipherSuites = enabledCipherSuites; + + // set the max protocol version this client is supporting. maxProtocolVersion = protocolVersion; - clnt_random = mesg.clnt_random; - // // Try to resume an existing session. This might be mandatory, // given certain API options. @@ -962,9 +1050,9 @@ if (session != null) { CipherSuite sessionSuite = session.getSuite(); ProtocolVersion sessionVersion = session.getProtocolVersion(); - if (isEnabled(sessionSuite) == false) { + if (isNegotiable(sessionSuite) == false) { if (debug != null && Debug.isOn("session")) { - System.out.println("%% can't resume, cipher disabled"); + System.out.println("%% can't resume, unavailable cipher"); } session = null; } @@ -984,9 +1072,8 @@ + " from port " + getLocalPortSE()); } } - mesg.sessionId = session.getSessionId(); - mesg.protocolVersion = sessionVersion; + sessionId = session.getSessionId(); maxProtocolVersion = sessionVersion; // Update SSL version number in underlying SSL socket and @@ -995,33 +1082,78 @@ setVersion(sessionVersion); } - // - // don't say much beyond the obvious if we _must_ resume. - // + /* + * Force use of the previous session ciphersuite, and + * add the SCSV if enabled. + */ if (!enableNewSession) { if (session == null) { throw new SSLException( "Can't reuse existing SSL client session"); } - mesg.setCipherSuites(new CipherSuiteList(sessionSuite)); - return mesg; - } - } - if (session == null) { - if (enableNewSession) { - mesg.sessionId = SSLSessionImpl.nullSession.getSessionId(); - } else { - throw new SSLException("No existing session to resume."); + + Collection cipherList = + new ArrayList(2); + cipherList.add(sessionSuite); + if (!secureRenegotiation && + cipherSuites.contains(CipherSuite.C_SCSV)) { + cipherList.add(CipherSuite.C_SCSV); + } // otherwise, renegotiation_info extension will be used + + cipherSuites = new CipherSuiteList(cipherList); } } - // - // All we have left to do is fill out the cipher suites. - // (If this changes, change the 'return' above!) - // - mesg.setCipherSuites(enabledCipherSuites); + if (session == null && !enableNewSession) { + throw new SSLException("No existing session to resume"); + } + + // exclude SCSV for secure renegotiation + if (secureRenegotiation && cipherSuites.contains(CipherSuite.C_SCSV)) { + Collection cipherList = + new ArrayList(cipherSuites.size() - 1); + for (CipherSuite suite : cipherSuites.collection()) { + if (suite != CipherSuite.C_SCSV) { + cipherList.add(suite); + } + } + + cipherSuites = new CipherSuiteList(cipherList); + } - return mesg; + // make sure there is a negotiable cipher suite. + boolean negotiable = false; + for (CipherSuite suite : cipherSuites.collection()) { + if (isNegotiable(suite)) { + negotiable = true; + break; + } + } + + if (!negotiable) { + throw new SSLException("No negotiable cipher suite"); + } + + // create the ClientHello message + ClientHello clientHelloMessage = new ClientHello( + sslContext.getSecureRandom(), maxProtocolVersion, + sessionId, cipherSuites); + + // reset the client random cookie + clnt_random = clientHelloMessage.clnt_random; + + /* + * need to set the renegotiation_info extension for: + * 1: secure renegotiation + * 2: initial handshake and no SCSV in the ClientHello + * 3: insecure renegotiation and no SCSV in the ClientHello + */ + if (secureRenegotiation || + !cipherSuites.contains(CipherSuite.C_SCSV)) { + clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData); + } + + return clientHelloMessage; } /* diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java --- a/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -172,9 +172,7 @@ * Server can ask the client to initiate a new handshake, e.g. to change * session parameters after a connection has been (re)established. */ -static final -class HelloRequest extends HandshakeMessage -{ +static final class HelloRequest extends HandshakeMessage { int messageType() { return ht_hello_request; } HelloRequest() { } @@ -210,10 +208,7 @@ * Until we know how to parse it, we will just read what we know * about, and let our caller handle the jumps over unknown data. */ -static final -class ClientHello extends HandshakeMessage -{ - int messageType() { return ht_client_hello; } +static final class ClientHello extends HandshakeMessage { ProtocolVersion protocolVersion; RandomCookie clnt_random; @@ -225,37 +220,20 @@ private final static byte[] NULL_COMPRESSION = new byte[] {0}; - ClientHello(SecureRandom generator, ProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - clnt_random = new RandomCookie(generator); - compression_methods = NULL_COMPRESSION; - // sessionId, cipher_suites TBS later - } + ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, + SessionId sessionId, CipherSuiteList cipherSuites) { - CipherSuiteList getCipherSuites() { - return cipherSuites; - } + this.protocolVersion = protocolVersion; + this.sessionId = sessionId; + this.cipherSuites = cipherSuites; - // Set the ciphersuites. - // This method may only be called once. - void setCipherSuites(CipherSuiteList cipherSuites) { - this.cipherSuites = cipherSuites; if (cipherSuites.containsEC()) { extensions.add(SupportedEllipticCurvesExtension.DEFAULT); extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT); } - } - int messageLength() { - /* - * Add fixed size parts of each field... - * version + random + session + cipher + compress - */ - return (2 + 32 + 1 + 2 + 1 - + sessionId.length() /* ... + variable parts */ - + (cipherSuites.size() * 2) - + compression_methods.length) - + extensions.length(); + clnt_random = new RandomCookie(generator); + compression_methods = NULL_COMPRESSION; } ClientHello(HandshakeInStream s, int messageLength) throws IOException { @@ -269,6 +247,34 @@ } } + CipherSuiteList getCipherSuites() { + return cipherSuites; + } + + // add renegotiation_info extension + void addRenegotiationInfoExtension(byte[] clientVerifyData) { + HelloExtension renegotiationInfo = new RenegotiationInfoExtension( + clientVerifyData, new byte[0]); + extensions.add(renegotiationInfo); + } + + @Override + int messageType() { return ht_client_hello; } + + @Override + int messageLength() { + /* + * Add fixed size parts of each field... + * version + random + session + cipher + compress + */ + return (2 + 32 + 1 + 2 + 1 + + sessionId.length() /* ... + variable parts */ + + (cipherSuites.size() * 2) + + compression_methods.length) + + extensions.length(); + } + + @Override void send(HandshakeOutStream s) throws IOException { s.putInt8(protocolVersion.major); s.putInt8(protocolVersion.minor); @@ -279,6 +285,7 @@ extensions.send(s); } + @Override void print(PrintStream s) throws IOException { s.println("*** ClientHello, " + protocolVersion); @@ -315,7 +322,6 @@ CipherSuite cipherSuite; byte compression_method; HelloExtensions extensions = new HelloExtensions(); - int extensionLength; ServerHello() { // empty @@ -1425,8 +1431,6 @@ */ static final class Finished extends HandshakeMessage { - int messageType() { return ht_finished; } - // constant for a Finished message sent by the client final static int CLIENT = 1; @@ -1468,7 +1472,7 @@ * both client and server are fully in sync, and that the handshake * computations have been successful. */ - boolean verify(ProtocolVersion protocolVersion, + boolean verify(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, int sender, SecretKey master) { byte[] myFinished = getFinished(protocolVersion, handshakeHash, sender, master); @@ -1542,14 +1546,25 @@ CertificateVerify.updateDigest(md, pad1, pad2, masterSecret); } + // get the verify_data of the finished message + byte[] getVerifyData() { + return verifyData; + } + + @Override + int messageType() { return ht_finished; } + + @Override int messageLength() { return verifyData.length; } + @Override void send(HandshakeOutStream out) throws IOException { out.write(verifyData); } + @Override void print(PrintStream s) throws IOException { s.println("*** Finished"); if (debug != null && Debug.isOn("verbose")) { @@ -1557,7 +1572,6 @@ s.println("***"); } } - } // diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/Handshaker.java --- a/jdk/src/share/classes/sun/security/ssl/Handshaker.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/Handshaker.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -66,6 +66,14 @@ // the currently active protocol version during a renegotiation ProtocolVersion activeProtocolVersion; + // security parameters for secure renegotiation. + boolean secureRenegotiation; + byte[] clientVerifyData; + byte[] serverVerifyData; + + // is it an initial negotiation or a renegotiation? + boolean isInitialHandshake; + // list of enabled protocols ProtocolList enabledProtocols; @@ -128,31 +136,66 @@ static final Debug debug = Debug.getInstance("ssl"); // By default, disable the unsafe legacy session renegotiation - static final boolean renegotiable = Debug.getBooleanProperty( + static final boolean allowUnsafeRenegotiation = Debug.getBooleanProperty( "sun.security.ssl.allowUnsafeRenegotiation", false); + // For maximum interoperability and backward compatibility, RFC 5746 + // allows server (or client) to accept ClientHello (or ServerHello) + // message without the secure renegotiation_info extension or SCSV. + // + // For maximum security, RFC 5746 also allows server (or client) to + // reject such message with a fatal "handshake_failure" alert. + // + // By default, allow such legacy hello messages. + static final boolean allowLegacyHelloMessages = Debug.getBooleanProperty( + "sun.security.ssl.allowLegacyHelloMessages", true); + // need to dispose the object when it is invalidated boolean invalidated; Handshaker(SSLSocketImpl c, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, - boolean isClient) { + boolean isClient, ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { this.conn = c; - init(context, enabledProtocols, needCertVerify, isClient); + init(context, enabledProtocols, needCertVerify, isClient, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); } Handshaker(SSLEngineImpl engine, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, - boolean isClient) { + boolean isClient, ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { this.engine = engine; - init(context, enabledProtocols, needCertVerify, isClient); + init(context, enabledProtocols, needCertVerify, isClient, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); } private void init(SSLContextImpl context, ProtocolList enabledProtocols, - boolean needCertVerify, boolean isClient) { + boolean needCertVerify, boolean isClient, + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Allow unsafe renegotiation: " + allowUnsafeRenegotiation + + "\nAllow legacy hello messages: " + allowLegacyHelloMessages + + "\nIs initial handshake: " + isInitialHandshake + + "\nIs secure renegotiation: " + secureRenegotiation); + } this.sslContext = context; this.isClient = isClient; + this.activeProtocolVersion = activeProtocolVersion; + this.isInitialHandshake = isInitialHandshake; + this.secureRenegotiation = secureRenegotiation; + this.clientVerifyData = clientVerifyData; + this.serverVerifyData = serverVerifyData; enableNewSession = true; invalidated = false; @@ -353,8 +396,8 @@ * changed due to change in JCE providers since it was enabled). * Does not check if the required server certificates are available. */ - boolean isEnabled(CipherSuite s) { - return enabledCipherSuites.contains(s) && s.isAvailable(); + boolean isNegotiable(CipherSuite s) { + return enabledCipherSuites.contains(s) && s.isNegotiable(); } /** @@ -459,6 +502,27 @@ } /* + * Returns true if renegotiation is in use for this connection. + */ + boolean isSecureRenegotiation() { + return secureRenegotiation; + } + + /* + * Returns the verify_data from the Finished message sent by the client. + */ + byte[] getClientVerifyData() { + return clientVerifyData; + } + + /* + * Returns the verify_data from the Finished message sent by the server. + */ + byte[] getServerVerifyData() { + return serverVerifyData; + } + + /* * This routine is fed SSL handshake records when they become available, * and processes messages found therein. */ diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/HelloExtensions.java --- a/jdk/src/share/classes/sun/security/ssl/HelloExtensions.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/HelloExtensions.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, 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 @@ -81,7 +81,10 @@ } else if (extType == ExtensionType.EXT_ELLIPTIC_CURVES) { extension = new SupportedEllipticCurvesExtension(s, extlen); } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) { - extension = new SupportedEllipticPointFormatsExtension(s, extlen); + extension = + new SupportedEllipticPointFormatsExtension(s, extlen); + } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) { + extension = new RenegotiationInfoExtension(s, extlen); } else { extension = new UnknownExtension(s, extlen, extType); } @@ -89,7 +92,8 @@ len -= extlen + 4; } if (len != 0) { - throw new SSLProtocolException("Error parsing extensions: extra data"); + throw new SSLProtocolException( + "Error parsing extensions: extra data"); } } @@ -162,7 +166,8 @@ return name; } - static List knownExtensions = new ArrayList(8); + static List knownExtensions = + new ArrayList(9); static ExtensionType get(int id) { for (ExtensionType ext : knownExtensions) { @@ -180,17 +185,44 @@ } // extensions defined in RFC 3546 - final static ExtensionType EXT_SERVER_NAME = e( 0, "server_name"); - final static ExtensionType EXT_MAX_FRAGMENT_LENGTH = e( 1, "max_fragment_length"); - final static ExtensionType EXT_CLIENT_CERTIFICATE_URL = e( 2, "client_certificate_url"); - final static ExtensionType EXT_TRUSTED_CA_KEYS = e( 3, "trusted_ca_keys"); - final static ExtensionType EXT_TRUNCATED_HMAC = e( 4, "truncated_hmac"); - final static ExtensionType EXT_STATUS_REQUEST = e( 5, "status_request"); + final static ExtensionType EXT_SERVER_NAME = + e(0x0000, "server_name"); // IANA registry value: 0 + final static ExtensionType EXT_MAX_FRAGMENT_LENGTH = + e(0x0001, "max_fragment_length"); // IANA registry value: 1 + final static ExtensionType EXT_CLIENT_CERTIFICATE_URL = + e(0x0002, "client_certificate_url"); // IANA registry value: 2 + final static ExtensionType EXT_TRUSTED_CA_KEYS = + e(0x0003, "trusted_ca_keys"); // IANA registry value: 3 + final static ExtensionType EXT_TRUNCATED_HMAC = + e(0x0004, "truncated_hmac"); // IANA registry value: 4 + final static ExtensionType EXT_STATUS_REQUEST = + e(0x0005, "status_request"); // IANA registry value: 5 + + // extensions defined in RFC 4681 + final static ExtensionType EXT_USER_MAPPING = + e(0x0006, "user_mapping"); // IANA registry value: 6 + + // extensions defined in RFC 5081 + final static ExtensionType EXT_CERT_TYPE = + e(0x0009, "cert_type"); // IANA registry value: 9 // extensions defined in RFC 4492 (ECC) - final static ExtensionType EXT_ELLIPTIC_CURVES = e(10, "elliptic_curves"); - final static ExtensionType EXT_EC_POINT_FORMATS = e(11, "ec_point_formats"); + final static ExtensionType EXT_ELLIPTIC_CURVES = + e(0x000A, "elliptic_curves"); // IANA registry value: 10 + final static ExtensionType EXT_EC_POINT_FORMATS = + e(0x000B, "ec_point_formats"); // IANA registry value: 11 + // extensions defined in RFC 5054 + final static ExtensionType EXT_SRP = + e(0x000C, "srp"); // IANA registry value: 12 + + // extensions defined in RFC 5246 + final static ExtensionType EXT_SIGNATURE_ALGORITHMS = + e(0x000D, "signature_algorithms"); // IANA registry value: 13 + + // extensions defined in RFC 5746 + final static ExtensionType EXT_RENEGOTIATION_INFO = + e(0xff01, "renegotiation_info"); // IANA registry value: 65281 } abstract class HelloExtension { @@ -238,9 +270,11 @@ } } -// Support for the server_name extension is incomplete. Parsing is implemented -// so that we get nicer debug output, but we neither send it nor do we do -// act on it if we receive it. +/* + * Support for the server_name extension is incomplete. Parsing is implemented + * so that we get nicer debug output, but we neither send it nor do we do + * act on it if we receive it. + */ final class ServerNameExtension extends HelloExtension { final static int NAME_HOST_NAME = 0; @@ -268,9 +302,9 @@ final String hostname; ServerName(HandshakeInStream s) throws IOException { - length = s.getInt16(); - type = s.getInt8(); - data = s.getBytes16(); + length = s.getInt16(); // ServerNameList length + type = s.getInt8(); // NameType + data = s.getBytes16(); // HostName (length read in getBytes16) if (type == NAME_HOST_NAME) { hostname = new String(data, "UTF8"); } else { @@ -549,3 +583,85 @@ return "Extension " + type + ", formats: " + list; } } + +/* + * For secure renegotiation, RFC5746 defines a new TLS extension, + * "renegotiation_info" (with extension type 0xff01), which contains a + * cryptographic binding to the enclosing TLS connection (if any) for + * which the renegotiation is being performed. The "extension data" + * field of this extension contains a "RenegotiationInfo" structure: + * + * struct { + * opaque renegotiated_connection<0..255>; + * } RenegotiationInfo; + */ +final class RenegotiationInfoExtension extends HelloExtension { + private final byte[] renegotiated_connection; + + RenegotiationInfoExtension(byte[] clientVerifyData, + byte[] serverVerifyData) { + super(ExtensionType.EXT_RENEGOTIATION_INFO); + + if (clientVerifyData.length != 0) { + renegotiated_connection = + new byte[clientVerifyData.length + serverVerifyData.length]; + System.arraycopy(clientVerifyData, 0, renegotiated_connection, + 0, clientVerifyData.length); + + if (serverVerifyData.length != 0) { + System.arraycopy(serverVerifyData, 0, renegotiated_connection, + clientVerifyData.length, serverVerifyData.length); + } + } else { + // ignore both the client and server verify data. + renegotiated_connection = new byte[0]; + } + } + + RenegotiationInfoExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_RENEGOTIATION_INFO); + + // check the extension length + if (len < 1) { + throw new SSLProtocolException("Invalid " + type + " extension"); + } + + int renegoInfoDataLen = s.getInt8(); + if (renegoInfoDataLen + 1 != len) { // + 1 = the byte we just read + throw new SSLProtocolException("Invalid " + type + " extension"); + } + + renegotiated_connection = new byte[renegoInfoDataLen]; + if (renegoInfoDataLen != 0) { + s.read(renegotiated_connection, 0, renegoInfoDataLen); + } + } + + + // Length of the encoded extension, including the type and length fields + int length() { + return 5 + renegotiated_connection.length; + } + + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putInt16(renegotiated_connection.length + 1); + s.putBytes8(renegotiated_connection); + } + + boolean isEmpty() { + return renegotiated_connection.length == 0; + } + + byte[] getRenegotiatedConnection() { + return renegotiated_connection; + } + + public String toString() { + return "Extension " + type + ", renegotiated_connection: " + + (renegotiated_connection.length == 0 ? "" : + Debug.toString(renegotiated_connection)); + } + +} diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/OutputRecord.java --- a/jdk/src/share/classes/sun/security/ssl/OutputRecord.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/OutputRecord.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -343,6 +343,9 @@ * example, Netscape Commerce 1.0 servers. The V3 message is in the * header and the bytes passed as parameter. This routine translates * the V3 message into an equivalent V2 one. + * + * Note that the translation will strip off all hello extensions as + * SSL V2.0 does not support hello extension. */ private void V3toV2ClientHello(byte v3Msg []) throws SSLException { int v3SessionIdLenOffset = 2 + 32; // version + nonce @@ -361,12 +364,21 @@ int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length int v2CipherSpecLen = 0; count = 11; + boolean containsRenegoInfoSCSV = false; for (int i = 0; i < cipherSpecs; i++) { byte byte1, byte2; byte1 = v3Msg[v3CipherSpecOffset++]; byte2 = v3Msg[v3CipherSpecOffset++]; v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2); + if (!containsRenegoInfoSCSV && + byte1 == (byte)0x00 && byte2 == (byte)0xFF) { + containsRenegoInfoSCSV = true; + } + } + + if (!containsRenegoInfoSCSV) { + v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF); } /* diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java --- a/jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -275,6 +275,12 @@ private CipherBox readCipher, writeCipher; // NOTE: compression state would be saved here + /* + * security parameters for secure renegotiation. + */ + private boolean secureRenegotiation; + private byte[] clientVerifyData; + private byte[] serverVerifyData; /* * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * @@ -356,6 +362,11 @@ writeCipher = CipherBox.NULL; writeMAC = MAC.NULL; + // default security parameters for secure renegotiation + secureRenegotiation = false; + clientVerifyData = new byte[0]; + serverVerifyData = new byte[0]; + enabledCipherSuites = CipherSuiteList.getDefault(); enabledProtocols = ProtocolList.getDefault(); @@ -434,11 +445,14 @@ } if (roleIsServer) { handshaker = new ServerHandshaker(this, sslContext, - enabledProtocols, doClientAuth, - connectionState == cs_RENEGOTIATE, protocolVersion); + enabledProtocols, doClientAuth, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); } else { handshaker = new ClientHandshaker(this, sslContext, - enabledProtocols, protocolVersion); + enabledProtocols, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); } handshaker.enabledCipherSuites = enabledCipherSuites; handshaker.setEnableSessionCreation(enableSessionCreation); @@ -640,8 +654,16 @@ break; case cs_DATA: - if (!Handshaker.renegotiable) { - throw new SSLHandshakeException("renegotiation is not allowed"); + if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { + throw new SSLHandshakeException( + "Insecure renegotiation is not allowed"); + } + + if (!secureRenegotiation) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: Using insecure renegotiation"); + } } // initialize the handshaker, move to cs_RENEGOTIATE @@ -978,6 +1000,12 @@ connectionState = cs_DATA; } } else if (handshaker.isDone()) { + // reset the parameters for secure renegotiation. + secureRenegotiation = + handshaker.isSecureRenegotiation(); + clientVerifyData = handshaker.getClientVerifyData(); + serverVerifyData = handshaker.getServerVerifyData(); + sess = handshaker.getSession(); if (!writer.hasOutboundData()) { hsStatus = HandshakeStatus.FINISHED; diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java --- a/jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -307,8 +307,9 @@ try { ServerHandshaker handshaker = tmp.getServerHandshaker(); - for (Iterator t = enabledCipherSuites.iterator(); t.hasNext(); ) { - CipherSuite suite = (CipherSuite)t.next(); + for (Iterator t = enabledCipherSuites.iterator(); + t.hasNext();) { + CipherSuite suite = t.next(); if (handshaker.trySetCipherSuite(suite)) { checkedEnabled = true; return; diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java --- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -275,9 +275,9 @@ * This is necessary so that processing of close_notify alerts * from the peer are handled properly. */ - private Object handshakeLock; - ReentrantLock writeLock; - private Object readLock; + final private Object handshakeLock = new Object(); + final ReentrantLock writeLock = new ReentrantLock(); + final private Object readLock = new Object(); private InputRecord inrec; @@ -289,6 +289,13 @@ // NOTE: compression state would be saved here /* + * security parameters for secure renegotiation. + */ + private boolean secureRenegotiation; + private byte[] clientVerifyData; + private byte[] serverVerifyData; + + /* * The authentication context holds all information used to establish * who this end of the connection is (certificate chains, private keys, * etc) and who is trusted (e.g. as CAs or websites). @@ -528,11 +535,13 @@ writeCipher = CipherBox.NULL; writeMAC = MAC.NULL; + // initial security parameters for secure renegotiation + secureRenegotiation = false; + clientVerifyData = new byte[0]; + serverVerifyData = new byte[0]; + enabledCipherSuites = CipherSuiteList.getDefault(); enabledProtocols = ProtocolList.getDefault(); - handshakeLock = new Object(); - writeLock = new ReentrantLock(); - readLock = new Object(); inrec = null; // save the acc @@ -914,6 +923,12 @@ connectionState = cs_DATA; } } else if (handshaker.isDone()) { + // reset the parameters for secure renegotiation. + secureRenegotiation = + handshaker.isSecureRenegotiation(); + clientVerifyData = handshaker.getClientVerifyData(); + serverVerifyData = handshaker.getServerVerifyData(); + sess = handshaker.getSession(); handshaker = null; connectionState = cs_DATA; @@ -1091,11 +1106,14 @@ } if (roleIsServer) { handshaker = new ServerHandshaker(this, sslContext, - enabledProtocols, doClientAuth, - connectionState == cs_RENEGOTIATE, protocolVersion); + enabledProtocols, doClientAuth, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); } else { handshaker = new ClientHandshaker(this, sslContext, - enabledProtocols, protocolVersion); + enabledProtocols, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); } handshaker.enabledCipherSuites = enabledCipherSuites; handshaker.setEnableSessionCreation(enableSessionCreation); @@ -1200,8 +1218,16 @@ break; case cs_DATA: - if (!Handshaker.renegotiable) { - throw new SSLHandshakeException("renegotiation is not allowed"); + if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { + throw new SSLHandshakeException( + "Insecure renegotiation is not allowed"); + } + + if (!secureRenegotiation) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: Using insecure renegotiation"); + } } // initialize the handshaker, move to cs_RENEGOTIATE diff -r e565c553e9fc -r 533f4ad71f88 jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java --- a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -69,9 +69,6 @@ // flag to check for clientCertificateVerify message private boolean needClientVerify = false; - // indicate a renegotiation handshaking - private boolean isRenegotiation = false; - /* * For exportable ciphersuites using non-exportable key sizes, we use * ephemeral RSA keys. We could also do anonymous RSA in the same way @@ -100,13 +97,15 @@ */ ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context, ProtocolList enabledProtocols, byte clientAuth, - boolean isRenegotiation, ProtocolVersion activeProtocolVersion) { + ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, + boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { super(socket, context, enabledProtocols, - (clientAuth != SSLEngineImpl.clauth_none), false); + (clientAuth != SSLEngineImpl.clauth_none), false, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); doClientAuth = clientAuth; - this.isRenegotiation = isRenegotiation; - this.activeProtocolVersion = activeProtocolVersion; } /* @@ -114,13 +113,15 @@ */ ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context, ProtocolList enabledProtocols, byte clientAuth, - boolean isRenegotiation, ProtocolVersion activeProtocolVersion) { + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { super(engine, context, enabledProtocols, - (clientAuth != SSLEngineImpl.clauth_none), false); + (clientAuth != SSLEngineImpl.clauth_none), false, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); doClientAuth = clientAuth; - this.isRenegotiation = isRenegotiation; - this.activeProtocolVersion = activeProtocolVersion; } /* @@ -269,41 +270,122 @@ mesg.print(System.out); } - // if it is a renegotiation request and renegotiation is not allowed - if (isRenegotiation && !renegotiable) { - if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { - // response with a no_negotiation warning, - warningSE(Alerts.alert_no_negotiation); + // Does the message include security renegotiation indication? + boolean renegotiationIndicated = false; - // invalidate the handshake so that the caller can - // dispose this object. - invalidated = true; + // check the TLS_EMPTY_RENEGOTIATION_INFO_SCSV + CipherSuiteList cipherSuites = mesg.getCipherSuites(); + if (cipherSuites.contains(CipherSuite.C_SCSV)) { + renegotiationIndicated = true; + if (isInitialHandshake) { + secureRenegotiation = true; + } else { + // abort the handshake with a fatal handshake_failure alert + if (secureRenegotiation) { + fatalSE(Alerts.alert_handshake_failure, + "The SCSV is present in a secure renegotiation"); + } else { + fatalSE(Alerts.alert_handshake_failure, + "The SCSV is present in a insecure renegotiation"); + } + } + } - // If there is still unread block in the handshake - // input stream, it would be truncated with the disposal - // and the next handshake message will become incomplete. - // - // However, according to SSL/TLS specifications, no more - // handshake message could immediately follow ClientHello - // or HelloRequest. But in case of any improper messages, - // we'd better check to ensure there is no remaining bytes - // in the handshake input stream. - if (input.available() > 0) { - fatalSE(Alerts.alert_unexpected_message, - "ClientHello followed by an unexpected " + - "handshake message"); + // check the "renegotiation_info" extension + RenegotiationInfoExtension clientHelloRI = (RenegotiationInfoExtension) + mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO); + if (clientHelloRI != null) { + renegotiationIndicated = true; + if (isInitialHandshake) { + // verify the length of the "renegotiated_connection" field + if (!clientHelloRI.isEmpty()) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "The renegotiation_info field is not empty"); + } + secureRenegotiation = true; + } else { + if (!secureRenegotiation) { + // unexpected RI extension for insecure renegotiation, + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "The renegotiation_info is present in a insecure " + + "renegotiation"); } - return; - } else { - // For SSLv3, send the handshake_failure fatal error. - // Note that SSLv3 does not define a no_negotiation alert - // like TLSv1. However we cannot ignore the message - // simply, otherwise the other side was waiting for a - // response that would never come. - fatalSE(Alerts.alert_handshake_failure, - "renegotiation is not allowed"); + // verify the client_verify_data value + if (!Arrays.equals(clientVerifyData, + clientHelloRI.getRenegotiatedConnection())) { + fatalSE(Alerts.alert_handshake_failure, + "Incorrect verify data in ClientHello " + + "renegotiation_info message"); + } + } + } else if (!isInitialHandshake && secureRenegotiation) { + // if the connection's "secure_renegotiation" flag is set to TRUE + // and the "renegotiation_info" extension is not present, abort + // the handshake. + fatalSE(Alerts.alert_handshake_failure, + "Inconsistent secure renegotiation indication"); + } + + // if there is no security renegotiation indication or the previous + // handshake is insecure. + if (!renegotiationIndicated || !secureRenegotiation) { + if (isInitialHandshake) { + if (!allowLegacyHelloMessages) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "Failed to negotiate the use of secure renegotiation"); + } + + // continue with legacy ClientHello + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Warning: No renegotiation " + + "indication in ClientHello, allow legacy ClientHello"); + } + } else if (!allowUnsafeRenegotiation) { + // abort the handshake + if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { + // response with a no_renegotiation warning, + warningSE(Alerts.alert_no_renegotiation); + + // invalidate the handshake so that the caller can + // dispose this object. + invalidated = true; + + // If there is still unread block in the handshake + // input stream, it would be truncated with the disposal + // and the next handshake message will become incomplete. + // + // However, according to SSL/TLS specifications, no more + // handshake message could immediately follow ClientHello + // or HelloRequest. But in case of any improper messages, + // we'd better check to ensure there is no remaining bytes + // in the handshake input stream. + if (input.available() > 0) { + fatalSE(Alerts.alert_unexpected_message, + "ClientHello followed by an unexpected " + + "handshake message"); + } + + return; + } else { + // For SSLv3, send the handshake_failure fatal error. + // Note that SSLv3 does not define a no_renegotiation + // alert like TLSv1. However we cannot ignore the message + // simply, otherwise the other side was waiting for a + // response that would never come. + fatalSE(Alerts.alert_handshake_failure, + "Renegotiation is not allowed"); + } + } else { // !isInitialHandshake && allowUnsafeRenegotiation + // continue with unsafe renegotiation. + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: continue with insecure renegotiation"); + } } } @@ -454,7 +536,7 @@ // verify that the ciphersuite from the cached session // is in the list of client requested ciphersuites and // we have it enabled - if ((isEnabled(suite) == false) || + if ((isNegotiable(suite) == false) || (mesg.getCipherSuites().contains(suite) == false)) { resumingSession = false; } else { @@ -484,8 +566,8 @@ if (!enableNewSession) { throw new SSLException("Client did not resume a session"); } - supportedCurves = (SupportedEllipticCurvesExtension)mesg.extensions.get - (ExtensionType.EXT_ELLIPTIC_CURVES); + supportedCurves = (SupportedEllipticCurvesExtension) + mesg.extensions.get(ExtensionType.EXT_ELLIPTIC_CURVES); chooseCipherSuite(mesg); session = new SSLSessionImpl(protocolVersion, cipherSuite, sslContext.getSecureRandom(), @@ -498,6 +580,21 @@ m1.sessionId = session.getSessionId(); m1.compression_method = session.getCompression(); + if (secureRenegotiation) { + // For ServerHellos that are initial handshakes, then the + // "renegotiated_connection" field in "renegotiation_info" + // extension is of zero length. + // + // For ServerHellos that are renegotiating, this field contains + // the concatenation of client_verify_data and server_verify_data. + // + // Note that for initial handshakes, both the clientVerifyData + // variable and serverVerifyData variable are of zero length. + HelloExtension serverHelloRI = new RenegotiationInfoExtension( + clientVerifyData, serverVerifyData); + m1.extensions.add(serverHelloRI); + } + if (debug != null && Debug.isOn("handshake")) { m1.print(System.out); System.out.println("Cipher suite: " + session.getSuite()); @@ -686,11 +783,13 @@ */ private void chooseCipherSuite(ClientHello mesg) throws IOException { for (CipherSuite suite : mesg.getCipherSuites().collection()) { - if (isEnabled(suite) == false) { + if (isNegotiable(suite) == false) { continue; } + if (doClientAuth == SSLEngineImpl.clauth_required) { - if ((suite.keyExchange == K_DH_ANON) || (suite.keyExchange == K_ECDH_ANON)) { + if ((suite.keyExchange == K_DH_ANON) || + (suite.keyExchange == K_ECDH_ANON)) { continue; } } @@ -728,7 +827,7 @@ return true; } - if (suite.isAvailable() == false) { + if (suite.isNegotiable() == false) { return false; } @@ -1136,6 +1235,13 @@ } /* + * save client verify data for secure renegotiation + */ + if (secureRenegotiation) { + clientVerifyData = mesg.getVerifyData(); + } + + /* * OK, it verified. If we're doing the full handshake, add that * "Finished" message to the hash of handshake messages, then send * the change_cipher_spec and Finished message. @@ -1185,6 +1291,13 @@ sendChangeCipherSpec(mesg, finishedTag); /* + * save server verify data for secure renegotiation + */ + if (secureRenegotiation) { + serverVerifyData = mesg.getVerifyData(); + } + + /* * Update state machine so client MUST send 'finished' next * The update should only take place if it is not in the fast * handshake mode since the server has to wait for a finished diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/pkcs11/fips/CipherTest.java --- a/jdk/test/sun/security/pkcs11/fips/CipherTest.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/pkcs11/fips/CipherTest.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -114,10 +114,11 @@ } boolean isEnabled() { -// return cipherSuite.equals("SSL_RSA_WITH_RC4_128_MD5") && -// (clientAuth != null); -// return cipherSuite.indexOf("_RSA_") != -1; -// return cipherSuite.indexOf("DH_anon") != -1; + // ignore SCSV + if (cipherSuite.equals("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { + return false; + } + return true; } diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/pkcs11/sslecc/CipherTest.java --- a/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -114,12 +114,11 @@ } boolean isEnabled() { -// if (true) return cipherSuite.contains("_ECDH_"); -// return cipherSuite.equals("SSL_RSA_WITH_RC4_128_MD5") && -// (clientAuth != null); -// return cipherSuite.indexOf("_RSA_") != -1; -// return cipherSuite.indexOf("DH_anon") != -1; -// return cipherSuite.contains("ECDSA") == false; + // ignore SCSV + if (cipherSuite.equals("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { + return false; + } + return true; } diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java --- a/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -25,8 +25,6 @@ * @test * @bug 4403428 * @summary Invalidating JSSE session on server causes SSLProtocolException - * @ignore incompatible with disabled unsafe renegotiation (6898739), please - * reenable when safe renegotiation is implemented. * @author Brad Wetmore */ diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/JSSERenegotiate.java --- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/JSSERenegotiate.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/JSSERenegotiate.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -26,8 +26,6 @@ * @bug 4280338 * @summary "Unsupported SSL message version" SSLProtocolException * w/SSL_RSA_WITH_NULL_MD5 - * @ignore incompatible with disabled unsafe renegotiation (6898739), please - * reenable when safe renegotiation is implemented. * * @author Ram Marti * @author Brad Wetmore diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/CheckStatus.java --- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/CheckStatus.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/CheckStatus.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -25,8 +25,6 @@ * @test * @bug 4948079 * @summary SSLEngineResult needs updating [none yet] - * @ignore incompatible with disabled unsafe renegotiation (6898739), please - * reenable when safe renegotiation is implemented. * * This is a simple hack to test a bunch of conditions and check * their return codes. diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/ConnectionTest.java --- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/ConnectionTest.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/ConnectionTest.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -26,8 +26,6 @@ * @bug 4495742 * @summary Add non-blocking SSL/TLS functionality, usable with any * I/O abstraction - * @ignore incompatible with disabled unsafe renegotiation (6898739), please - * reenable when safe renegotiation is implemented. * * This is a bit hacky, meant to test various conditions. The main * thing I wanted to do with this was to do buffer reads/writes diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/NoAuthClientAuth.java --- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/NoAuthClientAuth.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/NoAuthClientAuth.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -25,8 +25,6 @@ * @test * @bug 4495742 * @summary Demonstrate SSLEngine switch from no client auth to client auth. - * @ignore incompatible with disabled unsafe renegotiation (6898739), please - * reenable when safe renegotiation is implemented. * * @author Brad R. Wetmore */ diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/TestAllSuites.java --- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/TestAllSuites.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/TestAllSuites.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -119,6 +119,15 @@ return; } + /* + * Don't run the SCSV suite + */ + if (suite.equals("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { + System.out.println("Ignoring SCSV suite"); + return; + } + + if (!suite.contains("DH_anon")) { ssle2.setNeedClientAuth(true); } diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/sanity/ciphersuites/CheckCipherSuites.java --- a/jdk/test/sun/security/ssl/sanity/ciphersuites/CheckCipherSuites.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/sanity/ciphersuites/CheckCipherSuites.java Sat Jun 12 00:42:51 2010 -0700 @@ -64,6 +64,8 @@ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", + }; private final static String[] ENABLED_UNLIMITED = { @@ -101,6 +103,8 @@ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", + }; // supported ciphersuites using default JCE policy jurisdiction files @@ -133,6 +137,7 @@ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", "SSL_RSA_WITH_NULL_MD5", "SSL_RSA_WITH_NULL_SHA", @@ -160,6 +165,7 @@ "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", + }; // supported ciphersuites using unlimited JCE policy jurisdiction files @@ -199,6 +205,7 @@ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", "SSL_RSA_WITH_NULL_MD5", "SSL_RSA_WITH_NULL_SHA", @@ -228,6 +235,7 @@ "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", + }; private static void showSuites(String[] suites) { diff -r e565c553e9fc -r 533f4ad71f88 jdk/test/sun/security/ssl/sanity/interop/CipherTest.java --- a/jdk/test/sun/security/ssl/sanity/interop/CipherTest.java Fri Jun 11 14:47:22 2010 +0100 +++ b/jdk/test/sun/security/ssl/sanity/interop/CipherTest.java Sat Jun 12 00:42:51 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -115,10 +115,11 @@ } boolean isEnabled() { -// return cipherSuite.equals("SSL_RSA_WITH_RC4_128_MD5") && -// (clientAuth != null); -// return cipherSuite.indexOf("_RSA_") != -1; -// return cipherSuite.indexOf("DH_anon") != -1; + // ignore SCSV + if (cipherSuite.equals("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { + return false; + } + return true; }