All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.tls.TlsClientProtocol Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java APIs for the TLS, including a JSSE provider. The APIs are designed primarily to be used in conjunction with the BC FIPS provider. The APIs may also be used with other providers although if being used in a FIPS context it is the responsibility of the user to ensure that any other providers used are FIPS certified and used appropriately.

There is a newer version: 2.0.19
Show newest version
package org.bouncycastle.tls;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.bouncycastle.tls.crypto.TlsStreamSigner;
import org.bouncycastle.util.Arrays;

public class TlsClientProtocol
    extends TlsProtocol
{
    protected TlsClient tlsClient = null;
    TlsClientContextImpl tlsClientContext = null;

    protected byte[] selectedSessionID = null;

    protected TlsKeyExchange keyExchange = null;
    protected TlsAuthentication authentication = null;

    protected CertificateStatus certificateStatus = null;
    protected CertificateRequest certificateRequest = null;

    /**
     * Constructor for non-blocking mode.
*
* When data is received, use {@link #offerInput(byte[])} to provide the received ciphertext, * then use {@link #readInput(byte[], int, int)} to read the corresponding cleartext.
*
* Similarly, when data needs to be sent, use {@link #writeApplicationData(byte[], int, int)} to * provide the cleartext, then use {@link #readOutput(byte[], int, int)} to get the * corresponding ciphertext. */ public TlsClientProtocol() { super(); } /** * Constructor for blocking mode. * @param input The stream of data from the server * @param output The stream of data to the server */ public TlsClientProtocol(InputStream input, OutputStream output) { super(input, output); } /** * Initiates a TLS handshake in the role of client.
*
* In blocking mode, this will not return until the handshake is complete. * In non-blocking mode, use {@link TlsPeer#notifyHandshakeComplete()} to * receive a callback when the handshake is complete. * * @param tlsClient The {@link TlsClient} to use for the handshake. * @throws IOException If in blocking mode and handshake was not successful. */ public void connect(TlsClient tlsClient) throws IOException { if (tlsClient == null) { throw new IllegalArgumentException("'tlsClient' cannot be null"); } if (this.tlsClient != null) { throw new IllegalStateException("'connect' can only be called once"); } this.tlsClient = tlsClient; this.securityParameters = new SecurityParameters(); this.securityParameters.entity = ConnectionEnd.client; this.tlsClientContext = new TlsClientContextImpl(tlsClient.getCrypto(), securityParameters); this.securityParameters.clientRandom = createRandomBlock(tlsClient.shouldUseGMTUnixTime(), tlsClientContext); this.securityParameters.extendedPadding = tlsClient.shouldUseExtendedPadding(); this.tlsClient.init(tlsClientContext); this.recordStream.init(tlsClientContext); TlsSession sessionToResume = tlsClient.getSessionToResume(); if (sessionToResume != null && sessionToResume.isResumable()) { SessionParameters sessionParameters = sessionToResume.exportSessionParameters(); if (sessionParameters != null) { this.tlsSession = sessionToResume; this.sessionParameters = sessionParameters; } } sendClientHelloMessage(); this.connection_state = CS_CLIENT_HELLO; if (blocking) { blockForHandshake(); } } protected void cleanupHandshake() { super.cleanupHandshake(); this.selectedSessionID = null; this.keyExchange = null; this.authentication = null; this.certificateStatus = null; this.certificateRequest = null; } protected TlsContext getContext() { return tlsClientContext; } AbstractTlsContext getContextAdmin() { return tlsClientContext; } protected TlsPeer getPeer() { return tlsClient; } protected void handleHandshakeMessage(short type, ByteArrayInputStream buf) throws IOException { if (this.resumedSession) { if (type != HandshakeType.finished || this.connection_state != CS_SERVER_HELLO) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } processFinishedMessage(buf); this.connection_state = CS_SERVER_FINISHED; sendChangeCipherSpecMessage(); sendFinishedMessage(); this.connection_state = CS_CLIENT_FINISHED; completeHandshake(); return; } switch (type) { case HandshakeType.certificate: { switch (this.connection_state) { case CS_SERVER_HELLO: { handleSupplementalData(null); // NB: Fall through to next case label } case CS_SERVER_SUPPLEMENTAL_DATA: { // Parse the Certificate message and send to cipher suite ByteArrayOutputStream endPointHash = new ByteArrayOutputStream(); this.peerCertificate = Certificate.parse(getContext(), buf, endPointHash); assertEmpty(buf); securityParameters.tlsServerEndPoint = endPointHash.toByteArray(); // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus if (this.peerCertificate == null || this.peerCertificate.isEmpty()) { this.allowCertificateStatus = false; } this.authentication = tlsClient.getAuthentication(); if (this.authentication == null) { throw new TlsFatalAlert(AlertDescription.internal_error); } break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_SERVER_CERTIFICATE; break; } case HandshakeType.certificate_status: { switch (this.connection_state) { case CS_SERVER_CERTIFICATE: { if (!this.allowCertificateStatus) { /* * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the * server MUST have included an extension of type "status_request" with empty * "extension_data" in the extended server hello.. */ throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.certificateStatus = CertificateStatus.parse(buf); assertEmpty(buf); this.connection_state = CS_CERTIFICATE_STATUS; break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } case HandshakeType.finished: { switch (this.connection_state) { case CS_CLIENT_FINISHED: { if (this.expectSessionTicket) { /* * RFC 5077 3.3. This message MUST be sent if the server included a * SessionTicket extension in the ServerHello. */ throw new TlsFatalAlert(AlertDescription.unexpected_message); } // NB: Fall through to next case label } case CS_SERVER_SESSION_TICKET: { processFinishedMessage(buf); this.connection_state = CS_SERVER_FINISHED; completeHandshake(); break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } case HandshakeType.server_hello: { switch (this.connection_state) { case CS_CLIENT_HELLO: { receiveServerHelloMessage(buf); this.connection_state = CS_SERVER_HELLO; this.recordStream.notifyHelloComplete(); applyMaxFragmentLengthExtension(); if (this.resumedSession) { this.securityParameters.masterSecret = getContext().getCrypto().adoptSecret(sessionParameters.getMasterSecret()); this.recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); } else { invalidateSession(); this.tlsSession = TlsUtils.importSession(this.selectedSessionID, null); this.sessionParameters = null; } break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } case HandshakeType.supplemental_data: { switch (this.connection_state) { case CS_SERVER_HELLO: { handleSupplementalData(readSupplementalDataMessage(buf)); break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } case HandshakeType.server_hello_done: { switch (this.connection_state) { case CS_SERVER_HELLO: { handleSupplementalData(null); // NB: Fall through to next case label } case CS_SERVER_SUPPLEMENTAL_DATA: { this.authentication = null; // NB: Fall through to next case label } case CS_SERVER_CERTIFICATE: case CS_CERTIFICATE_STATUS: { handleServerCertificate(); // There was no server key exchange message; check it's OK this.keyExchange.skipServerKeyExchange(); // NB: Fall through to next case label } case CS_SERVER_KEY_EXCHANGE: case CS_CERTIFICATE_REQUEST: { assertEmpty(buf); this.connection_state = CS_SERVER_HELLO_DONE; Vector clientSupplementalData = tlsClient.getClientSupplementalData(); if (clientSupplementalData != null) { sendSupplementalDataMessage(clientSupplementalData); } this.connection_state = CS_CLIENT_SUPPLEMENTAL_DATA; TlsCredentials clientCredentials = null; TlsCredentialedSigner credentialedSigner = null; TlsStreamSigner streamSigner = null; if (certificateRequest == null) { this.keyExchange.skipClientCredentials(); } else { clientCredentials = validateCredentials(this.authentication.getClientCredentials(certificateRequest)); if (clientCredentials == null) { this.keyExchange.skipClientCredentials(); /* * RFC 5246 If no suitable certificate is available, the client MUST send a * certificate message containing no certificates. * * NOTE: In previous RFCs, this was SHOULD instead of MUST. */ sendCertificateMessage(Certificate.EMPTY_CHAIN, null); } else { this.keyExchange.processClientCredentials(clientCredentials); sendCertificateMessage(clientCredentials.getCertificate(), null); if (clientCredentials instanceof TlsCredentialedSigner) { credentialedSigner = (TlsCredentialedSigner)clientCredentials; streamSigner = credentialedSigner.getStreamSigner(); } } } this.connection_state = CS_CLIENT_CERTIFICATE; boolean forceBuffering = streamSigner != null; TlsUtils.sealHandshakeHash(getContext(), this.recordStream.getHandshakeHash(), forceBuffering); /* * Send the client key exchange message, depending on the key exchange we are using * in our CipherSuite. */ sendClientKeyExchangeMessage(); this.connection_state = CS_CLIENT_KEY_EXCHANGE; TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish(); this.securityParameters.sessionHash = TlsUtils.getCurrentPRFHash(prepareFinishHash); establishMasterSecret(getContext(), keyExchange); recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); if (credentialedSigner != null) { DigitallySigned certificateVerify = TlsUtils.generateCertificateVerify(getContext(), credentialedSigner, streamSigner, prepareFinishHash); sendCertificateVerifyMessage(certificateVerify); this.connection_state = CS_CERTIFICATE_VERIFY; } sendChangeCipherSpecMessage(); sendFinishedMessage(); break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_CLIENT_FINISHED; break; } case HandshakeType.server_key_exchange: { switch (this.connection_state) { case CS_SERVER_HELLO: { handleSupplementalData(null); // NB: Fall through to next case label } case CS_SERVER_SUPPLEMENTAL_DATA: { this.authentication = null; // NB: Fall through to next case label } case CS_SERVER_CERTIFICATE: case CS_CERTIFICATE_STATUS: { handleServerCertificate(); this.keyExchange.processServerKeyExchange(buf); assertEmpty(buf); break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_SERVER_KEY_EXCHANGE; break; } case HandshakeType.certificate_request: { switch (this.connection_state) { case CS_SERVER_CERTIFICATE: case CS_CERTIFICATE_STATUS: { handleServerCertificate(); // There was no server key exchange message; check it's OK this.keyExchange.skipServerKeyExchange(); // NB: Fall through to next case label } case CS_SERVER_KEY_EXCHANGE: { if (this.authentication == null) { /* * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server * to request client identification. */ throw new TlsFatalAlert(AlertDescription.handshake_failure); } this.certificateRequest = CertificateRequest.parse(getContext(), buf); assertEmpty(buf); this.certificateRequest = TlsUtils.validateCertificateRequest(this.certificateRequest, this.keyExchange); /* * TODO Give the client a chance to immediately select the CertificateVerify hash * algorithm here to avoid tracking the other hash algorithms unnecessarily? */ TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(), this.certificateRequest.getSupportedSignatureAlgorithms()); break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_CERTIFICATE_REQUEST; break; } case HandshakeType.session_ticket: { switch (this.connection_state) { case CS_CLIENT_FINISHED: { if (!this.expectSessionTicket) { /* * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a * SessionTicket extension in the ServerHello. */ throw new TlsFatalAlert(AlertDescription.unexpected_message); } /* * RFC 5077 3.4. If the client receives a session ticket from the server, then it * discards any Session ID that was sent in the ServerHello. */ invalidateSession(); receiveNewSessionTicketMessage(buf); break; } default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_SERVER_SESSION_TICKET; break; } case HandshakeType.hello_request: { assertEmpty(buf); /* * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the * client is currently negotiating a session. This message may be ignored by the client * if it does not wish to renegotiate a session, or the client may, if it wishes, * respond with a no_renegotiation alert. */ if (this.connection_state == CS_END) { refuseRenegotiation(); } break; } case HandshakeType.client_hello: case HandshakeType.client_key_exchange: case HandshakeType.certificate_verify: case HandshakeType.hello_verify_request: default: throw new TlsFatalAlert(AlertDescription.unexpected_message); } } protected void handleServerCertificate() throws IOException { if (this.authentication == null) { // There was no server certificate message; check it's OK securityParameters.tlsServerEndPoint = TlsUtils.EMPTY_BYTES; this.keyExchange.skipServerCredentials(); } else { TlsUtils.processServerCertificate(peerCertificate, certificateStatus, keyExchange, authentication, clientExtensions, serverExtensions); } } protected void handleSupplementalData(Vector serverSupplementalData) throws IOException { this.tlsClient.processServerSupplementalData(serverSupplementalData); this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA; this.keyExchange = tlsClient.getKeyExchange(); this.keyExchange.init(getContext()); } protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf) throws IOException { NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf); assertEmpty(buf); tlsClient.notifyNewSessionTicket(newSessionTicket); } protected void receiveServerHelloMessage(ByteArrayInputStream buf) throws IOException { ProtocolVersion server_version = TlsUtils.readVersion(buf); { if (!TlsUtils.isTLSv10(server_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } // Check that this matches what the server is sending in the record layer if (!server_version.equals(this.recordStream.getReadVersion())) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ProtocolVersion client_version = getContext().getClientVersion(); if (!server_version.isEqualOrEarlierVersionOf(client_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.recordStream.setWriteVersion(server_version); getContextAdmin().setServerVersion(server_version); this.tlsClient.notifyServerVersion(server_version); } /* * Read the server random */ this.securityParameters.serverRandom = TlsUtils.readFully(32, buf); this.selectedSessionID = TlsUtils.readOpaque8(buf); if (this.selectedSessionID.length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.tlsClient.notifySessionID(this.selectedSessionID); this.resumedSession = this.selectedSessionID.length > 0 && this.tlsSession != null && Arrays.areEqual(this.selectedSessionID, this.tlsSession.getSessionID()); /* * Find out which CipherSuite the server has chosen and check that it was one of the offered * ones, and is a valid selection for the negotiated version. */ int selectedCipherSuite = TlsUtils.readUint16(buf); if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.isSCSV(selectedCipherSuite) || !TlsUtils.isValidCipherSuiteForVersion(selectedCipherSuite, getContext().getServerVersion())) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.tlsClient.notifySelectedCipherSuite(selectedCipherSuite); /* * Find out which CompressionMethod the server has chosen and check that it was one of the * offered ones. */ short selectedCompressionMethod = TlsUtils.readUint8(buf); if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod); /* * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server * hello message when the client has requested extended functionality via the extended * client hello message specified in Section 2.1. ... Note that the extended server hello * message is only sent in response to an extended client hello message. This prevents the * possibility that the extended server hello message could "break" existing TLS 1.0 * clients. */ this.serverExtensions = readExtensions(buf); /* * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an * extended client hello message. * * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server * Hello is always allowed. */ if (this.serverExtensions != null) { Enumeration e = this.serverExtensions.keys(); while (e.hasMoreElements()) { Integer extType = (Integer)e.nextElement(); /* * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a * ClientHello containing only the SCSV is an explicit exception to the prohibition * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is * only allowed because the client is signaling its willingness to receive the * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ if (extType.equals(EXT_RenegotiationInfo)) { continue; } /* * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the * same extension type appeared in the corresponding ClientHello. If a client * receives an extension type in ServerHello that it did not request in the * associated ClientHello, it MUST abort the handshake with an unsupported_extension * fatal alert. */ if (null == TlsUtils.getExtensionData(this.clientExtensions, extType)) { throw new TlsFatalAlert(AlertDescription.unsupported_extension); } /* * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and send a server hello containing no * extensions[.] */ if (this.resumedSession) { // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats // throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } /* * RFC 5746 3.4. Client Behavior: Initial Handshake */ { /* * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo); if (renegExtData != null) { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The * client MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake (by sending a fatal * handshake_failure alert). */ this.secure_renegotiation = true; if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming this.tlsClient.notifySecureRenegotiation(this.secure_renegotiation); Hashtable sessionClientExtensions = clientExtensions, sessionServerExtensions = serverExtensions; if (this.resumedSession) { if (selectedCipherSuite != this.sessionParameters.getCipherSuite() || selectedCompressionMethod != this.sessionParameters.getCompressionAlgorithm() || !server_version.equals(this.sessionParameters.getNegotiatedVersion())) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } sessionClientExtensions = null; sessionServerExtensions = this.sessionParameters.readServerExtensions(); } this.securityParameters.cipherSuite = selectedCipherSuite; this.securityParameters.compressionAlgorithm = selectedCompressionMethod; if (sessionServerExtensions != null) { { /* * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the * client. */ boolean serverSentEncryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(sessionServerExtensions); if (serverSentEncryptThenMAC && !TlsUtils.isBlockCipherSuite(selectedCipherSuite)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.securityParameters.encryptThenMAC = serverSentEncryptThenMAC; } this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(sessionServerExtensions); this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(sessionServerExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in * a session resumption handshake. */ this.allowCertificateStatus = !this.resumedSession && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions, TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter); this.expectSessionTicket = !this.resumedSession && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions, TlsProtocol.EXT_SessionTicket, AlertDescription.illegal_parameter); } /* * TODO[session-hash] * * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes * that do not use the extended master secret [..]. (and see 5.2, 5.3) */ if (sessionClientExtensions != null) { this.tlsClient.processServerExtensions(sessionServerExtensions); } this.securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(), this.securityParameters.getCipherSuite()); /* * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify * verify_data_length has a verify_data_length equal to 12. This includes all * existing cipher suites. */ this.securityParameters.verifyDataLength = 12; } protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify) throws IOException { HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify); certificateVerify.encode(message); message.writeToRecordStream(); } protected void sendClientHelloMessage() throws IOException { this.recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion()); ProtocolVersion client_version = this.tlsClient.getClientVersion(); if (!TlsUtils.isTLSv10(client_version)) { throw new TlsFatalAlert(AlertDescription.internal_error); } getContextAdmin().setClientVersion(client_version); /* * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a * Session ID in the TLS ClientHello. */ byte[] session_id = TlsUtils.EMPTY_BYTES; if (this.tlsSession != null) { session_id = this.tlsSession.getSessionID(); if (session_id == null || session_id.length > 32) { session_id = TlsUtils.EMPTY_BYTES; } } boolean fallback = this.tlsClient.isFallback(); this.offeredCipherSuites = this.tlsClient.getCipherSuites(); this.offeredCompressionMethods = this.tlsClient.getCompressionMethods(); if (session_id.length > 0 && this.sessionParameters != null) { if (!Arrays.contains(this.offeredCipherSuites, sessionParameters.getCipherSuite()) || !Arrays.contains(this.offeredCompressionMethods, sessionParameters.getCompressionAlgorithm())) { session_id = TlsUtils.EMPTY_BYTES; } } this.clientExtensions = this.tlsClient.getClientExtensions(); HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); TlsUtils.writeVersion(client_version, message); message.write(this.securityParameters.getClientRandom()); TlsUtils.writeOpaque8(session_id, message); // Cipher Suites (and SCSV) { /* * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo); boolean noRenegExt = (null == renegExtData); boolean noRenegSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); if (noRenegExt && noRenegSCSV) { // TODO Consider whether to default to a client extension instead // this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.clientExtensions); // this.clientExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)); this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); } /* * RFC 7507 4. If a client sends a ClientHello.client_version containing a lower value * than the latest (highest-valued) version supported by the client, it SHOULD include * the TLS_FALLBACK_SCSV cipher suite value in ClientHello.cipher_suites [..]. (The * client SHOULD put TLS_FALLBACK_SCSV after all cipher suites that it actually intends * to negotiate.) */ if (fallback && !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)) { this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV); } TlsUtils.writeUint16ArrayWithUint16Length(offeredCipherSuites, message); } TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message); if (clientExtensions != null) { writeExtensions(message, clientExtensions); } message.writeToRecordStream(); } protected void sendClientKeyExchangeMessage() throws IOException { HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange); this.keyExchange.generateClientKeyExchange(message); message.writeToRecordStream(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy