protected bool VerifySignature(CertificatePublicKey publicKey, byte[] data, byte[] signedParams)
        {
            // Initialize the signature position and validity
            int position = 0;
            bool signatureOk = false;

            // Get the corresponding signer for public key
            SignatureAlgorithm sigAlg = _pluginManager.GetSignatureAlgorithmByOid(publicKey.Oid);
            if (sigAlg == null)
            {
                throw new AlertException(AlertDescription.IllegalParameter, "Signer for given public key not found");
            }

            // Select hash algorithm, null means SSLv3/TLSv1 hash
            HashAlgorithm hashAlgorithm = null;
            if (_version.HasSelectableSighash)
            {
                byte hashAlgorithmType = signedParams[position++];
                byte signAlgorithmType = signedParams[position++];
                if (sigAlg.SignatureAlgorithmType != signAlgorithmType)
                {
                    throw new AlertException(AlertDescription.DecryptError, "Certificate signed with invalid signature algorithm");
                }
                if (!sigAlg.SupportsHashAlgorithmType(hashAlgorithmType))
                {
                    throw new AlertException(AlertDescription.DecryptError, "Certificate signed with invalid hash algorithm");
                }
                hashAlgorithm = GetSignatureHashAlgorithm(sigAlg, hashAlgorithmType);
            }

            // Check that signature length is valid (same as stored)
            int len = (signedParams[position] << 8) | signedParams[position + 1];
            if (len != signedParams.Length - position - 2)
            {
                throw new AlertException(AlertDescription.DecodeError, "Signature length not valid");
            }
            position += 2;

            // Extract the signature from the end of the signed parameters
            byte[] signature = new byte[len];
            Buffer.BlockCopy(signedParams, position, signature, 0, len);

            // Verify correctness of the signature
            signatureOk = sigAlg.VerifyData(_version, data, hashAlgorithm, publicKey, signature);
            if (!signatureOk)
            {
                throw new AlertException(AlertDescription.DecodeError, "Signature from server incorrect");
            }

            return signatureOk;
        }
        protected override void ProcessServerHelloDone(HandshakeMessage serverHelloDone)
        {
            if (_state != HandshakeState.ReceivedCertificate &&
                _state != HandshakeState.ReceivedServerKeyExchange &&
                _state != HandshakeState.ReceivedCertificateRequest)
            {
                throw new AlertException(AlertDescription.UnexpectedMessage,
                                         "Server hello done received at the wrong time");
            }

            bool clientCertificateSent = false;
            if (_clientCertificateRequested)
            {
                // Ask for correct certificate from the callback
                int certificateIndex = _certificateSelectionCallback(_availableCertificates.ToArray(), _serverCertificates);
                if (certificateIndex >= 0 && certificateIndex < _availableCertificates.Count)
                {
                    _clientCertificates.AddRange(_availableCertificates[certificateIndex]);
                    _selectedPrivateKey = _availablePrivateKeys[certificateIndex];
                }

                // If certificate was selected, send it to server
                if (_clientCertificates.Count > 0)
                {
                    HandshakeCertificate certificate = new HandshakeCertificate(_version);
                    certificate.CertificateList.AddRange(_clientCertificates);
                    OutputMessage(certificate);

                    clientCertificateSent = true;
                }
                else
                {
                    // TODO: In case of SSLv3 we should send warning alert instead?
                    HandshakeCertificate certificate = new HandshakeCertificate(_version);
                    OutputMessage(certificate);
                }
            }

            // Send client key exchange message
            byte[] clientKeys = null;
            if (_serverCertificates.Count > 0)
            {
                var cert = _serverCertificates[0];
                CertificatePublicKey publicKey = new CertificatePublicKey(cert);
                this.logger?.Trace($"OID (lookup with http://www.oid-info.com/get/[oid]) of key from server certificate: {publicKey.Oid}");
                var data = Asn1Object.FromByteArray(cert.GetKeyAlgorithmParameters());
                this.logger?.Trace($" OID of key parameters from server certificate: {data}");
                clientKeys = _cipherSuite.KeyExchangeAlgorithm.GetClientKeys(_version, _maxVersion, publicKey);
            }
            HandshakeMessage keyex = new HandshakeMessage(HandshakeMessageType.ClientKeyExchange, _version, clientKeys);
            OutputMessage(keyex);

            if (clientCertificateSent)
            {
                // Get all handshake messages
                byte[] handshakeMessages = _handshakeStream.ToArray();

                // FIXME: Generate the signature of handshakeMessages with client cert signature
                //        this breaks if client certificate signature is different type than negotiated
                byte[] signature = GenerateSignature(_selectedPrivateKey, handshakeMessages);

                // Create CertificateVerify message and send it to server
                HandshakeMessage verify = new HandshakeMessage(HandshakeMessageType.CertificateVerify, _version, signature);
                OutputMessage(verify);
            }

            // Generate the master secret from key exchange
            _connectionState.MasterSecret = GenerateMasterSecret(_version, _cipherSuite, _connectionState);

            // Wait for changecipherspec+finished
            _state = HandshakeState.SendChangeCipherSpec;
        }
        protected override void ProcessCertificateVerify(HandshakeMessage verify)
        {
            if (_state != HandshakeState.ReceivedClientKeyExchange)
            {
                throw new AlertException(AlertDescription.UnexpectedMessage,
                                         "Certificate verify received at the wrong time");
            }
            if (!_clientCertificateReceived)
            {
                throw new AlertException(AlertDescription.UnexpectedMessage,
                                         "Certificate verify received even though client certificate not received");
            }

            // Get all handshake messages up to, but not including, this one
            byte[] allMessages = _handshakeStream.ToArray();
            byte[] handshakeMessages = new byte[allMessages.Length - verify.Encode().Length];
            Buffer.BlockCopy(allMessages, 0, handshakeMessages, 0, handshakeMessages.Length);

            // Verify the signature of handshake messages
            CertificatePublicKey publicKey = new CertificatePublicKey(_clientCertificates[0]);
            bool signatureOk = VerifySignature(publicKey, handshakeMessages, verify.Data);
            if (!signatureOk)
            {
                throw new AlertException(AlertDescription.DecodeError, "Signature from client incorrect");
            }

            // Wait for changecipherspec+finished
            _state = HandshakeState.ReceivedCertificateVerify;
        }
        protected override void ProcessServerKeyExchange(HandshakeMessage keyExchange)
        {
            if (_state != HandshakeState.ReceivedServerHello &&
                _state != HandshakeState.ReceivedCertificate)
            {
                throw new AlertException(AlertDescription.UnexpectedMessage,
                                         "Server key exchange received at the wrong time");
            }
            if (!_cipherSuite.IsAnonymous && _serverCertificates.Count == 0)
            {
                throw new AlertException(AlertDescription.HandshakeFailure,
                                         "No certificate received even though cipher suite is not anonymous");
            }

            // Process server keys, also returns us the signature data
            var cert = _serverCertificates[0];
            byte[] signature = _cipherSuite.KeyExchangeAlgorithm.ProcessServerKeys(_version, keyExchange.Data, cert);

            // Extract the signed data from the complete payload
            byte[] serverKeys = new byte[keyExchange.Data.Length - signature.Length];
            Buffer.BlockCopy(keyExchange.Data, 0, serverKeys, 0, serverKeys.Length);

            // Verify correctness of the signature
            if (!_cipherSuite.IsAnonymous)
            {
                CertificatePublicKey publicKey = new CertificatePublicKey(cert);

                // Generate the original data signed from server keys
                byte[] signedData = new byte[_connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length + serverKeys.Length];
                Buffer.BlockCopy(_connectionState.ClientRandom, 0, signedData, 0, _connectionState.ClientRandom.Length);
                Buffer.BlockCopy(_connectionState.ServerRandom, 0, signedData, _connectionState.ClientRandom.Length, _connectionState.ServerRandom.Length);
                Buffer.BlockCopy(serverKeys, 0, signedData, _connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length, serverKeys.Length);

                bool signatureOk = VerifySignature(publicKey, signedData, signature);
                if (!signatureOk)
                {
                    throw new AlertException(AlertDescription.DecodeError,
                                             "Signature from server incorrect");
                }
            }

            // Wait for certificate request or server hello done
            _state = HandshakeState.ReceivedServerKeyExchange;
        }