internal virtual DtlsTransport ClientHandshake(ClientHandshakeState state, DtlsRecordLayer recordLayer)
        {
            SecurityParameters    securityParameters = state.clientContext.SecurityParameters;
            DtlsReliableHandshake handshake          = new DtlsReliableHandshake(state.clientContext, recordLayer);

            byte[] clientHelloBody = GenerateClientHello(state, state.client);

            recordLayer.SetWriteVersion(ProtocolVersion.DTLSv10);

            handshake.SendMessage(HandshakeType.client_hello, clientHelloBody);

            DtlsReliableHandshake.Message serverMessage = handshake.ReceiveMessage();

            while (serverMessage.Type == HandshakeType.hello_verify_request)
            {
                ProtocolVersion recordLayerVersion = recordLayer.ReadVersion;
                ProtocolVersion client_version     = state.clientContext.ClientVersion;

                /*
                 * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0 regardless of
                 * the version of TLS that is expected to be negotiated. DTLS 1.2 and 1.0 clients MUST use
                 * the version solely to indicate packet formatting (which is the same in both DTLS 1.2 and
                 * 1.0) and not as part of version negotiation.
                 */
                if (!recordLayerVersion.IsEqualOrEarlierVersionOf(client_version))
                {
                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                }

                recordLayer.ReadVersion = null;

                byte[] cookie  = ProcessHelloVerifyRequest(state, serverMessage.Body);
                byte[] patched = PatchClientHelloWithCookie(clientHelloBody, cookie);

                handshake.ResetHandshakeMessagesDigest();
                handshake.SendMessage(HandshakeType.client_hello, patched);

                serverMessage = handshake.ReceiveMessage();
            }

            if (serverMessage.Type == HandshakeType.server_hello)
            {
                ProtocolVersion recordLayerVersion = recordLayer.ReadVersion;
                ReportServerVersion(state, recordLayerVersion);
                recordLayer.SetWriteVersion(recordLayerVersion);

                ProcessServerHello(state, serverMessage.Body);
            }
            else
            {
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }

            handshake.NotifyHelloComplete();

            ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.maxFragmentLength);

            if (state.resumedSession)
            {
                securityParameters.masterSecret = Arrays.Clone(state.sessionParameters.MasterSecret);
                recordLayer.InitPendingEpoch(state.client.GetCipher());

                // NOTE: Calculated exclusive of the actual Finished message from the server
                byte[] resExpectedServerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.server_finished,
                                                                                      TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null));
                ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), resExpectedServerVerifyData);

                // NOTE: Calculated exclusive of the Finished message itself
                byte[] resClientVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.client_finished,
                                                                              TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null));
                handshake.SendMessage(HandshakeType.finished, resClientVerifyData);

                handshake.Finish();

                state.clientContext.SetResumableSession(state.tlsSession);

                state.client.NotifyHandshakeComplete();

                return(new DtlsTransport(recordLayer));
            }

            InvalidateSession(state);

            if (state.selectedSessionID.Length > 0)
            {
                state.tlsSession = new TlsSessionImpl(state.selectedSessionID, null);
            }

            serverMessage = handshake.ReceiveMessage();

            if (serverMessage.Type == HandshakeType.supplemental_data)
            {
                ProcessServerSupplementalData(state, serverMessage.Body);
                serverMessage = handshake.ReceiveMessage();
            }
            else
            {
                state.client.ProcessServerSupplementalData(null);
            }

            state.keyExchange = state.client.GetKeyExchange();
            state.keyExchange.Init(state.clientContext);

            Certificate serverCertificate = null;

            if (serverMessage.Type == HandshakeType.certificate)
            {
                serverCertificate = ProcessServerCertificate(state, serverMessage.Body);
                serverMessage     = handshake.ReceiveMessage();
            }
            else
            {
                // Okay, Certificate is optional
                state.keyExchange.SkipServerCredentials();
            }

            // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
            if (serverCertificate == null || serverCertificate.IsEmpty)
            {
                state.allowCertificateStatus = false;
            }

            if (serverMessage.Type == HandshakeType.certificate_status)
            {
                ProcessCertificateStatus(state, serverMessage.Body);
                serverMessage = handshake.ReceiveMessage();
            }
            else
            {
                // Okay, CertificateStatus is optional
            }

            if (serverMessage.Type == HandshakeType.server_key_exchange)
            {
                ProcessServerKeyExchange(state, serverMessage.Body);
                serverMessage = handshake.ReceiveMessage();
            }
            else
            {
                // Okay, ServerKeyExchange is optional
                state.keyExchange.SkipServerKeyExchange();
            }

            if (serverMessage.Type == HandshakeType.certificate_request)
            {
                ProcessCertificateRequest(state, serverMessage.Body);

                /*
                 * TODO Give the client a chance to immediately select the CertificateVerify hash
                 * algorithm here to avoid tracking the other hash algorithms unnecessarily?
                 */
                TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash,
                                                 state.certificateRequest.SupportedSignatureAlgorithms);

                serverMessage = handshake.ReceiveMessage();
            }
            else
            {
                // Okay, CertificateRequest is optional
            }

            if (serverMessage.Type == HandshakeType.server_hello_done)
            {
                if (serverMessage.Body.Length != 0)
                {
                    throw new TlsFatalAlert(AlertDescription.decode_error);
                }
            }
            else
            {
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }

            handshake.HandshakeHash.SealHashAlgorithms();

            IList clientSupplementalData = state.client.GetClientSupplementalData();

            if (clientSupplementalData != null)
            {
                byte[] supplementalDataBody = GenerateSupplementalData(clientSupplementalData);
                handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody);
            }

            if (state.certificateRequest != null)
            {
                state.clientCredentials = state.authentication.GetClientCredentials(state.clientContext, state.certificateRequest);

                /*
                 * 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.
                 */
                Certificate clientCertificate = null;
                if (state.clientCredentials != null)
                {
                    clientCertificate = state.clientCredentials.Certificate;
                }
                if (clientCertificate == null)
                {
                    clientCertificate = Certificate.EmptyChain;
                }

                byte[] certificateBody = GenerateCertificate(clientCertificate);
                handshake.SendMessage(HandshakeType.certificate, certificateBody);
            }

            if (state.clientCredentials != null)
            {
                state.keyExchange.ProcessClientCredentials(state.clientCredentials);
            }
            else
            {
                state.keyExchange.SkipClientCredentials();
            }

            byte[] clientKeyExchangeBody = GenerateClientKeyExchange(state);
            handshake.SendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody);

            TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish();

            securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.clientContext, prepareFinishHash, null);

            TlsProtocol.EstablishMasterSecret(state.clientContext, state.keyExchange);
            recordLayer.InitPendingEpoch(state.client.GetCipher());

            if (state.clientCredentials != null && state.clientCredentials is TlsSignerCredentials)
            {
                TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials;

                /*
                 * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
                 */
                SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
                    state.clientContext, signerCredentials);

                byte[] hash;
                if (signatureAndHashAlgorithm == null)
                {
                    hash = securityParameters.SessionHash;
                }
                else
                {
                    hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
                }

                byte[]          signature             = signerCredentials.GenerateCertificateSignature(hash);
                DigitallySigned certificateVerify     = new DigitallySigned(signatureAndHashAlgorithm, signature);
                byte[]          certificateVerifyBody = GenerateCertificateVerify(state, certificateVerify);
                handshake.SendMessage(HandshakeType.certificate_verify, certificateVerifyBody);
            }

            // NOTE: Calculated exclusive of the Finished message itself
            byte[] clientVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.client_finished,
                                                                       TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null));
            handshake.SendMessage(HandshakeType.finished, clientVerifyData);

            if (state.expectSessionTicket)
            {
                serverMessage = handshake.ReceiveMessage();
                if (serverMessage.Type == HandshakeType.session_ticket)
                {
                    ProcessNewSessionTicket(state, serverMessage.Body);
                }
                else
                {
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
            }

            // NOTE: Calculated exclusive of the actual Finished message from the server
            byte[] expectedServerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.server_finished,
                                                                               TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null));
            ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedServerVerifyData);

            handshake.Finish();

            if (state.tlsSession != null)
            {
                state.sessionParameters = new SessionParameters.Builder()
                                          .SetCipherSuite(securityParameters.CipherSuite)
                                          .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm)
                                          .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
                                          .SetMasterSecret(securityParameters.MasterSecret)
                                          .SetPeerCertificate(serverCertificate)
                                          .SetPskIdentity(securityParameters.PskIdentity)
                                          .SetSrpIdentity(securityParameters.SrpIdentity)
                                          // TODO Consider filtering extensions that aren't relevant to resumed sessions
                                          .SetServerExtensions(state.serverExtensions)
                                          .Build();

                state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters);

                state.clientContext.SetResumableSession(state.tlsSession);
            }

            state.client.NotifyHandshakeComplete();

            return(new DtlsTransport(recordLayer));
        }
        internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer)
        {
            SecurityParameters    securityParameters = state.serverContext.SecurityParameters;
            DtlsReliableHandshake handshake          = new DtlsReliableHandshake(state.serverContext, recordLayer);

            DtlsReliableHandshake.Message clientMessage = handshake.ReceiveMessage();

            // NOTE: DTLSRecordLayer requires any DTLS version, we don't otherwise constrain this
            //ProtocolVersion recordLayerVersion = recordLayer.ReadVersion;

            if (clientMessage.Type == HandshakeType.client_hello)
            {
                ProcessClientHello(state, clientMessage.Body);
            }
            else
            {
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }

            {
                byte[] serverHelloBody = GenerateServerHello(state);

                ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.maxFragmentLength);

                ProtocolVersion recordLayerVersion = state.serverContext.ServerVersion;
                recordLayer.ReadVersion = recordLayerVersion;
                recordLayer.SetWriteVersion(recordLayerVersion);

                handshake.SendMessage(HandshakeType.server_hello, serverHelloBody);
            }

            handshake.NotifyHelloComplete();

            IList serverSupplementalData = state.server.GetServerSupplementalData();

            if (serverSupplementalData != null)
            {
                byte[] supplementalDataBody = GenerateSupplementalData(serverSupplementalData);
                handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody);
            }

            state.keyExchange = state.server.GetKeyExchange();
            state.keyExchange.Init(state.serverContext);

            state.serverCredentials = state.server.GetCredentials();

            Certificate serverCertificate = null;

            if (state.serverCredentials == null)
            {
                state.keyExchange.SkipServerCredentials();
            }
            else
            {
                state.keyExchange.ProcessServerCredentials(state.serverCredentials);

                serverCertificate = state.serverCredentials.Certificate;
                byte[] certificateBody = GenerateCertificate(serverCertificate);
                handshake.SendMessage(HandshakeType.certificate, certificateBody);
            }

            // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
            if (serverCertificate == null || serverCertificate.IsEmpty)
            {
                state.allowCertificateStatus = false;
            }

            if (state.allowCertificateStatus)
            {
                CertificateStatus certificateStatus = state.server.GetCertificateStatus();
                if (certificateStatus != null)
                {
                    byte[] certificateStatusBody = GenerateCertificateStatus(state, certificateStatus);
                    handshake.SendMessage(HandshakeType.certificate_status, certificateStatusBody);
                }
            }

            byte[] serverKeyExchange = state.keyExchange.GenerateServerKeyExchange();
            if (serverKeyExchange != null)
            {
                handshake.SendMessage(HandshakeType.server_key_exchange, serverKeyExchange);
            }

            if (state.serverCredentials != null)
            {
                state.certificateRequest = state.server.GetCertificateRequest();
                if (state.certificateRequest != null)
                {
                    if (TlsUtilities.IsTlsV12(state.serverContext) != (state.certificateRequest.SupportedSignatureAlgorithms != null))
                    {
                        throw new TlsFatalAlert(AlertDescription.internal_error);
                    }

                    state.keyExchange.ValidateCertificateRequest(state.certificateRequest);

                    byte[] certificateRequestBody = GenerateCertificateRequest(state, state.certificateRequest);
                    handshake.SendMessage(HandshakeType.certificate_request, certificateRequestBody);

                    TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash,
                                                     state.certificateRequest.SupportedSignatureAlgorithms);
                }
            }

            handshake.SendMessage(HandshakeType.server_hello_done, TlsUtilities.EmptyBytes);

            handshake.HandshakeHash.SealHashAlgorithms();

            clientMessage = handshake.ReceiveMessage();

            if (clientMessage.Type == HandshakeType.supplemental_data)
            {
                ProcessClientSupplementalData(state, clientMessage.Body);
                clientMessage = handshake.ReceiveMessage();
            }
            else
            {
                state.server.ProcessClientSupplementalData(null);
            }

            if (state.certificateRequest == null)
            {
                state.keyExchange.SkipClientCredentials();
            }
            else
            {
                if (clientMessage.Type == HandshakeType.certificate)
                {
                    ProcessClientCertificate(state, clientMessage.Body);
                    clientMessage = handshake.ReceiveMessage();
                }
                else
                {
                    if (TlsUtilities.IsTlsV12(state.serverContext))
                    {
                        /*
                         * 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.
                         */
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    NotifyClientCertificate(state, Certificate.EmptyChain);
                }
            }

            if (clientMessage.Type == HandshakeType.client_key_exchange)
            {
                ProcessClientKeyExchange(state, clientMessage.Body);
            }
            else
            {
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }

            TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish();

            securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.serverContext, prepareFinishHash, null);

            TlsProtocol.EstablishMasterSecret(state.serverContext, state.keyExchange);
            recordLayer.InitPendingEpoch(state.server.GetCipher());

            /*
             * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing
             * capability (i.e., all certificates except those containing fixed Diffie-Hellman
             * parameters).
             */
            if (ExpectCertificateVerifyMessage(state))
            {
                byte[] certificateVerifyBody = handshake.ReceiveMessageBody(HandshakeType.certificate_verify);
                ProcessCertificateVerify(state, certificateVerifyBody, prepareFinishHash);
            }

            // NOTE: Calculated exclusive of the actual Finished message from the client
            byte[] expectedClientVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.client_finished,
                                                                               TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null));
            ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedClientVerifyData);

            if (state.expectSessionTicket)
            {
                NewSessionTicket newSessionTicket     = state.server.GetNewSessionTicket();
                byte[]           newSessionTicketBody = GenerateNewSessionTicket(state, newSessionTicket);
                handshake.SendMessage(HandshakeType.session_ticket, newSessionTicketBody);
            }

            // NOTE: Calculated exclusive of the Finished message itself
            byte[] serverVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.server_finished,
                                                                       TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null));
            handshake.SendMessage(HandshakeType.finished, serverVerifyData);

            handshake.Finish();

            state.server.NotifyHandshakeComplete();

            return(new DtlsTransport(recordLayer));
        }
        protected override void HandleHandshakeMessage(byte type, MemoryStream buf)
        {
            switch (type)
            {
            case HandshakeType.client_hello:
            {
                switch (this.mConnectionState)
                {
                case CS_START:
                {
                    ReceiveClientHelloMessage(buf);
                    this.mConnectionState = CS_CLIENT_HELLO;

                    SendServerHelloMessage();
                    this.mConnectionState = CS_SERVER_HELLO;

                    mRecordStream.NotifyHelloComplete();

                    IList serverSupplementalData = mTlsServer.GetServerSupplementalData();
                    if (serverSupplementalData != null)
                    {
                        SendSupplementalDataMessage(serverSupplementalData);
                    }
                    this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA;

                    this.mKeyExchange = mTlsServer.GetKeyExchange();
                    this.mKeyExchange.Init(Context);

                    this.mServerCredentials = mTlsServer.GetCredentials();

                    Certificate serverCertificate = null;

                    if (this.mServerCredentials == null)
                    {
                        this.mKeyExchange.SkipServerCredentials();
                    }
                    else
                    {
                        this.mKeyExchange.ProcessServerCredentials(this.mServerCredentials);

                        serverCertificate = this.mServerCredentials.Certificate;
                        SendCertificateMessage(serverCertificate);
                    }
                    this.mConnectionState = CS_SERVER_CERTIFICATE;

                    // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
                    if (serverCertificate == null || serverCertificate.IsEmpty)
                    {
                        this.mAllowCertificateStatus = false;
                    }

                    if (this.mAllowCertificateStatus)
                    {
                        CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus();
                        if (certificateStatus != null)
                        {
                            SendCertificateStatusMessage(certificateStatus);
                        }
                    }

                    this.mConnectionState = CS_CERTIFICATE_STATUS;

                    byte[] serverKeyExchange = this.mKeyExchange.GenerateServerKeyExchange();
                    if (serverKeyExchange != null)
                    {
                        SendServerKeyExchangeMessage(serverKeyExchange);
                    }
                    this.mConnectionState = CS_SERVER_KEY_EXCHANGE;

                    if (this.mServerCredentials != null)
                    {
                        this.mCertificateRequest = mTlsServer.GetCertificateRequest();
                        if (this.mCertificateRequest != null)
                        {
                            if (TlsUtilities.IsTlsV12(Context) != (mCertificateRequest.SupportedSignatureAlgorithms != null))
                            {
                                throw new TlsFatalAlert(AlertDescription.internal_error);
                            }

                            this.mKeyExchange.ValidateCertificateRequest(mCertificateRequest);

                            SendCertificateRequestMessage(mCertificateRequest);

                            TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
                                                             this.mCertificateRequest.SupportedSignatureAlgorithms);
                        }
                    }
                    this.mConnectionState = CS_CERTIFICATE_REQUEST;

                    SendServerHelloDoneMessage();
                    this.mConnectionState = CS_SERVER_HELLO_DONE;

                    this.mRecordStream.HandshakeHash.SealHashAlgorithms();

                    break;
                }

                case CS_END:
                {
                    RefuseRenegotiation();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.supplemental_data:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO_DONE:
                {
                    mTlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf));
                    this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.certificate:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO_DONE:
                case CS_CLIENT_SUPPLEMENTAL_DATA:
                {
                    if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
                    {
                        mTlsServer.ProcessClientSupplementalData(null);
                    }

                    if (this.mCertificateRequest == null)
                    {
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ReceiveCertificateMessage(buf);
                    this.mConnectionState = CS_CLIENT_CERTIFICATE;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.client_key_exchange:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO_DONE:
                case CS_CLIENT_SUPPLEMENTAL_DATA:
                case CS_CLIENT_CERTIFICATE:
                {
                    if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
                    {
                        mTlsServer.ProcessClientSupplementalData(null);
                    }

                    if (mConnectionState < CS_CLIENT_CERTIFICATE)
                    {
                        if (this.mCertificateRequest == null)
                        {
                            this.mKeyExchange.SkipClientCredentials();
                        }
                        else
                        {
                            if (TlsUtilities.IsTlsV12(Context))
                            {
                                /*
                                 * 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.
                                 */
                                throw new TlsFatalAlert(AlertDescription.unexpected_message);
                            }
                            else if (TlsUtilities.IsSsl(Context))
                            {
                                if (this.mPeerCertificate == null)
                                {
                                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                                }
                            }
                            else
                            {
                                NotifyClientCertificate(Certificate.EmptyChain);
                            }
                        }
                    }

                    ReceiveClientKeyExchangeMessage(buf);
                    this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.certificate_verify:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_KEY_EXCHANGE:
                {
                    /*
                     * RFC 5246 7.4.8 This message is only sent following a client certificate that has
                     * signing capability (i.e., all certificates except those containing fixed
                     * Diffie-Hellman parameters).
                     */
                    if (!ExpectCertificateVerifyMessage())
                    {
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ReceiveCertificateVerifyMessage(buf);
                    this.mConnectionState = CS_CERTIFICATE_VERIFY;

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.finished:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_KEY_EXCHANGE:
                case CS_CERTIFICATE_VERIFY:
                {
                    if (mConnectionState < CS_CERTIFICATE_VERIFY && ExpectCertificateVerifyMessage())
                    {
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ProcessFinishedMessage(buf);
                    this.mConnectionState = CS_CLIENT_FINISHED;

                    if (this.mExpectSessionTicket)
                    {
                        SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket());
                    }
                    this.mConnectionState = CS_SERVER_SESSION_TICKET;

                    SendChangeCipherSpecMessage();
                    SendFinishedMessage();
                    this.mConnectionState = CS_SERVER_FINISHED;

                    CompleteHandshake();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.hello_request:
            case HandshakeType.hello_verify_request:
            case HandshakeType.server_hello:
            case HandshakeType.server_key_exchange:
            case HandshakeType.certificate_request:
            case HandshakeType.server_hello_done:
            case HandshakeType.session_ticket:
            default:
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }
        }
    protected override void HandleHandshakeMessage(byte type, byte[] data)
    {
        MemoryStream memoryStream = new MemoryStream(data);

        switch (type)
        {
        case 1:
            switch (base.mConnectionState)
            {
            case 0:
            {
                ReceiveClientHelloMessage(memoryStream);
                base.mConnectionState = 1;
                SendServerHelloMessage();
                base.mConnectionState = 2;
                mRecordStream.NotifyHelloComplete();
                IList serverSupplementalData = mTlsServer.GetServerSupplementalData();
                if (serverSupplementalData != null)
                {
                    SendSupplementalDataMessage(serverSupplementalData);
                }
                base.mConnectionState = 3;
                mKeyExchange          = mTlsServer.GetKeyExchange();
                mKeyExchange.Init(Context);
                mServerCredentials = mTlsServer.GetCredentials();
                Certificate certificate = null;
                if (mServerCredentials == null)
                {
                    mKeyExchange.SkipServerCredentials();
                }
                else
                {
                    mKeyExchange.ProcessServerCredentials(mServerCredentials);
                    certificate = mServerCredentials.Certificate;
                    SendCertificateMessage(certificate);
                }
                base.mConnectionState = 4;
                if (certificate == null || certificate.IsEmpty)
                {
                    mAllowCertificateStatus = false;
                }
                if (mAllowCertificateStatus)
                {
                    CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus();
                    if (certificateStatus != null)
                    {
                        SendCertificateStatusMessage(certificateStatus);
                    }
                }
                base.mConnectionState = 5;
                byte[] array = mKeyExchange.GenerateServerKeyExchange();
                if (array != null)
                {
                    SendServerKeyExchangeMessage(array);
                }
                base.mConnectionState = 6;
                if (mServerCredentials != null)
                {
                    mCertificateRequest = mTlsServer.GetCertificateRequest();
                    if (mCertificateRequest != null)
                    {
                        if (TlsUtilities.IsTlsV12(Context) != (mCertificateRequest.SupportedSignatureAlgorithms != null))
                        {
                            throw new TlsFatalAlert(80);
                        }
                        mKeyExchange.ValidateCertificateRequest(mCertificateRequest);
                        SendCertificateRequestMessage(mCertificateRequest);
                        TlsUtilities.TrackHashAlgorithms(mRecordStream.HandshakeHash, mCertificateRequest.SupportedSignatureAlgorithms);
                    }
                }
                base.mConnectionState = 7;
                SendServerHelloDoneMessage();
                base.mConnectionState = 8;
                mRecordStream.HandshakeHash.SealHashAlgorithms();
                break;
            }

            case 16:
                RefuseRenegotiation();
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        case 23:
        {
            short mConnectionState = base.mConnectionState;
            if (mConnectionState == 8)
            {
                mTlsServer.ProcessClientSupplementalData(TlsProtocol.ReadSupplementalDataMessage(memoryStream));
                base.mConnectionState = 9;
                break;
            }
            throw new TlsFatalAlert(10);
        }

        case 11:
            switch (base.mConnectionState)
            {
            case 8:
            case 9:
                if (base.mConnectionState < 9)
                {
                    mTlsServer.ProcessClientSupplementalData(null);
                }
                if (mCertificateRequest == null)
                {
                    throw new TlsFatalAlert(10);
                }
                ReceiveCertificateMessage(memoryStream);
                base.mConnectionState = 10;
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        case 16:
            switch (base.mConnectionState)
            {
            case 8:
            case 9:
            case 10:
                if (base.mConnectionState < 9)
                {
                    mTlsServer.ProcessClientSupplementalData(null);
                }
                if (base.mConnectionState < 10)
                {
                    if (mCertificateRequest == null)
                    {
                        mKeyExchange.SkipClientCredentials();
                    }
                    else
                    {
                        if (TlsUtilities.IsTlsV12(Context))
                        {
                            throw new TlsFatalAlert(10);
                        }
                        if (TlsUtilities.IsSsl(Context))
                        {
                            if (mPeerCertificate == null)
                            {
                                throw new TlsFatalAlert(10);
                            }
                        }
                        else
                        {
                            NotifyClientCertificate(Certificate.EmptyChain);
                        }
                    }
                }
                ReceiveClientKeyExchangeMessage(memoryStream);
                base.mConnectionState = 11;
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        case 15:
        {
            short mConnectionState = base.mConnectionState;
            if (mConnectionState == 11)
            {
                if (!ExpectCertificateVerifyMessage())
                {
                    throw new TlsFatalAlert(10);
                }
                ReceiveCertificateVerifyMessage(memoryStream);
                base.mConnectionState = 12;
                break;
            }
            throw new TlsFatalAlert(10);
        }

        case 20:
            switch (base.mConnectionState)
            {
            case 11:
            case 12:
                if (base.mConnectionState < 12 && ExpectCertificateVerifyMessage())
                {
                    throw new TlsFatalAlert(10);
                }
                ProcessFinishedMessage(memoryStream);
                base.mConnectionState = 13;
                if (mExpectSessionTicket)
                {
                    SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket());
                    SendChangeCipherSpecMessage();
                }
                base.mConnectionState = 14;
                SendFinishedMessage();
                base.mConnectionState = 15;
                base.mConnectionState = 16;
                CompleteHandshake();
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        default:
            throw new TlsFatalAlert(10);
        }
    }
        protected override void HandleHandshakeMessage(byte type, MemoryStream buf)
        {
            if (HTTPManager.Logger.Level <= Loglevels.All)
            {
                HTTPManager.Logger.Verbose("TlsClientProtocol", "HandleHandshakeMessage " + type.ToString(), this.LoggingContext);
            }

            if (this.mResumedSession)
            {
                if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO)
                {
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                ProcessFinishedMessage(buf);
                this.mConnectionState = CS_SERVER_FINISHED;

                SendChangeCipherSpecMessage();
                SendFinishedMessage();
                this.mConnectionState = CS_CLIENT_FINISHED;

                CompleteHandshake();
                return;
            }

            switch (type)
            {
            case HandshakeType.certificate:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                case CS_SERVER_SUPPLEMENTAL_DATA:
                {
                    if (this.mConnectionState == CS_SERVER_HELLO)
                    {
                        HandleSupplementalData(null);
                    }

                    // Parse the Certificate message and Send to cipher suite

                    this.mPeerCertificate = Certificate.Parse(buf);

                    AssertEmpty(buf);

                    // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
                    if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty)
                    {
                        this.mAllowCertificateStatus = false;
                    }

                    this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate);

                    this.mAuthentication = mTlsClient.GetAuthentication();

                    // We are expecting a Certificate_Status message, so going to delay the NotifyServerCertificate call
                    // until the server_hello_done. (no until CS_SERVER_CERTIFICATE as the server might not send one)
                    // This way, we can validate both the certificate status and the received certificates.
                    if (!this.mTlsClient.ExpectEmptyCertificateStatusExtension)
                    {
                        this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
                    }

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_SERVER_CERTIFICATE;
                break;
            }

            case HandshakeType.certificate_status:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_CERTIFICATE:
                {
                    if (!this.mAllowCertificateStatus)
                    {
                        /*
                         * 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.mTlsClient.CertificateStatus = CertificateStatus.Parse(buf);

                    AssertEmpty(buf);

                    // TODO[RFC 3546] Figure out how to provide this to the client/authentication.

                    this.mConnectionState = CS_CERTIFICATE_STATUS;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.finished:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_FINISHED:
                case CS_SERVER_SESSION_TICKET:
                {
                    if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket)
                    {
                        /*
                         * 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);
                    }

                    ProcessFinishedMessage(buf);
                    this.mConnectionState = CS_SERVER_FINISHED;

                    CompleteHandshake();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.server_hello:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_HELLO:
                {
                    ReceiveServerHelloMessage(buf);
                    this.mConnectionState = CS_SERVER_HELLO;

                    this.mRecordStream.NotifyHelloComplete();

                    ApplyMaxFragmentLengthExtension();

                    if (this.mResumedSession)
                    {
                        this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret);
                        this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
                    }
                    else
                    {
                        InvalidateSession();

                        if (this.mSelectedSessionID.Length > 0)
                        {
                            this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null);
                        }
                    }

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.supplemental_data:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                {
                    HandleSupplementalData(ReadSupplementalDataMessage(buf));
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.server_hello_done:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                case CS_SERVER_SUPPLEMENTAL_DATA:
                case CS_SERVER_CERTIFICATE:
                case CS_CERTIFICATE_STATUS:
                case CS_SERVER_KEY_EXCHANGE:
                case CS_CERTIFICATE_REQUEST:
                {
                    if (this.mTlsClient.ExpectEmptyCertificateStatusExtension)
                    {
                        this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
                    }

                    if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
                    {
                        HandleSupplementalData(null);
                    }

                    if (mConnectionState < CS_SERVER_CERTIFICATE)
                    {
                        // There was no server certificate message; check it's OK
                        this.mKeyExchange.SkipServerCredentials();
                        this.mAuthentication = null;
                    }

                    if (mConnectionState < CS_SERVER_KEY_EXCHANGE)
                    {
                        // There was no server key exchange message; check it's OK
                        this.mKeyExchange.SkipServerKeyExchange();
                    }

                    AssertEmpty(buf);

                    this.mConnectionState = CS_SERVER_HELLO_DONE;

                    this.mRecordStream.HandshakeHash.SealHashAlgorithms();

                    IList clientSupplementalData = mTlsClient.GetClientSupplementalData();
                    if (clientSupplementalData != null)
                    {
                        SendSupplementalDataMessage(clientSupplementalData);
                    }
                    this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;

                    TlsCredentials clientCreds = null;
                    if (mCertificateRequest == null)
                    {
                        this.mKeyExchange.SkipClientCredentials();
                    }
                    else
                    {
                        clientCreds = this.mAuthentication.GetClientCredentials(Context, mCertificateRequest);

                        if (clientCreds == null)
                        {
                            this.mKeyExchange.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.EmptyChain);
                        }
                        else
                        {
                            this.mKeyExchange.ProcessClientCredentials(clientCreds);

                            SendCertificateMessage(clientCreds.Certificate);
                        }
                    }

                    this.mConnectionState = CS_CLIENT_CERTIFICATE;

                    /*
                     * Send the client key exchange message, depending on the key exchange we are using
                     * in our CipherSuite.
                     */
                    SendClientKeyExchangeMessage();
                    this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;

                    if (TlsUtilities.IsSsl(Context))
                    {
                        EstablishMasterSecret(Context, mKeyExchange);
                    }

                    TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish();
                    this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, prepareFinishHash, null);

                    if (!TlsUtilities.IsSsl(Context))
                    {
                        EstablishMasterSecret(Context, mKeyExchange);
                    }

                    mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());

                    if (clientCreds != null && clientCreds is TlsSignerCredentials)
                    {
                        TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;

                        /*
                         * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
                         */
                        SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
                            Context, signerCredentials);

                        byte[] hash;
                        if (signatureAndHashAlgorithm == null)
                        {
                            hash = mSecurityParameters.SessionHash;
                        }
                        else
                        {
                            hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
                        }

                        byte[]          signature         = signerCredentials.GenerateCertificateSignature(hash);
                        DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
                        SendCertificateVerifyMessage(certificateVerify);

                        this.mConnectionState = CS_CERTIFICATE_VERIFY;
                    }

                    SendChangeCipherSpecMessage();
                    SendFinishedMessage();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_CLIENT_FINISHED;
                break;
            }

            case HandshakeType.server_key_exchange:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                case CS_SERVER_SUPPLEMENTAL_DATA:
                case CS_SERVER_CERTIFICATE:
                case CS_CERTIFICATE_STATUS:
                {
                    if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
                    {
                        HandleSupplementalData(null);
                    }

                    if (mConnectionState < CS_SERVER_CERTIFICATE)
                    {
                        // There was no server certificate message; check it's OK
                        this.mKeyExchange.SkipServerCredentials();
                        this.mAuthentication = null;
                    }

                    this.mKeyExchange.ProcessServerKeyExchange(buf);

                    AssertEmpty(buf);
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_SERVER_KEY_EXCHANGE;
                break;
            }

            case HandshakeType.certificate_request:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_CERTIFICATE:
                case CS_CERTIFICATE_STATUS:
                case CS_SERVER_KEY_EXCHANGE:
                {
                    if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE)
                    {
                        // There was no server key exchange message; check it's OK
                        this.mKeyExchange.SkipServerKeyExchange();
                    }

                    if (this.mAuthentication == 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.mCertificateRequest = CertificateRequest.Parse(Context, buf);

                    AssertEmpty(buf);

                    this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest);

                    /*
                     * TODO Give the client a chance to immediately select the CertificateVerify hash
                     * algorithm here to avoid tracking the other hash algorithms unnecessarily?
                     */
                    TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
                                                     this.mCertificateRequest.SupportedSignatureAlgorithms);

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_CERTIFICATE_REQUEST;
                break;
            }

            case HandshakeType.session_ticket:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_FINISHED:
                {
                    if (!this.mExpectSessionTicket)
                    {
                        /*
                         * 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.mConnectionState = 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.mConnectionState == 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);
            }
        }