public virtual void Connect(TlsClient tlsClient)
{
if (tlsClient == null)
throw new ArgumentNullException("tlsClient");
if (this.tlsClient != null)
throw new InvalidOperationException("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, 4, 28);
TlsUtilities.WriteGmtUnixTime(securityParameters.clientRandom, 0);
this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
this.tlsClient = tlsClient;
this.tlsClient.Init(tlsClientContext);
MemoryStream outStr = new MemoryStream();
TlsUtilities.WriteVersion(outStr);
outStr.Write(securityParameters.clientRandom, 0, 32);
/*
* Length of Session id
*/
TlsUtilities.WriteUint8(0, outStr);
this.offeredCipherSuites = this.tlsClient.GetCipherSuites();
// ExtensionType -> 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.
*/
bool noRenegExt = clientExtensions == null
|| !clientExtensions.Contains(ExtensionType.renegotiation_info);
int count = offeredCipherSuites.Length;
if (noRenegExt)
{
// Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
++count;
}
TlsUtilities.WriteUint16(2 * count, outStr);
for (int i = 0; i < offeredCipherSuites.Length; ++i)
{
TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
}
if (noRenegExt)
{
TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
}
}
/*
* Compression methods, just the null method.
*/
this.offeredCompressionMethods = tlsClient.GetCompressionMethods();
{
TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
for (int i = 0; i < offeredCompressionMethods.Length; ++i)
{
TlsUtilities.WriteUint8((byte)offeredCompressionMethods[i], outStr);
}
}
// Extensions
if (clientExtensions != null)
{
MemoryStream ext = new MemoryStream();
foreach (ExtensionType extType in clientExtensions.Keys)
{
WriteExtension(ext, extType, (byte[])clientExtensions[extType]);
}
TlsUtilities.WriteOpaque16(ext.ToArray(), outStr);
}
MemoryStream bos = new MemoryStream();
TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
TlsUtilities.WriteUint24((int)outStr.Length, bos);
byte[] outBytes = outStr.ToArray();
bos.Write(outBytes, 0, outBytes.Length);
byte[] message = bos.ToArray();
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.tlsStream = new TlsStream(this);
}