+ switch (negotiate.State)
+ {
+ case NegotiateState.SentClientHello:
+ this.protocol.EndSendRecord (result);
+
+ // we are now ready to ready the receive the hello response.
+ negotiate.State = NegotiateState.ReceiveClientHelloResponse;
+
+ // Start reading the client hello response
+ this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
+ break;
+
+ case NegotiateState.ReceiveClientHelloResponse:
+ this.SafeEndReceiveRecord (result, true);
+
+ if (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone &&
+ (!this.context.AbbreviatedHandshake || this.context.LastHandshakeMsg != HandshakeType.ServerHello)) {
+ // Read next record (skip empty, e.g. warnings alerts)
+ this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
+ break;
+ }
+
+ // special case for abbreviated handshake where no ServerHelloDone is sent from the server
+ if (this.context.AbbreviatedHandshake) {
+ ClientSessionCache.SetContextFromCache (this.context);
+ this.context.Negotiating.Cipher.ComputeKeys ();
+ this.context.Negotiating.Cipher.InitializeCipher ();
+
+ negotiate.State = NegotiateState.SentCipherSpec;
+
+ // Send Change Cipher Spec message with the current cipher
+ // or as plain text if this is the initial negotiation
+ this.protocol.BeginSendChangeCipherSpec(NegotiateAsyncWorker, negotiate);
+ } else {
+ // Send client certificate if requested
+ // even if the server ask for it it _may_ still be optional
+ bool clientCertificate = this.context.ServerSettings.CertificateRequest;
+
+ using (var memstream = new MemoryStream())
+ {
+ // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
+ // the current design doesn't allow a very cute way to handle
+ // SSL3 alert warning for NoCertificate (41).
+ if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
+ {
+ clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
+ (this.context.ClientSettings.Certificates.Count > 0));
+ // this works well with OpenSSL (but only for SSL3)
+ }
+
+ byte[] record = null;
+
+ if (clientCertificate)
+ {
+ record = this.protocol.EncodeHandshakeRecord(HandshakeType.Certificate);
+ memstream.Write(record, 0, record.Length);
+ }
+
+ // Send Client Key Exchange
+ record = this.protocol.EncodeHandshakeRecord(HandshakeType.ClientKeyExchange);
+ memstream.Write(record, 0, record.Length);
+
+ // Now initialize session cipher with the generated keys
+ this.context.Negotiating.Cipher.InitializeCipher();
+
+ // Send certificate verify if requested (optional)
+ if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
+ {
+ record = this.protocol.EncodeHandshakeRecord(HandshakeType.CertificateVerify);
+ memstream.Write(record, 0, record.Length);
+ }
+
+ // send the chnage cipher spec.
+ this.protocol.SendChangeCipherSpec(memstream);
+
+ // Send Finished message
+ record = this.protocol.EncodeHandshakeRecord(HandshakeType.Finished);
+ memstream.Write(record, 0, record.Length);
+
+ negotiate.State = NegotiateState.SentKeyExchange;
+
+ // send all the records.
+ this.innerStream.BeginWrite (memstream.GetBuffer (), 0, (int)memstream.Length, NegotiateAsyncWorker, negotiate);
+ }
+ }
+ break;
+
+ case NegotiateState.SentKeyExchange:
+ this.innerStream.EndWrite (result);
+
+ negotiate.State = NegotiateState.ReceiveFinishResponse;
+
+ this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
+
+ break;
+
+ case NegotiateState.ReceiveFinishResponse:
+ this.SafeEndReceiveRecord (result);
+
+ // Read record until server finished is received
+ if (this.context.HandshakeState != HandshakeState.Finished) {
+ // If all goes well this will process messages:
+ // Change Cipher Spec
+ // Server finished
+ this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
+ }
+ else {
+ // Reset Handshake messages information
+ this.context.HandshakeMessages.Reset ();
+
+ // Clear Key Info
+ this.context.ClearKeyInfo();
+
+ negotiate.SetComplete ();
+ }
+ break;
+
+
+ case NegotiateState.SentCipherSpec:
+ this.protocol.EndSendChangeCipherSpec (result);
+
+ negotiate.State = NegotiateState.ReceiveCipherSpecResponse;
+
+ // Start reading the cipher spec response
+ this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
+ break;
+
+ case NegotiateState.ReceiveCipherSpecResponse:
+ this.SafeEndReceiveRecord (result, true);
+
+ if (this.context.HandshakeState != HandshakeState.Finished)
+ {
+ this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
+ }
+ else
+ {
+ negotiate.State = NegotiateState.SentFinished;
+ this.protocol.BeginSendRecord(HandshakeType.Finished, NegotiateAsyncWorker, negotiate);
+ }
+ break;
+
+ case NegotiateState.SentFinished:
+ this.protocol.EndSendRecord (result);
+
+ // Reset Handshake messages information
+ this.context.HandshakeMessages.Reset ();
+
+ // Clear Key Info
+ this.context.ClearKeyInfo();