/// <summary> /// Initializes a new instance of the ArtifactResolve class. /// </summary> /// <param name="serviceProvider">Service Provider to issue this request</param> /// <param name="artifact">SAMLv2 Artifact</param> public ArtifactResolve(IServiceProvider serviceProvider, Artifact artifact, Saml2Utils saml2Utils) { xml = new XmlDocument(); xml.PreserveWhitespace = true; nsMgr = new XmlNamespaceManager(xml.NameTable); nsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); nsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); Id = saml2Utils.GenerateId(); IssueInstant = saml2Utils.GenerateIssueInstant(); Issuer = serviceProvider.EntityId; Artifact = artifact; var rawXml = new StringBuilder(); rawXml.Append("<samlp:ArtifactResolve"); rawXml.Append(" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\""); rawXml.Append(" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\""); rawXml.Append(" ID=\"" + Id + "\""); rawXml.Append(" Version=\"2.0\""); rawXml.Append(" IssueInstant=\"" + IssueInstant + "\""); rawXml.Append(">"); rawXml.Append(" <saml:Issuer>" + Issuer + "</saml:Issuer>"); rawXml.Append(" <samlp:Artifact>" + Artifact + "</samlp:Artifact>"); rawXml.Append("</samlp:ArtifactResolve>"); xml.LoadXml(rawXml.ToString()); }
/// <summary> /// Retrieve the AuthnResponse object found within the HttpRequest /// in the context of the HttpContext, performing validation of /// the AuthnResponse prior to returning to the user. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <returns>AuthnResponse object</returns> public AuthnResponse GetAuthnResponse(HttpContext context) { ArtifactResponse artifactResponse = null; AuthnResponse authnResponse = null; ICollection authnRequests = AuthnRequestCache.GetSentAuthnRequests(context); HttpRequest request = context.Request; // Check if a saml response was received... if (string.IsNullOrEmpty(request[Saml2Constants.ResponseParameter]) && string.IsNullOrEmpty(request[Saml2Constants.ArtifactParameter])) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlResponseReceived); } // Obtain AuthnResponse object from either HTTP-POST or HTTP-Artifact if (request[Saml2Constants.ResponseParameter] != null) { string samlResponse = Saml2Utils.ConvertFromBase64(request[Saml2Constants.ResponseParameter]); authnResponse = new AuthnResponse(samlResponse); XmlDocument xmlDoc = (XmlDocument)authnResponse.XmlDom; StringBuilder logMessage = new StringBuilder(); logMessage.Append("AuthnResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); } else if (request[Saml2Constants.ArtifactParameter] != null) { Artifact artifact = new Artifact(request[Saml2Constants.ArtifactParameter]); artifactResponse = this.GetArtifactResponse(artifact); authnResponse = artifactResponse.AuthnResponse; XmlDocument xmlDoc = (XmlDocument)artifactResponse.XmlDom; StringBuilder logMessage = new StringBuilder(); logMessage.Append("ArtifactResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); } string prevAuthnRequestId = authnResponse.InResponseTo; try { if (artifactResponse != null) { this.ValidateForArtifact(artifactResponse, authnRequests); } else { this.ValidateForPost(authnResponse, authnRequests); } } catch (Saml2Exception se) { // log and throw again... XmlDocument authnResponseXml = (XmlDocument)authnResponse.XmlDom; StringBuilder logMessage = new StringBuilder(); logMessage.Append(se.Message).Append("\r\n").Append(authnResponseXml.InnerXml); FedletLogger.Warning(logMessage.ToString()); throw; } finally { AuthnRequestCache.RemoveSentAuthnRequest(context, prevAuthnRequestId); } return authnResponse; }
/// <summary> /// Gets the Identity Provider associated with the specified artifact. /// The currently maintained list of IDPs each have their entity ID /// hashed and compared with the given artifact's source ID to make /// the correct determination. /// </summary> /// <param name="artifact">SAML artifact.</param> /// <returns> /// Identity Provider who's entity ID matches the source ID /// within the artifact, null if not found. /// </returns> private IdentityProvider GetIdpFromArtifact(Artifact artifact) { SHA1 sha1 = new SHA1CryptoServiceProvider(); IdentityProvider idp = null; string idpEntityIdHashed = null; foreach (string idpEntityId in this.IdentityProviders.Keys) { idpEntityIdHashed = BitConverter.ToString(sha1.ComputeHash(Encoding.UTF8.GetBytes(idpEntityId))); idpEntityIdHashed = idpEntityIdHashed.Replace("-", string.Empty); if (idpEntityIdHashed == artifact.SourceId) { idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; break; } } return idp; }
/// <summary> /// Retrieve the ArtifactResponse object with the given SAMLv2 /// artifact. /// </summary> /// <param name="artifact">SAMLv2 artifact</param> /// <returns>ArtifactResponse object</returns> public ArtifactResponse GetArtifactResponse(Artifact artifact) { ArtifactResolve artifactResolve = new ArtifactResolve(this.ServiceProvider, artifact); ArtifactResponse artifactResponse = null; IdentityProvider idp = this.GetIdpFromArtifact(artifact); if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromArtifact); } string artifactResolutionSvcLoc = idp.GetArtifactResolutionServiceLocation(Saml2Constants.HttpSoapProtocolBinding); if (artifactResolutionSvcLoc == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpArtifactResSvcLocNotDefined); } HttpWebRequest request = null; HttpWebResponse response = null; try { Uri artifactResolutionSvcUri = new Uri(artifactResolutionSvcLoc); request = (HttpWebRequest)WebRequest.Create(artifactResolutionSvcUri); XmlDocument artifactResolveXml = (XmlDocument)artifactResolve.XmlDom; if (idp.WantArtifactResolveSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, artifactResolveXml, artifactResolve.Id, true); } } string soapMessage = Saml2Utils.CreateSoapMessage(artifactResolveXml.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(); StringBuilder logMessage = new StringBuilder(); logMessage.Append("ArtifactResolve:\r\n").Append(artifactResolveXml.OuterXml); FedletLogger.Info(logMessage.ToString()); 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:ArtifactResponse", soapNsMgr); string artifactResponseXml = responseXml.OuterXml; artifactResponse = new ArtifactResponse(artifactResponseXml); if (artifactResolve.Id != artifactResponse.InResponseTo) { throw new Saml2Exception(Resources.ArtifactResolutionInvalidInResponseTo); } } catch (WebException we) { throw new ServiceProviderUtilityException(Resources.ArtifactResolutionWebException, we); } finally { if (response != null) { response.Close(); } } return artifactResponse; }
/// <summary> /// Retrieve the ArtifactResponse object with the given SAMLv2 /// artifact. /// </summary> /// <param name="artifact">SAMLv2 artifact</param> /// <returns>ArtifactResponse object</returns> public ArtifactResponse GetArtifactResponse(Artifact artifact) { ArtifactResolve artifactResolve = new ArtifactResolve(this.ServiceProvider, artifact); ArtifactResponse artifactResponse = null; IdentityProvider idp = this.GetIdpFromArtifact(artifact); if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromArtifact); } string artifactResolutionSvcLoc = idp.GetArtifactResolutionServiceLocation(Saml2Constants.HttpSoapProtocolBinding); if (artifactResolutionSvcLoc == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpArtifactResSvcLocNotDefined); } HttpWebRequest request = null; HttpWebResponse response = null; try { Uri artifactResolutionSvcUri = new Uri(artifactResolutionSvcLoc); if (artifactResolutionSvcUri.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(artifactResolutionSvcUri); string authCertAlias = ConfigurationManager.AppSettings[Saml2Constants.MutualAuthCertAlias]; if (artifactResolutionSvcUri.Scheme == "https" && !string.IsNullOrWhiteSpace(authCertAlias)) { X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(authCertAlias); if (cert != null) { request.ClientCertificates.Add(cert); } } XmlDocument artifactResolveXml = (XmlDocument)artifactResolve.XmlDom; if (idp.WantArtifactResolveSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, artifactResolveXml, artifactResolve.Id, true, this.ServiceProvider); } } string soapMessage = Saml2Utils.CreateSoapMessage(artifactResolveXml.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(); StringBuilder logMessage = new StringBuilder(); logMessage.Append("ArtifactResolve:\r\n").Append(artifactResolveXml.OuterXml); FedletLogger.Info(logMessage.ToString()); 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:ArtifactResponse", soapNsMgr); string artifactResponseXml = responseXml.OuterXml; artifactResponse = new ArtifactResponse(artifactResponseXml); if (artifactResolve.Id != artifactResponse.InResponseTo) { throw new Saml2Exception(Resources.ArtifactResolutionInvalidInResponseTo); } } catch (WebException we) { throw new ServiceProviderUtilityException(Resources.ArtifactResolutionWebException, we); } finally { if (response != null) { response.Close(); } } return artifactResponse; }
/// <summary> /// Retrieve the AuthnResponse object found within the HttpRequest /// in the context of the HttpContext, performing validation of /// the AuthnResponse prior to returning to the user. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <returns>AuthnResponse object</returns> public AuthnResponse GetAuthnResponse(HttpContextBase context) { ArtifactResponse artifactResponse = null; AuthnResponse authnResponse; ICollection authnRequests = AuthnRequestCache.GetSentAuthnRequests(context); HttpRequestBase request = context.Request; // Obtain AuthnResponse object from either HTTP-POST or HTTP-Artifact if (!string.IsNullOrWhiteSpace(request[Saml2Constants.ResponseParameter])) { string samlResponse = _saml2Utils.ConvertFromBase64(request[Saml2Constants.ResponseParameter]); authnResponse = new AuthnResponse(samlResponse); var xmlDoc = (XmlDocument) authnResponse.XmlDom; _logger.Info("AuthnResponse:\r\n{0}", xmlDoc.OuterXml); } else if (!string.IsNullOrWhiteSpace(request[Saml2Constants.ArtifactParameter])) { var artifact = new Artifact(request[Saml2Constants.ArtifactParameter]); artifactResponse = GetArtifactResponse(artifact); authnResponse = artifactResponse.AuthnResponse; var xmlDoc = (XmlDocument) artifactResponse.XmlDom; _logger.Info("ArtifactResponse:\r\n{0}", xmlDoc.OuterXml); } else { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlResponseReceived); } string prevAuthnRequestId = authnResponse.InResponseTo; try { if (artifactResponse != null) { ValidateForArtifact(artifactResponse, authnRequests); } else { ValidateForPost(authnResponse, authnRequests); } } catch (Saml2Exception se) { // add some context var authnResponseXml = (XmlDocument) authnResponse.XmlDom; se.Data["xml"] = authnResponseXml.InnerXml; throw; } finally { AuthnRequestCache.RemoveSentAuthnRequest(context, prevAuthnRequestId); } return authnResponse; }