/// <summary>
        /// Validates the given LogoutResponse object except for xml signature.
        /// XML signature checking is expected to be done prior to calling
        /// this method based on the appropriate profile.
        /// </summary>
        /// <param name="logoutResponse">LogoutResponse object.</param>
        /// <param name="logoutRequests">
        /// Collection of previously sent logoutRequests used to compare with
        /// the InResponseTo attribute of the LogoutResponse (if present).
        /// </param>
        private void Validate(LogoutResponse logoutResponse, ICollection logoutRequests)
        {
            if (logoutResponse == null)
            {
                throw new Saml2Exception(Resources.ServiceProviderUtilityLogoutResponseIsNull);
            }

            ServiceProviderUtility.CheckInResponseTo(logoutResponse, logoutRequests);
            this.CheckIssuer(logoutResponse.Issuer);
            this.CheckCircleOfTrust(logoutResponse.Issuer);
            ServiceProviderUtility.CheckStatusCode(logoutResponse.StatusCode);
        }
        /// <summary>
        /// Retrieve the LogoutResponse object found within the HttpRequest
        /// in the context of the HttpContext, performing validation of
        /// the LogoutResponse prior to returning to the user.
        /// </summary>
        /// <param name="context">
        /// HttpContext containing session, request, and response objects.
        /// </param>
        /// <returns>LogoutResponse object</returns>
        public LogoutResponse GetLogoutResponse(HttpContext context)
        {
            LogoutResponse logoutResponse = null;
            HttpRequest request = context.Request;

            // Check if a saml response was received...
            if (String.IsNullOrEmpty(request[Saml2Constants.ResponseParameter]))
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlResponseReceived);
            }

            // Obtain the LogoutRequest object...
            if (request.HttpMethod == "GET")
            {
                string samlResponse = Saml2Utils.ConvertFromBase64Decompress(request[Saml2Constants.ResponseParameter]);
                logoutResponse = new LogoutResponse(samlResponse);
            }
            else
            {
                string samlResponse = Saml2Utils.ConvertFromBase64(request[Saml2Constants.ResponseParameter]);
                logoutResponse = new LogoutResponse(samlResponse);
            }

            XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom;
            StringBuilder logMessage = new StringBuilder();
            logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml);
            FedletLogger.Info(logMessage.ToString());

            string prevLogoutRequestId = logoutResponse.InResponseTo;
            try
            {
                if (request.HttpMethod == "GET")
                {
                    string queryString
                        = request.RawUrl.Substring(request.RawUrl.IndexOf("?", StringComparison.Ordinal) + 1);
                    FedletLogger.Info("LogoutResponse query string:\r\n" + queryString);
                    this.ValidateForRedirect(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context), queryString);
                }
                else
                {
                    this.ValidateForPost(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context));
                }
            }
            catch (Saml2Exception se)
            {
                // log and throw again...
                logMessage = new StringBuilder();
                logMessage.Append(se.Message).Append("\r\n").Append(xmlDoc.InnerXml);
                FedletLogger.Warning(logMessage.ToString());
                throw;
            }
            finally
            {
                LogoutRequestCache.RemoveSentLogoutRequest(context, prevLogoutRequestId);
            }

            return logoutResponse;
        }
        /// <summary>
        /// Checks the signature of the given logoutResponse assuming
        /// the signature is within the XML.
        /// </summary>
        /// <param name="logoutResponse">SAMLv2 LogoutRequest object.</param>
        private void CheckSignature(LogoutResponse logoutResponse)
        {
            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutResponse.Issuer];

            if (idp == null)
            {
                throw new Saml2Exception(Resources.InvalidIssuer);
            }

            Saml2Utils.ValidateSignedXml(
                idp.SigningCertificate,
                logoutResponse.XmlDom,
                logoutResponse.XmlSignature,
                logoutResponse.Id);
        }
        /// <summary>
        /// Checks the signature of the given LogoutResponse with
        /// the raw query string.
        /// </summary>
        /// <param name="logoutResponse">SAMLv2 LogoutResponse object.</param>
        /// <param name="queryString">
        /// Raw query string that contains the response and possible signature.
        /// </param>
        private void CheckSignature(LogoutResponse logoutResponse, string queryString)
        {
            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutResponse.Issuer];

            Saml2Utils.ValidateSignedQueryString(idp.SigningCertificate, queryString);
        }
        /// <summary>
        /// Validates the given LogoutResponse object obtained from a
        /// Redirect. If this service provider desires the logout respone to 
        /// be signed, XML signature checking will be performed.
        /// </summary>
        /// <param name="logoutResponse">LogoutResponse object.</param>
        /// <param name="logoutRequests">
        /// Collection of previously sent logoutRequests used to compare with
        /// the InResponseTo attribute of the LogoutResponse (if present).
        /// </param>
        /// <param name="queryString">
        /// Raw query string that contains the request and possible signature
        /// </param>
        public void ValidateForRedirect(LogoutResponse logoutResponse, ICollection logoutRequests, string queryString)
        {
            if (logoutResponse == null)
            {
                throw new Saml2Exception(Resources.ServiceProviderUtilityLogoutResponseIsNull);
            }

            if (this.ServiceProvider.WantLogoutResponseSigned)
            {
                this.CheckSignature(logoutResponse, queryString);
            }

            this.Validate(logoutResponse, logoutRequests);
        }
        /// <summary>
        /// Checks the InResponseTo field of the given LogoutResponse to
        /// see if it is one of the managed logout requests.
        /// </summary>
        /// <param name="logoutResponse">SAMLv2 LogoutResponse.</param>
        /// <param name="logoutRequests">
        /// Collection of previously sent LogoutRequests.
        /// </param>
        private static void CheckInResponseTo(LogoutResponse logoutResponse, ICollection logoutRequests)
        {
            if (logoutRequests != null && logoutResponse.InResponseTo != null)
            {
                IEnumerator i = logoutRequests.GetEnumerator();
                while (i.MoveNext())
                {
                    LogoutRequest logoutRequest = (LogoutRequest)i.Current;
                    if (logoutRequest.Id == logoutResponse.InResponseTo)
                    {
                        // Found one, return quietly.
                        return;
                    }
                }
            }

            // Didn't find one, complain loudly.
            throw new Saml2Exception(Resources.LogoutResponseInvalidInResponseTo);
        }
        /// <summary>
        /// Sends a SOAP LogoutRequest to the specified IDP.
        /// </summary>
        /// <param name="logoutRequest">
        /// LogoutRequest object.
        /// </param>
        /// <param name="idpEntityId">Entity ID of the IDP.</param>
        public void SendSoapLogoutRequest(LogoutRequest logoutRequest, string idpEntityId)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            LogoutResponse logoutResponse = null;
            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId];

            if (logoutRequest == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull);
            }
            else if (idp == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound);
            }
            else if (idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding) == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined);
            }

            try
            {
                Uri soapLogoutSvcUri = new Uri(idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding));
                request = (HttpWebRequest)WebRequest.Create(soapLogoutSvcUri);
                XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom;

                if (idp.WantLogoutRequestSigned)
                {
                    if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias))
                    {
                        throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias);
                    }
                    else
                    {
                        Saml2Utils.SignXml(
                            this.ServiceProvider.SigningCertificateAlias,
                            logoutRequestXml,
                            logoutRequest.Id,
                            true);
                    }
                }

                string soapMessage = Saml2Utils.CreateSoapMessage(logoutRequestXml.InnerXml);

                byte[] byteArray = Encoding.UTF8.GetBytes(soapMessage);
                request.ContentType = "text/xml";
                request.ContentLength = byteArray.Length;
                request.AllowAutoRedirect = false;
                request.Method = "POST";

                Stream requestStream = request.GetRequestStream();
                requestStream.Write(byteArray, 0, byteArray.Length);
                requestStream.Close();

                response = (HttpWebResponse)request.GetResponse();
                StreamReader streamReader = new StreamReader(response.GetResponseStream());
                string responseContent = streamReader.ReadToEnd();
                streamReader.Close();

                XmlDocument soapResponse = new XmlDocument();
                soapResponse.PreserveWhitespace = true;
                soapResponse.LoadXml(responseContent);

                XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapResponse.NameTable);
                soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
                soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol");
                soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion");
                soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");

                XmlElement root = soapResponse.DocumentElement;
                XmlNode responseXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutResponse", soapNsMgr);
                string logoutResponseXml = responseXml.OuterXml;

                logoutResponse = new LogoutResponse(logoutResponseXml);
                StringBuilder logMessage = new StringBuilder();
                logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml);
                FedletLogger.Info(logMessage.ToString());

                ArrayList logoutRequests = new ArrayList();
                logoutRequests.Add(logoutRequest);
                this.Validate(logoutResponse, logoutRequests);
            }
            catch (WebException we)
            {
                throw new ServiceProviderUtilityException(Resources.LogoutRequestWebException, we);
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
        }
        /// <summary>
        /// Writes a SOAP LogoutResponse to the Response object found within
        /// the given HttpContext based on the given logout request.
        /// </summary>
        /// <param name="context">
        /// HttpContext containing session, request, and response objects.
        /// </param>
        /// <param name="logoutRequest">
        /// LogoutRequest object.
        /// </param>
        public void SendSoapLogoutResponse(HttpContextBase context, LogoutRequest logoutRequest)
        {
            IIdentityProvider idp;
            if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp))
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound);
            }

            var parameters = new NameValueCollection();
            parameters[Saml2Constants.Binding] = Saml2Constants.HttpSoapProtocolBinding;

            var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils);
            var logoutResponseXml = (XmlDocument) logoutResponse.XmlDom;

            if (idp.WantLogoutResponseSigned)
            {
                if (string.IsNullOrEmpty(ServiceProvider.SigningCertificateAlias))
                {
                    throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias);
                }

                _saml2Utils.SignXml(
                    ServiceProvider.SigningCertificateAlias,
                    logoutResponseXml,
                    logoutResponse.Id,
                    true);
            }

            _logger.Info("LogoutResponse:\r\n{0}", logoutResponseXml.OuterXml);

            string soapMessage = _saml2Utils.CreateSoapMessage(logoutResponseXml.OuterXml);

            context.Response.ContentType = "text/xml";
            context.Response.Write(soapMessage);
        }
        /// <summary>
        /// Sends a SOAP LogoutRequest to the specified IDP.
        /// </summary>
        /// <param name="logoutRequest">
        /// LogoutRequest object.
        /// </param>
        /// <param name="idpEntityId">Entity ID of the IDP.</param>
        public void SendSoapLogoutRequest(LogoutRequest logoutRequest, string idpEntityId)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            LogoutResponse logoutResponse = null;
            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId];

            if (logoutRequest == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull);
            }
            else if (idp == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound);
            }
            else if (idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding) == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined);
            }

            try
            {
                Uri soapLogoutSvcUri = new Uri(idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding));
                if (soapLogoutSvcUri.Scheme == "https")
                {
                    System.Net.ServicePointManager.ServerCertificateValidationCallback +=
                    delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                    System.Security.Cryptography.X509Certificates.X509Chain chain,
                    System.Net.Security.SslPolicyErrors sslPolicyErrors)
                    {
                        if (ServiceProvider.TrustAllCerts
                            || sslPolicyErrors.HasFlag(System.Net.Security.SslPolicyErrors.None))
                            {
                                return true;
                            }
                        
                        StringBuilder logErrorMessage = new StringBuilder();
                        logErrorMessage.Append("SSLPolicyError: ").Append(sslPolicyErrors);
                        FedletLogger.Error(logErrorMessage.ToString());
                        return false;
                    };
                }

                request = (HttpWebRequest)WebRequest.Create(soapLogoutSvcUri);

                string authCertAlias = ConfigurationManager.AppSettings[Saml2Constants.MutualAuthCertAlias];
                if (soapLogoutSvcUri.Scheme == "https" && !string.IsNullOrWhiteSpace(authCertAlias))
                {
                    X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(authCertAlias);
                    if (cert != null)
                    {
                        request.ClientCertificates.Add(cert);
                    }
                }

                XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom;

                if (idp.WantLogoutRequestSigned)
                {
                    if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias))
                    {
                        throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias);
                    }
                    else
                    {
                        Saml2Utils.SignXml(
                            this.ServiceProvider.SigningCertificateAlias,
                            logoutRequestXml,
                            logoutRequest.Id,
                            true,
                            this.ServiceProvider);
                    }
                }

                string soapMessage = Saml2Utils.CreateSoapMessage(logoutRequestXml.InnerXml);

                byte[] byteArray = Encoding.UTF8.GetBytes(soapMessage);
                request.ContentType = "text/xml";
                request.ContentLength = byteArray.Length;
                request.AllowAutoRedirect = false;
                request.Method = "POST";

                Stream requestStream = request.GetRequestStream();
                requestStream.Write(byteArray, 0, byteArray.Length);
                requestStream.Close();

                response = (HttpWebResponse)request.GetResponse();
                StreamReader streamReader = new StreamReader(response.GetResponseStream());
                string responseContent = streamReader.ReadToEnd();
                streamReader.Close();

                XmlDocument soapResponse = new XmlDocument();
                soapResponse.PreserveWhitespace = true;
                soapResponse.LoadXml(responseContent);

                XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapResponse.NameTable);
                soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
                soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol");
                soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion");
                soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");

                XmlElement root = soapResponse.DocumentElement;
                XmlNode responseXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutResponse", soapNsMgr);
                string logoutResponseXml = responseXml.OuterXml;

                logoutResponse = new LogoutResponse(logoutResponseXml);
                StringBuilder logMessage = new StringBuilder();
                logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml);
                FedletLogger.Info(logMessage.ToString());

                ArrayList logoutRequests = new ArrayList();
                logoutRequests.Add(logoutRequest);
                this.Validate(logoutResponse, logoutRequests);
            }
            catch (WebException we)
            {
                throw new ServiceProviderUtilityException(Resources.LogoutRequestWebException, we);
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
        }
        /// <summary>
        /// Send the SAML LogoutResponse message based on the received
        /// LogoutRequest.  POST (default) or Redirect is supported.
        /// </summary>
        /// <param name="context">
        /// HttpContext containing session, request, and response objects.
        /// </param>
        /// <param name="logoutRequest">
        /// LogoutRequest corresponding to the ensuing LogoutResponse to send.
        /// </param>
        public void SendLogoutResponse(HttpContext context, LogoutRequest logoutRequest)
        {
            if (logoutRequest == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull);
            }

            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer];
            if (idp == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromLogoutRequest);
            }

            // send logout response based on how it was received
            if (context.Request.HttpMethod == "GET")
            {
                NameValueCollection parameters = new NameValueCollection();
                parameters[Saml2Constants.Binding] = Saml2Constants.HttpRedirectProtocolBinding;
                LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters);

                StringBuilder logMessage = new StringBuilder();
                XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom;
                logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml);
                FedletLogger.Info(logMessage.ToString());

                parameters = Saml2Utils.GetRequestParameters(context.Request);
                string redirectUrl = this.GetLogoutResponseRedirectLocation(logoutResponse, idp.EntityId, parameters);
                context.Response.Redirect(redirectUrl.ToString(), true);
            }
            else
            {
                NameValueCollection parameters = new NameValueCollection();
                parameters[Saml2Constants.Binding] = Saml2Constants.HttpPostProtocolBinding;
                LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters);

                StringBuilder logMessage = new StringBuilder();
                XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom;
                logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml);
                FedletLogger.Info(logMessage.ToString());

                parameters = Saml2Utils.GetRequestParameters(context.Request);
                string postHtml = this.GetLogoutResponsePostHtml(logoutResponse, idp.EntityId, parameters);
                context.Response.Write(postHtml);
                context.Response.End();
            }
        }
        /// <summary>
        /// Retrieve the LogoutResponse object found within the HttpRequest
        /// in the context of the HttpContext, performing validation of
        /// the LogoutResponse prior to returning to the user.
        /// </summary>
        /// <param name="context">
        /// HttpContext containing session, request, and response objects.
        /// </param>
        /// <returns>LogoutResponse object</returns>
        public LogoutResponse GetLogoutResponse(HttpContextBase context)
        {
            LogoutResponse logoutResponse;
            HttpRequestBase request = context.Request;

            // Check if a saml response was received...
            if (String.IsNullOrEmpty(request[Saml2Constants.ResponseParameter]))
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlResponseReceived);
            }

            // Obtain the LogoutRequest object...
            if (request.HttpMethod == "GET")
            {
                string samlResponse = _saml2Utils.ConvertFromBase64Decompress(request[Saml2Constants.ResponseParameter]);
                logoutResponse = new LogoutResponse(samlResponse);
            }
            else
            {
                string samlResponse = _saml2Utils.ConvertFromBase64(request[Saml2Constants.ResponseParameter]);
                logoutResponse = new LogoutResponse(samlResponse);
            }

            var xmlDoc = (XmlDocument) logoutResponse.XmlDom;
            _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml);

            string prevLogoutRequestId = logoutResponse.InResponseTo;
            try
            {
                if (request.HttpMethod == "GET")
                {
                    string queryString
                        = request.RawUrl.Substring(request.RawUrl.IndexOf("?", StringComparison.Ordinal) + 1);
                    _logger.Info("LogoutResponse query string:\r\n{0}", queryString);
                    ValidateForRedirect(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context), queryString);
                }
                else
                {
                    ValidateForPost(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context));
                }
            }
            catch (Saml2Exception se)
            {
                // add some context
                se.Data["xml"] = xmlDoc.InnerXml;
                throw;
            }
            finally
            {
                LogoutRequestCache.RemoveSentLogoutRequest(context, prevLogoutRequestId);
            }

            return logoutResponse;
        }
        /// <summary>
        /// Checks the signature of the given logoutResponse assuming
        /// the signature is within the XML.
        /// </summary>
        /// <param name="logoutResponse">SAMLv2 LogoutRequest object.</param>
        private void CheckSignature(LogoutResponse logoutResponse)
        {
            IIdentityProvider idp;

            if (!IdentityProviders.TryGetValue(logoutResponse.Issuer, out idp))
            {
                throw new Saml2Exception(Resources.InvalidIssuer);
            }

            _saml2Utils.ValidateSignedXml(
                idp.SigningCertificate,
                logoutResponse.XmlDom,
                logoutResponse.XmlSignature,
                logoutResponse.Id);
        }
        /// <summary>
        /// Validates the given LogoutResponse object obtained from a POST. If
        /// this service provider desires the logout respone to be signed, XML
        /// signature checking will be performed.
        /// </summary>
        /// <param name="logoutResponse">LogoutResponse object.</param>
        /// <param name="logoutRequests">
        /// Collection of previously sent logoutRequests used to compare with
        /// the InResponseTo attribute of the LogoutResponse (if present).
        /// </param>
        public void ValidateForPost(LogoutResponse logoutResponse, ICollection logoutRequests)
        {
            if (logoutResponse == null)
            {
                throw new Saml2Exception(Resources.ServiceProviderUtilityLogoutResponseIsNull);
            }

            if (ServiceProvider.WantLogoutResponseSigned)
            {
                CheckSignature(logoutResponse);
            }

            Validate(logoutResponse, logoutRequests);
        }
        /// <summary>
        /// Gets the HTML for use of submitting the LogoutResponse with POST.
        /// </summary>
        /// <param name="logoutResponse">
        /// LogoutResponse to packaged for a POST.
        /// </param>
        /// <param name="idpEntityId">Entity ID of the IDP.</param>
        /// <param name="parameters">
        /// NameVallueCollection of additional parameters.
        /// </param>
        /// <returns>
        /// HTML with auto-form submission with POST of the LogoutRequest
        /// </returns>
        public string GetLogoutResponsePostHtml(LogoutResponse logoutResponse, string idpEntityId, NameValueCollection parameters)
        {
            if (logoutResponse == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutResponseIsNull);
            }

            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId];
            if (idp == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound);
            }

            string sloPostResponseLocation = idp.GetSingleLogoutServiceResponseLocation(Saml2Constants.HttpPostProtocolBinding);
            if (sloPostResponseLocation == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcResLocNotDefined);
            }

            string relayState = null;
            if (parameters != null && !string.IsNullOrEmpty(parameters[Saml2Constants.RelayState]))
            {
                relayState = parameters[Saml2Constants.RelayState];
                Saml2Utils.ValidateRelayState(relayState, this.ServiceProvider.RelayStateUrlList);
            }

            XmlDocument logoutResponseXml = (XmlDocument)logoutResponse.XmlDom;

            if (idp.WantLogoutResponseSigned)
            {
                if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias))
                {
                    throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias);
                }
                else
                {
                    Saml2Utils.SignXml(
                        this.ServiceProvider.SigningCertificateAlias,
                        logoutResponseXml,
                        logoutResponse.Id,
                        true);
                }
            }

            string packagedLogoutResponse = Saml2Utils.ConvertToBase64(logoutResponseXml.InnerXml);
            string inputFieldFormat = "<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />";

            StringBuilder html = new StringBuilder();
            html.Append("<html><head><title>OpenSSO - IDP initiated SLO</title></head>");
            html.Append("<body onload=\"document.forms[0].submit();\">");
            html.Append("<form method=\"post\" action=\"");
            html.Append(sloPostResponseLocation);
            html.Append("\">");
            html.Append("<input type=\"hidden\" name=\"");
            html.Append(Saml2Constants.ResponseParameter);
            html.Append("\" value=\"");
            html.Append(packagedLogoutResponse);
            html.Append("\" />");

            if (!string.IsNullOrEmpty(relayState))
            {
                html.Append(string.Format(
                                          CultureInfo.InvariantCulture,
                                          inputFieldFormat,
                                          Saml2Constants.RelayState,
                                          HttpUtility.HtmlEncode(relayState)));
            }

            html.Append("</form>");
            html.Append("</body>");
            html.Append("</html>");

            return html.ToString();
        }
        /// <summary>
        /// Writes a SOAP LogoutResponse to the Response object found within
        /// the given HttpContext based on the given logout request.
        /// </summary>
        /// <param name="context">
        /// HttpContext containing session, request, and response objects.
        /// </param>
        /// <param name="logoutRequest">
        /// LogoutRequest object.
        /// </param>
        public void SendSoapLogoutResponse(HttpContext context, LogoutRequest logoutRequest)
        {
            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer];

            if (idp == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound);
            }

            NameValueCollection parameters = new NameValueCollection();
            parameters[Saml2Constants.Binding] = Saml2Constants.HttpSoapProtocolBinding;

            LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters);
            XmlDocument logoutResponseXml = (XmlDocument)logoutResponse.XmlDom;

            if (idp.WantLogoutResponseSigned)
            {
                if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias))
                {
                    throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias);
                }
                else
                {
                    Saml2Utils.SignXml(
                        this.ServiceProvider.SigningCertificateAlias,
                        logoutResponseXml,
                        logoutResponse.Id,
                        true);
                }
            }

            StringBuilder logMessage = new StringBuilder();
            logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml.OuterXml);
            FedletLogger.Info(logMessage.ToString());

            string soapMessage = Saml2Utils.CreateSoapMessage(logoutResponseXml.OuterXml);

            context.Response.ContentType = "text/xml";
            context.Response.Write(soapMessage);
        }
        /// <summary>
        /// Gets the LogoutResponse location along with querystring parameters 
        /// to be used for actual browser requests.
        /// </summary>
        /// <param name="logoutResponse">
        /// LogoutResponse to packaged for a redirect.
        /// </param>
        /// <param name="idpEntityId">Entity ID of the IDP.</param>
        /// <param name="parameters">
        /// NameVallueCollection of additional parameters.
        /// </param>
        /// <returns>
        /// URL with query string parameter for the specified IDP.
        /// </returns>
        public string GetLogoutResponseRedirectLocation(LogoutResponse logoutResponse, string idpEntityId, NameValueCollection parameters)
        {
            if (logoutResponse == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutResponseIsNull);
            }

            IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId];
            if (idp == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound);
            }

            string sloRedirectResponseLocation = idp.GetSingleLogoutServiceResponseLocation(Saml2Constants.HttpRedirectProtocolBinding);
            if (sloRedirectResponseLocation == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined);
            }

            string packagedLogoutResponse = Saml2Utils.CompressConvertToBase64UrlEncode(logoutResponse.XmlDom);
            string queryString = Saml2Constants.ResponseParameter + "=" + packagedLogoutResponse;

            if (parameters != null && !string.IsNullOrEmpty(parameters[Saml2Constants.RelayState]))
            {
                string relayState = parameters[Saml2Constants.RelayState];
                Saml2Utils.ValidateRelayState(relayState, this.ServiceProvider.RelayStateUrlList);
                queryString += "&" + Saml2Constants.RelayState;
                queryString += "=" + HttpUtility.UrlEncode(relayState);
            }

            if (idp.WantLogoutResponseSigned)
            {
                if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias))
                {
                    throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias);
                }
                else
                {
                    queryString += "&" + Saml2Constants.SignatureAlgorithm;
                    queryString += "=" + HttpUtility.UrlEncode(Saml2Constants.SignatureAlgorithmRsa);
                    queryString = Saml2Utils.SignQueryString(this.ServiceProvider.SigningCertificateAlias, queryString);
                }
            }

            StringBuilder redirectUrl = new StringBuilder();
            redirectUrl.Append(sloRedirectResponseLocation);
            redirectUrl.Append(Saml2Utils.GetQueryStringDelimiter(sloRedirectResponseLocation));
            redirectUrl.Append(queryString);

            FedletLogger.Info("LogoutResponse via Redirect:\r\n" + redirectUrl);

            return redirectUrl.ToString();
        }
        /// <summary>
        /// Send the SAML LogoutResponse message based on the received
        /// LogoutRequest.  POST (default) or Redirect is supported.
        /// </summary>
        /// <param name="context">
        /// HttpContext containing session, request, and response objects.
        /// </param>
        /// <param name="logoutRequest">
        /// LogoutRequest corresponding to the ensuing LogoutResponse to send.
        /// </param>
        public void SendLogoutResponse(HttpContextBase context, LogoutRequest logoutRequest)
        {
            if (logoutRequest == null)
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull);
            }

            IIdentityProvider idp;
            if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp))
            {
                throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromLogoutRequest);
            }

            // send logout response based on how it was received
            if (context.Request.HttpMethod == "GET")
            {
                var parameters = new NameValueCollection();
                parameters[Saml2Constants.Binding] = Saml2Constants.HttpRedirectProtocolBinding;
                var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils);

                var xmlDoc = (XmlDocument) logoutResponse.XmlDom;
                _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml);

                parameters = _saml2Utils.GetRequestParameters(context.Request);
                string redirectUrl = GetLogoutResponseRedirectLocation(logoutResponse, idp.EntityId, parameters);
                context.Response.Redirect(redirectUrl, true);
            }
            else
            {
                var parameters = new NameValueCollection();
                parameters[Saml2Constants.Binding] = Saml2Constants.HttpPostProtocolBinding;
                var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils);

                var xmlDoc = (XmlDocument) logoutResponse.XmlDom;
                _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml);

                parameters = _saml2Utils.GetRequestParameters(context.Request);
                string postHtml = GetLogoutResponsePostHtml(logoutResponse, idp.EntityId, parameters);
                context.Response.Write(postHtml);
                context.Response.End();
            }
        }