public void generateClientKeyExchange(OutputStream os) throws IOException { /* * Choose a PremasterSecret and send it encrypted to the server */ premasterSecret = new byte[48]; context.getSecureRandom().nextBytes(premasterSecret); TlsUtils.writeVersion(premasterSecret, 0); PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine()); encoding.init( true, new ParametersWithRandom(this.rsaServerPublicKey, context.getSecureRandom())); try { byte[] keData = encoding.processBlock(premasterSecret, 0, premasterSecret.length); TlsUtils.writeUint24(keData.length + 2, os); TlsUtils.writeOpaque16(keData, os); } catch (InvalidCipherTextException e) { /* * This should never happen, only during decryption. */ throw new TlsFatalAlert(AlertDescription.internal_error); } }
protected byte[] generateServerHello(ServerHandshakeState state) throws IOException { SecurityParameters securityParameters = state.serverContext.getSecurityParameters(); ByteArrayOutputStream buf = new ByteArrayOutputStream(); ProtocolVersion server_version = state.server.getServerVersion(); if (!server_version.isEqualOrEarlierVersionOf(state.serverContext.getClientVersion())) { throw new TlsFatalAlert(AlertDescription.internal_error); } // TODO Read RFCs for guidance on the expected record layer version number // recordStream.setReadVersion(server_version); // recordStream.setWriteVersion(server_version); // recordStream.setRestrictReadVersion(true); state.serverContext.setServerVersion(server_version); TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf); buf.write(securityParameters.getServerRandom()); /* * The server may return an empty session_id to indicate that the session will not be cached * and therefore cannot be resumed. */ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf); state.selectedCipherSuite = state.server.getSelectedCipherSuite(); if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite) || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { throw new TlsFatalAlert(AlertDescription.internal_error); } validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error); state.selectedCompressionMethod = state.server.getSelectedCompressionMethod(); if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } TlsUtils.writeUint16(state.selectedCipherSuite, buf); TlsUtils.writeUint8(state.selectedCompressionMethod, buf); state.serverExtensions = state.server.getServerExtensions(); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ if (state.secure_renegotiation) { byte[] renegExtData = TlsUtils.getExtensionData(state.serverExtensions, TlsProtocol.EXT_RenegotiationInfo); boolean noRenegExt = (null == renegExtData); if (noRenegExt) { /* * 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 the secure_renegotiation flag is set to TRUE, the server MUST include an empty * "renegotiation_info" extension in the ServerHello message. */ state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions); state.serverExtensions.put( TlsProtocol.EXT_RenegotiationInfo, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)); } } if (state.serverExtensions != null) { state.maxFragmentLength = evaluateMaxFragmentLengthExtension( state.clientExtensions, state.serverExtensions, AlertDescription.internal_error); securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(state.serverExtensions); state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData( state.serverExtensions, TlsExtensionsUtils.EXT_status_request, AlertDescription.internal_error); state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData( state.serverExtensions, TlsProtocol.EXT_SessionTicket, AlertDescription.internal_error); TlsProtocol.writeExtensions(buf, state.serverExtensions); } return buf.toByteArray(); }
/** * Connects to the remote system using client authentication * * @param tlsClient * @throws IOException If 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"); } /* * Send Client hello * * First, generate some random data. */ this.securityParameters = new SecurityParameters(); this.securityParameters.clientRandom = new byte[32]; random.nextBytes(securityParameters.clientRandom); TlsUtils.writeGMTUnixTime(securityParameters.clientRandom, 0); this.tlsClientContext = new TlsClientContextImpl(random, securityParameters); this.rs.init(tlsClientContext); this.tlsClient = tlsClient; this.tlsClient.init(tlsClientContext); ByteArrayOutputStream os = new ByteArrayOutputStream(); ProtocolVersion client_version = this.tlsClient.getClientVersion(); this.tlsClientContext.setClientVersion(client_version); // TODO For SSLv3 support, server version needs to be set to ProtocolVersion.SSLv3 this.tlsClientContext.setServerVersion(client_version); TlsUtils.writeVersion(client_version, os); os.write(securityParameters.clientRandom); /* * Length of Session id */ TlsUtils.writeUint8((short) 0, os); /* * Cipher suites */ this.offeredCipherSuites = this.tlsClient.getCipherSuites(); // Integer -> byte[] this.clientExtensions = this.tlsClient.getClientExtensions(); // 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. */ boolean noRenegExt = clientExtensions == null || clientExtensions.get(EXT_RenegotiationInfo) == null; int count = offeredCipherSuites.length; if (noRenegExt) { // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV ++count; } TlsUtils.writeUint16(2 * count, os); TlsUtils.writeUint16Array(offeredCipherSuites, os); if (noRenegExt) { TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, os); } } // Compression methods this.offeredCompressionMethods = this.tlsClient.getCompressionMethods(); TlsUtils.writeUint8((short) offeredCompressionMethods.length, os); TlsUtils.writeUint8Array(offeredCompressionMethods, os); // Extensions if (clientExtensions != null) { ByteArrayOutputStream ext = new ByteArrayOutputStream(); Enumeration keys = clientExtensions.keys(); while (keys.hasMoreElements()) { Integer extType = (Integer) keys.nextElement(); writeExtension(ext, extType, (byte[]) clientExtensions.get(extType)); } TlsUtils.writeOpaque16(ext.toByteArray(), os); } ByteArrayOutputStream bos = new ByteArrayOutputStream(); TlsUtils.writeUint8(HandshakeType.client_hello, bos); TlsUtils.writeUint24(os.size(), bos); bos.write(os.toByteArray()); byte[] message = bos.toByteArray(); safeWriteMessage(ContentType.handshake, message, 0, message.length); connection_state = CS_CLIENT_HELLO_SEND; /* * We will now read data, until we have completed the handshake. */ while (connection_state != CS_DONE) { safeReadData(); } this.tlsInputStream = new TlsInputStream(this); this.tlsOutputStream = new TlsOutputStream(this); }