/** * Set cipherSuite and keyExchange to the given CipherSuite. * Does not perform any verification that this is a valid selection, * this must be done before calling this method. */ void setCipherSuite(CipherSuite s) { this.cipherSuite = s; this.keyExchange = s.keyExchange; } /** * Check if the given ciphersuite is enabled and available. * Does not check if the required server certificates are available. */ boolean isNegotiable(CipherSuite s) { if (activeCipherSuites == null) { activeCipherSuites = getActiveCipherSuites(); } return activeCipherSuites.contains(s) && s.isNegotiable(); } /** * Check if the given protocol version is enabled and available. */ boolean isNegotiable(ProtocolVersion protocolVersion) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } return activeProtocols.contains(protocolVersion); } /** * Select a protocol version from the list. Called from * ServerHandshaker to negotiate protocol version. * * Return the lower of the protocol version suggested in the * clien hello and the highest supported by the server. */ ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } return activeProtocols.selectProtocolVersion(protocolVersion); } /** * Get the active cipher suites. * * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT * negotiate these cipher suites in TLS 1.1 or later mode. * * Therefore, when the active protocols only include TLS 1.1 or later, * the client cannot request to negotiate those obsoleted cipher * suites. That 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. * * 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; } /* * 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; } /** * As long as handshaking has not activated, we can * change whether session creations are allowed. * * Callers should do their own checking if handshaking * has activated. */ void setEnableSessionCreation(boolean newSessions) { enableNewSession = newSessions; } /** * Create a new read cipher and return it to caller. */ CipherBox newReadCipher() throws NoSuchAlgorithmException { BulkCipher cipher = cipherSuite.cipher; CipherBox box; if (isClient) { box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), false); svrWriteKey = null; svrWriteIV = null; } else { box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), false); clntWriteKey = null; clntWriteIV = null; } return box; } /** * Create a new write cipher and return it to caller. */ CipherBox newWriteCipher() throws NoSuchAlgorithmException { BulkCipher cipher = cipherSuite.cipher; CipherBox box; if (isClient) { box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), true); clntWriteKey = null; clntWriteIV = null; } else { box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), true); svrWriteKey = null; svrWriteIV = null; } return box; } /** * Create a new read MAC and return it to caller. */ MAC newReadMAC() throws NoSuchAlgorithmException, InvalidKeyException { MacAlg macAlg = cipherSuite.macAlg; MAC mac; if (isClient) { mac = macAlg.newMac(protocolVersion, svrMacSecret); svrMacSecret = null; } else { mac = macAlg.newMac(protocolVersion, clntMacSecret); clntMacSecret = null; } return mac; } /** * Create a new write MAC and return it to caller. */ MAC newWriteMAC() throws NoSuchAlgorithmException, InvalidKeyException { MacAlg macAlg = cipherSuite.macAlg; MAC mac; if (isClient) { mac = macAlg.newMac(protocolVersion, clntMacSecret); clntMacSecret = null; } else { mac = macAlg.newMac(protocolVersion, svrMacSecret); svrMacSecret = null; } return mac; } /* * Returns true iff the handshake sequence is done, so that * this freshly created session can become the current one. */ boolean isDone() { return state == HandshakeMessage.ht_finished; } /* * Returns the session which was created through this * handshake sequence ... should be called after isDone() * returns true. */ SSLSessionImpl getSession() { return session; } /* * Set the handshake session */ void setHandshakeSessionSE(SSLSessionImpl handshakeSession) { if (conn != null) { conn.setHandshakeSession(handshakeSession); } else { engine.setHandshakeSession(handshakeSession); } } /* * 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. */ void process_record(InputRecord r, boolean expectingFinished) throws IOException { checkThrown(); /* * Store the incoming handshake data, then see if we can * now process any completed handshake messages */ input.incomingRecord(r); /* * We don't need to create a separate delegatable task * for finished messages. */ if ((conn != null) || expectingFinished) { processLoop(); } else { delegateTask(new PrivilegedExceptionAction<Void>() { public Void run() throws Exception { processLoop(); return null; } }); } } /* * On input, we hash messages one at a time since servers may need * to access an intermediate hash to validate a CertificateVerify * message. * * Note that many handshake messages can come in one record (and often * do, to reduce network resource utilization), and one message can also * require multiple records (e.g. very large Certificate messages). */ void processLoop() throws IOException { // need to read off 4 bytes at least to get the handshake // message type and length. while (input.available() >= 4) { byte messageType; int messageLen; /* * See if we can read the handshake message header, and * then the entire handshake message. If not, wait till * we can read and process an entire message. */ input.mark(4); messageType = (byte)input.getInt8(); messageLen = input.getInt24(); if (input.available() < messageLen) { input.reset(); return; } /* * Process the messsage. We require * that processMessage() consumes the entire message. In * lieu of explicit error checks (how?!) we assume that the * data will look like garbage on encoding/processing errors, * and that other protocol code will detect such errors. * * Note that digesting is normally deferred till after the * message has been processed, though to process at least the * client's Finished message (i.e. send the server's) we need * to acccelerate that digesting. * * Also, note that hello request messages are never hashed; * that includes the hello request header, too. */ if (messageType == HandshakeMessage.ht_hello_request) { input.reset(); processMessage(messageType, messageLen); input.ignore(4 + messageLen); } else { input.mark(messageLen); processMessage(messageType, messageLen); input.digestNow(); } } } /** * Returns true iff the handshaker has been activated. * * In activated state, the handshaker may not send any messages out. */ boolean activated() { return state >= -1; } /** * Returns true iff the handshaker has sent any messages. */ boolean started() { return state >= 0; // 0: HandshakeMessage.ht_hello_request // 1: HandshakeMessage.ht_client_hello } /* * 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(); } /** * Both client and server modes can start handshaking; but the * message they send to do so is different. */ abstract HandshakeMessage getKickstartMessage() throws SSLException; /* * Client and Server side protocols are each driven though this * call, which processes a single message and drives the appropriate * side of the protocol state machine (depending on the subclass). */ abstract void processMessage(byte messageType, int messageLen) throws IOException; /* * Most alerts in the protocol relate to handshaking problems. * Alerts are detected as the connection reads data. */ abstract void handshakeAlert(byte description) throws SSLProtocolException; /* * 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(); } } } /* * Single access point to key calculation logic. Given the * pre-master secret and the nonces from client and server, * produce all the keying material to be used. */ void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) { SecretKey master = calculateMasterSecret(preMasterSecret, version); session.setMasterSecret(master);Lines 501–1000 of 1386. Previous lines Load more
/** * Set cipherSuite and keyExchange to the given CipherSuite. * Does not perform any verification that this is a valid selection, * this must be done before calling this method. */ void setCipherSuite(CipherSuite s) { this.cipherSuite = s; this.keyExchange = s.keyExchange; } /** * Check if the given ciphersuite is enabled and available. * Does not check if the required server certificates are available. */ boolean isNegotiable(CipherSuite s) { if (activeCipherSuites == null) { activeCipherSuites = getActiveCipherSuites(); } return activeCipherSuites.contains(s) && s.isNegotiable(); } /** * Check if the given protocol version is enabled and available. */ boolean isNegotiable(ProtocolVersion protocolVersion) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } return activeProtocols.contains(protocolVersion); } /** * Select a protocol version from the list. Called from * ServerHandshaker to negotiate protocol version. * * Return the lower of the protocol version suggested in the * clien hello and the highest supported by the server. */ ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } return activeProtocols.selectProtocolVersion(protocolVersion); } /** * Get the active cipher suites. * * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT * negotiate these cipher suites in TLS 1.1 or later mode. * * Therefore, when the active protocols only include TLS 1.1 or later, * the client cannot request to negotiate those obsoleted cipher * suites. That 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. * * 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; } /* * 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; } /** * As long as handshaking has not activated, we can * change whether session creations are allowed. * * Callers should do their own checking if handshaking * has activated. */ void setEnableSessionCreation(boolean newSessions) { enableNewSession = newSessions; } /** * Create a new read cipher and return it to caller. */ CipherBox newReadCipher() throws NoSuchAlgorithmException { BulkCipher cipher = cipherSuite.cipher; CipherBox box; if (isClient) { box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), false); svrWriteKey = null; svrWriteIV = null; } else { box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), false); clntWriteKey = null; clntWriteIV = null; } return box; } /** * Create a new write cipher and return it to caller. */ CipherBox newWriteCipher() throws NoSuchAlgorithmException { BulkCipher cipher = cipherSuite.cipher; CipherBox box; if (isClient) { box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), true); clntWriteKey = null; clntWriteIV = null; } else { box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), true); svrWriteKey = null; svrWriteIV = null; } return box; } /** * Create a new read MAC and return it to caller. */ MAC newReadMAC() throws NoSuchAlgorithmException, InvalidKeyException { MacAlg macAlg = cipherSuite.macAlg; MAC mac; if (isClient) { mac = macAlg.newMac(protocolVersion, svrMacSecret); svrMacSecret = null; } else { mac = macAlg.newMac(protocolVersion, clntMacSecret); clntMacSecret = null; } return mac; } /** * Create a new write MAC and return it to caller. */ MAC newWriteMAC() throws NoSuchAlgorithmException, InvalidKeyException { MacAlg macAlg = cipherSuite.macAlg; MAC mac; if (isClient) { mac = macAlg.newMac(protocolVersion, clntMacSecret); clntMacSecret = null; } else { mac = macAlg.newMac(protocolVersion, svrMacSecret); svrMacSecret = null; } return mac; } /* * Returns true iff the handshake sequence is done, so that * this freshly created session can become the current one. */ boolean isDone() { return state == HandshakeMessage.ht_finished; } /* * Returns the session which was created through this * handshake sequence ... should be called after isDone() * returns true. */ SSLSessionImpl getSession() { return session; } /* * Set the handshake session */ void setHandshakeSessionSE(SSLSessionImpl handshakeSession) { if (conn != null) { conn.setHandshakeSession(handshakeSession); } else { engine.setHandshakeSession(handshakeSession); } } /* * 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. */ void process_record(InputRecord r, boolean expectingFinished) throws IOException { checkThrown(); /* * Store the incoming handshake data, then see if we can * now process any completed handshake messages */ input.incomingRecord(r); /* * We don't need to create a separate delegatable task * for finished messages. */ if ((conn != null) || expectingFinished) { processLoop(); } else { delegateTask(new PrivilegedExceptionAction<Void>() { public Void run() throws Exception { processLoop(); return null; } }); } } /* * On input, we hash messages one at a time since servers may need * to access an intermediate hash to validate a CertificateVerify * message. * * Note that many handshake messages can come in one record (and often * do, to reduce network resource utilization), and one message can also * require multiple records (e.g. very large Certificate messages). */ void processLoop() throws IOException { // need to read off 4 bytes at least to get the handshake // message type and length. while (input.available() >= 4) { byte messageType; int messageLen; /* * See if we can read the handshake message header, and * then the entire handshake message. If not, wait till * we can read and process an entire message. */ input.mark(4); messageType = (byte)input.getInt8(); messageLen = input.getInt24(); if (input.available() < messageLen) { input.reset(); return; } /* * Process the messsage. We require * that processMessage() consumes the entire message. In * lieu of explicit error checks (how?!) we assume that the * data will look like garbage on encoding/processing errors, * and that other protocol code will detect such errors. * * Note that digesting is normally deferred till after the * message has been processed, though to process at least the * client's Finished message (i.e. send the server's) we need * to acccelerate that digesting. * * Also, note that hello request messages are never hashed; * that includes the hello request header, too. */ if (messageType == HandshakeMessage.ht_hello_request) { input.reset(); processMessage(messageType, messageLen); input.ignore(4 + messageLen); } else { input.mark(messageLen); processMessage(messageType, messageLen); input.digestNow(); } } } /** * Returns true iff the handshaker has been activated. * * In activated state, the handshaker may not send any messages out. */ boolean activated() { return state >= -1; } /** * Returns true iff the handshaker has sent any messages. */ boolean started() { return state >= 0; // 0: HandshakeMessage.ht_hello_request // 1: HandshakeMessage.ht_client_hello } /* * 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(); } /** * Both client and server modes can start handshaking; but the * message they send to do so is different. */ abstract HandshakeMessage getKickstartMessage() throws SSLException; /* * Client and Server side protocols are each driven though this * call, which processes a single message and drives the appropriate * side of the protocol state machine (depending on the subclass). */ abstract void processMessage(byte messageType, int messageLen) throws IOException; /* * Most alerts in the protocol relate to handshaking problems. * Alerts are detected as the connection reads data. */ abstract void handshakeAlert(byte description) throws SSLProtocolException; /* * 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(); } } } /* * Single access point to key calculation logic. Given the * pre-master secret and the nonces from client and server, * produce all the keying material to be used. */ void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) { SecretKey master = calculateMasterSecret(preMasterSecret, version); session.setMasterSecret(master);