/* * 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<>(4); for (ProtocolVersion protocol : enabledProtocols.collection()) { boolean found = false; for (CipherSuite suite : enabledCipherSuites.collection()) { if (suite.isAvailable() && suite.obsoleted > protocol.v && suite.supported <= protocol.v) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { protocols.add(protocol); found = true; break; } else if (debug != null && Debug.isOn("verbose")) { System.out.println("Ignoring disabled cipher suite: " + suite + " for " + protocol); } } else if (debug != null && Debug.isOn("verbose")) { System.out.println("Ignoring unsupported cipher suite: " + suite + " for " + protocol); } } if (!found && (debug != null) && Debug.isOn("handshake")) { System.out.println("No available cipher suite for " + protocol); } } activeProtocols = new ProtocolList(protocols); } return activeProtocols; }
/* * Sends a change cipher spec message and updates the write side * cipher state so that future messages use the just-negotiated spec. */ void sendChangeCipherSpec(Finished mesg, boolean lastMessage) throws IOException { output.flush(); // i.e. handshake data /* * The write cipher state is protected by the connection write lock * so we must grab it while making the change. We also * make sure no writes occur between sending the ChangeCipherSpec * message, installing the new cipher state, and sending the * Finished message. * * We already hold SSLEngine/SSLSocket "this" by virtue * of this being called from the readRecord code. */ OutputRecord r; if (conn != null) { r = new OutputRecord(Record.ct_change_cipher_spec); } else { r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine); } r.setVersion(protocolVersion); r.write(1); // single byte of data if (conn != null) { conn.writeLock.lock(); try { conn.writeRecord(r); conn.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } mesg.write(output); output.flush(); } finally { conn.writeLock.unlock(); } } else { synchronized (engine.writeLock) { engine.writeRecord((EngineOutputRecord) r); engine.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } mesg.write(output); if (lastMessage) { output.setFinishedMsg(); } output.flush(); } } }
/** * Get the active cipher suites. * * <p>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. * * <p>Therefore, when the active protocols only include TLS 1.1 or later, the client cannot * request to negotiate those obsoleted cipher suites. That is, 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. * * <p>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<>(); if (!(activeProtocols.collection().isEmpty()) && activeProtocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : enabledCipherSuites.collection()) { if (suite.obsoleted > activeProtocols.min.v && suite.supported <= activeProtocols.max.v) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); } } else if (debug != null && Debug.isOn("verbose")) { if (suite.obsoleted <= activeProtocols.min.v) { System.out.println("Ignoring obsoleted cipher suite: " + suite); } else { System.out.println("Ignoring unsupported cipher suite: " + suite); } } } } activeCipherSuites = new CipherSuiteList(suites); } return activeCipherSuites; }
private void init( SSLContextImpl context, ProtocolList enabledProtocols, 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.needCertVerify = needCertVerify; this.activeProtocolVersion = activeProtocolVersion; this.isInitialHandshake = isInitialHandshake; this.secureRenegotiation = secureRenegotiation; this.clientVerifyData = clientVerifyData; this.serverVerifyData = serverVerifyData; enableNewSession = true; invalidated = false; setCipherSuite(CipherSuite.C_NULL); setEnabledProtocols(enabledProtocols); if (conn != null) { algorithmConstraints = new SSLAlgorithmConstraints(conn, true); } else { // engine != null algorithmConstraints = new SSLAlgorithmConstraints(engine, true); } // // In addition to the connection state machine, controlling // how the connection deals with the different sorts of records // that get sent (notably handshake transitions!), there's // also a handshaking state machine that controls message // sequencing. // // It's a convenient artifact of the protocol that this can, // with only a couple of minor exceptions, be driven by the // type constant for the last message seen: except for the // client's cert verify, those constants are in a convenient // order to drastically simplify state machine checking. // state = -2; // initialized but not activated }
/* * Used to kickstart the negotiation ... either writing a * ClientHello or a HelloRequest as appropriate, whichever * the subclass returns. NOP if handshaking's already started. */ void kickstart() throws IOException { if (state >= 0) { return; } HandshakeMessage m = getKickstartMessage(); if (debug != null && Debug.isOn("handshake")) { m.print(System.out); } m.write(output); output.flush(); state = m.messageType(); }
/* * Calculate the keys needed for this connection, once the session's * master secret has been calculated. Uses the master key and nonces; * the amount of keying material generated is a function of the cipher * suite that's been negotiated. * * This gets called both on the "full handshake" (where we exchanged * a premaster secret and started a new session) as well as on the * "fast handshake" (where we just resumed a pre-existing session). */ void calculateConnectionKeys(SecretKey masterKey) { /* * For both the read and write sides of the protocol, we use the * master to generate MAC secrets and cipher keying material. Block * ciphers need initialization vectors, which we also generate. * * First we figure out how much keying material is needed. */ int hashSize = cipherSuite.macAlg.size; boolean is_exportable = cipherSuite.exportable; BulkCipher cipher = cipherSuite.cipher; int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; // Which algs/params do we need to use? String keyMaterialAlg; PRF prf; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { keyMaterialAlg = "SunTls12KeyMaterial"; prf = cipherSuite.prfAlg; } else { keyMaterialAlg = "SunTlsKeyMaterial"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( masterKey, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, cipher.algorithm, cipher.keySize, expandedKeySize, cipher.ivSize, hashSize, prfHashAlg, prfHashLength, prfBlockSize); try { KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); kg.init(spec); TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec) kg.generateKey(); 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(); clntMacSecret = keySpec.getClientMacKey(); svrMacSecret = keySpec.getServerMacKey(); } catch (GeneralSecurityException e) { throw new ProviderException(e); } // // Dump the connection keys as they're generated. // if (debug != null && Debug.isOn("keygen")) { synchronized (System.out) { HexDumpEncoder dump = new HexDumpEncoder(); System.out.println("CONNECTION KEYGEN:"); // Inputs: System.out.println("Client Nonce:"); printHex(dump, clnt_random.random_bytes); System.out.println("Server Nonce:"); printHex(dump, svr_random.random_bytes); System.out.println("Master Secret:"); printHex(dump, masterKey.getEncoded()); // Outputs: System.out.println("Client MAC write Secret:"); printHex(dump, clntMacSecret.getEncoded()); System.out.println("Server MAC write Secret:"); printHex(dump, svrMacSecret.getEncoded()); if (clntWriteKey != null) { System.out.println("Client write key:"); printHex(dump, clntWriteKey.getEncoded()); System.out.println("Server write key:"); printHex(dump, svrWriteKey.getEncoded()); } else { System.out.println("... no encryption keys used"); } if (clntWriteIV != null) { System.out.println("Client write IV:"); printHex(dump, clntWriteIV.getIV()); System.out.println("Server write IV:"); printHex(dump, svrWriteIV.getIV()); } else { 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(); } } }
/* * Calculate the master secret from its various components. This is * used for key exchange by all cipher suites. * * The master secret is the catenation of three MD5 hashes, each * consisting of the pre-master secret and a SHA1 hash. Those three * SHA1 hashes are of (different) constant strings, the pre-master * secret, and the nonces provided by the client and the server. */ private SecretKey calculateMasterSecret( SecretKey preMasterSecret, ProtocolVersion requestedVersion) { if (debug != null && Debug.isOn("keygen")) { HexDumpEncoder dump = new HexDumpEncoder(); System.out.println("SESSION KEYGEN:"); System.out.println("PreMaster Secret:"); printHex(dump, preMasterSecret.getEncoded()); // Nonces are dumped with connection keygen, no // benefit to doing it twice } // What algs/params do we need to use? String masterAlg; PRF prf; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { masterAlg = "SunTls12MasterSecret"; prf = cipherSuite.prfAlg; } else { masterAlg = "SunTlsMasterSecret"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( preMasterSecret, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, prfHashAlg, prfHashLength, prfBlockSize); SecretKey masterSecret; try { KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); kg.init(spec); masterSecret = kg.generateKey(); } catch (GeneralSecurityException e) { // For RSA premaster secrets, do not signal a protocol error // due to the Bleichenbacher attack. See comments further down. if (!preMasterSecret.getAlgorithm().equals("TlsRsaPremasterSecret")) { throw new ProviderException(e); } if (debug != null && Debug.isOn("handshake")) { System.out.println("RSA master secret generation error:"); e.printStackTrace(System.out); System.out.println("Generating new random premaster secret"); } 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), // return master secret immediately. if ((requestedVersion == null) || !(masterSecret instanceof TlsMasterSecret)) { return masterSecret; } // we have checked the ClientKeyExchange message when reading TLS // record, the following check is necessary to ensure that // JCE provider does not ignore the checking, or the previous // checking process bypassed the premaster secret version checking. TlsMasterSecret tlsKey = (TlsMasterSecret) masterSecret; int major = tlsKey.getMajorVersion(); int minor = tlsKey.getMinorVersion(); if ((major < 0) || (minor < 0)) { return masterSecret; } // check if the premaster secret version is ok // the specification says that it must be the maximum version supported // by the client from its ClientHello message. However, many // implementations send the negotiated version, so accept both // for SSL v3.0 and TLS v1.0. // NOTE that we may be comparing two unsupported version numbers, which // is why we cannot use object reference equality in this special case. ProtocolVersion premasterVersion = ProtocolVersion.valueOf(major, minor); boolean versionMismatch = (premasterVersion.v != requestedVersion.v); /* * we never checked the client_version in server side * for TLS v1.0 and SSL v3.0. For compatibility, we * maintain this behavior. */ if (versionMismatch && requestedVersion.v <= ProtocolVersion.TLS10.v) { versionMismatch = (premasterVersion.v != protocolVersion.v); } if (versionMismatch == false) { // check passed, return key return masterSecret; } // Due to the Bleichenbacher attack, do not signal a protocol error. // Generate a random premaster secret and continue with the handshake, // which will fail when verifying the finished messages. // For more information, see comments in PreMasterSecret. if (debug != null && Debug.isOn("handshake")) { System.out.println( "RSA PreMasterSecret version error: expected" + protocolVersion + " or " + requestedVersion + ", decrypted: " + premasterVersion); System.out.println("Generating new random premaster secret"); } preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion); // recursive call with new premaster secret return calculateMasterSecret(preMasterSecret, null); }