/// <summary> /// Gets the trusted signers. /// </summary> /// <param name="keys">The keys.</param> /// <param name="identityProvider">The identity provider.</param> /// <returns>List of trusted certificate signers.</returns> public static IEnumerable<AsymmetricAlgorithm> GetTrustedSigners(ICollection<KeyDescriptor> keys, IdentityProviderElement identityProvider) { if (keys == null) { throw new ArgumentNullException("keys"); } var result = new List<AsymmetricAlgorithm>(keys.Count); foreach (var keyDescriptor in keys) { foreach (KeyInfoClause clause in (KeyInfo)keyDescriptor.KeyInfo) { // Check certificate specifications if (clause is KeyInfoX509Data) { var cert = XmlSignatureUtils.GetCertificateFromKeyInfo((KeyInfoX509Data)clause); if (!CertificateSatisfiesSpecifications(identityProvider, cert)) { continue; } } var key = XmlSignatureUtils.ExtractKey(clause); result.Add(key); } } return result; }
/// <summary> /// Transfers the client. /// </summary> /// <param name="idp">The identity provider.</param> /// <param name="context">The context.</param> private void TransferClient(IdentityProviderElement idp, HttpContext context) { var request = Saml20LogoutRequest.GetDefault(); // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.LogoutEndpoint, idp.Metadata.IDPSLOEndpoints); request.Destination = destination.Url; var nameIdFormat = StateService.Get<string>(IdpNameIdFormat); request.SubjectToLogOut.Format = nameIdFormat; // Handle POST binding if (destination.Binding == BindingType.Post) { var builder = new HttpPostBindingBuilder(destination); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = StateService.Get<string>(IdpNameId); request.SessionIndex = StateService.Get<string>(IdpSessionIdKey); var requestDocument = request.GetXml(); XmlSignatureUtils.SignDocument(requestDocument, request.Id); builder.Request = requestDocument.OuterXml; Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "POST", builder.Request); builder.GetPage().ProcessRequest(context); context.Response.End(); return; } // Handle Redirect binding if (destination.Binding == BindingType.Redirect) { request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = StateService.Get<string>(IdpNameId); request.SessionIndex = StateService.Get<string>(IdpSessionIdKey); var builder = new HttpRedirectBindingBuilder { Request = request.GetXml().OuterXml, SigningKey = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().PrivateKey }; var redirectUrl = destination.Url + "?" + builder.ToQuery(); Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "REDIRECT", redirectUrl); context.Response.Redirect(redirectUrl, true); return; } // Handle Artifact binding if (destination.Binding == BindingType.Artifact) { request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = StateService.Get<string>(IdpNameId); request.SessionIndex = StateService.Get<string>(IdpSessionIdKey); Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "ARTIFACT", request.GetXml().OuterXml); var builder = new HttpArtifactBindingBuilder(context); builder.RedirectFromLogout(destination, request, Guid.NewGuid().ToString("N")); } Logger.Error(ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid); }
/// <summary> /// Transfers the client. /// </summary> /// <param name="identityProvider">The identity provider.</param> /// <param name="request">The request.</param> /// <param name="context">The context.</param> private void TransferClient(IdentityProviderElement identityProvider, Saml20AuthnRequest request, HttpContext context) { // Set the last IDP we attempted to login at. StateService.Set(IdpTempSessionKey, identityProvider.Id); // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.Endpoints.SignOnEndpoint, identityProvider.Metadata.SSOEndpoints); request.Destination = destination.Url; if (identityProvider.ForceAuth) { request.ForceAuthn = true; } // Check isPassive status var isPassiveFlag = StateService.Get<bool?>(IdpIsPassive); if (isPassiveFlag != null && (bool)isPassiveFlag) { request.IsPassive = true; StateService.Set(IdpIsPassive, null); } if (identityProvider.IsPassive) { request.IsPassive = true; } // Check if request should forceAuthn var forceAuthnFlag = StateService.Get<bool?>(IdpForceAuthn); if (forceAuthnFlag != null && (bool)forceAuthnFlag) { request.ForceAuthn = true; StateService.Set(IdpForceAuthn, null); } // Check if protocol binding should be forced if (identityProvider.Endpoints.SignOnEndpoint != null) { if (!string.IsNullOrEmpty(identityProvider.Endpoints.SignOnEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = identityProvider.Endpoints.SignOnEndpoint.ForceProtocolBinding; } } // Save request message id to session StateService.Set(ExpectedInResponseToSessionKey, request.Id); switch (destination.Binding) { case BindingType.Redirect: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpRedirect); var redirectBuilder = new HttpRedirectBindingBuilder { SigningKey = _certificate.PrivateKey, Request = request.GetXml().OuterXml }; Logger.DebugFormat(TraceMessages.AuthnRequestSent, redirectBuilder.Request); var redirectLocation = request.Destination + "?" + redirectBuilder.ToQuery(); context.Response.Redirect(redirectLocation, true); break; case BindingType.Post: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpPost); var postBuilder = new HttpPostBindingBuilder(destination); // Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost; } var requestXml = request.GetXml(); XmlSignatureUtils.SignDocument(requestXml, request.Id); postBuilder.Request = requestXml.OuterXml; Logger.DebugFormat(TraceMessages.AuthnRequestSent, postBuilder.Request); postBuilder.GetPage().ProcessRequest(context); break; case BindingType.Artifact: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact); var artifactBuilder = new HttpArtifactBindingBuilder(context); // Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpArtifact; } Logger.DebugFormat(TraceMessages.AuthnRequestSent, request.GetXml().OuterXml); artifactBuilder.RedirectFromLogin(destination, request); break; default: Logger.Error(ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid); } }
/// <summary> /// Determines whether the certificate is satisfied by all specifications. /// </summary> /// <param name="idp">The identity provider.</param> /// <param name="cert">The cert.</param> /// <returns><c>true</c> if certificate is satisfied by all specifications; otherwise, <c>false</c>.</returns> private static bool CertificateSatisfiesSpecifications(IdentityProviderElement idp, X509Certificate2 cert) { return SpecificationFactory.GetCertificateSpecifications(idp).All(spec => spec.IsSatisfiedBy(cert)); }
/// <summary> /// Is called before the assertion is made into a strongly typed representation /// </summary> /// <param name="context">The HttpContext.</param> /// <param name="elem">The assertion element.</param> /// <param name="endpoint">The endpoint.</param> protected virtual void PreHandleAssertion(HttpContext context, XmlElement elem, IdentityProviderElement endpoint) { Logger.DebugFormat(TraceMessages.AssertionPrehandlerCalled); if (endpoint != null && endpoint.Endpoints.LogoutEndpoint != null && !string.IsNullOrEmpty(endpoint.Endpoints.LogoutEndpoint.TokenAccessor)) { var idpTokenAccessor = Activator.CreateInstance(Type.GetType(endpoint.Endpoints.LogoutEndpoint.TokenAccessor, false)) as ISaml20IdpTokenAccessor; if (idpTokenAccessor != null) { Logger.DebugFormat("{0}.{1} called", idpTokenAccessor.GetType(), "ReadToken"); idpTokenAccessor.ReadToken(elem); Logger.DebugFormat("{0}.{1} finished", idpTokenAccessor.GetType(), "ReadToken"); } } }
/// <summary> /// Performs the attribute query against the specified IdP endpoint and adds the resulting attributes to <c>Saml20Identity.Current</c>. /// </summary> /// <param name="context">The http context.</param> /// <param name="endPoint">The IdP to perform the query against.</param> /// <param name="nameIdFormat">The name id format.</param> public void PerformQuery(HttpContext context, IdentityProviderElement endPoint, string nameIdFormat) { Logger.DebugFormat("{0}.{1} called", GetType(), "PerformQuery()"); var builder = new HttpSoapBindingBuilder(context); var name = new NameId { Value = Saml20Identity.Current.Name, Format = nameIdFormat }; _attrQuery.Subject.Items = new object[] { name }; _attrQuery.SamlAttribute = _attributes.ToArray(); var query = new XmlDocument(); query.LoadXml(Serialization.SerializeToXmlString(_attrQuery)); XmlSignatureUtils.SignDocument(query, Id); if (query.FirstChild is XmlDeclaration) { query.RemoveChild(query.FirstChild); } Logger.DebugFormat(TraceMessages.AttrQuerySent, endPoint.Metadata.GetAttributeQueryEndpointLocation(), query.OuterXml); Stream s; try { s = builder.GetResponse(endPoint.Metadata.GetAttributeQueryEndpointLocation(), query.OuterXml, endPoint.AttributeQuery); } catch (Exception e) { Logger.Error(e.Message, e); throw; } var parser = new HttpSoapBindingParser(s); var status = parser.GetStatus(); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.AttrQueryStatusNotSuccessful, Serialization.SerializeToXmlString(status)); throw new Saml20Exception(status.StatusMessage); } bool isEncrypted; var xmlAssertion = Saml20SignonHandler.GetAssertion(parser.SamlMessage, out isEncrypted); if (isEncrypted) { var ass = new Saml20EncryptedAssertion((RSA)Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().PrivateKey); ass.LoadXml(xmlAssertion); ass.Decrypt(); xmlAssertion = ass.Assertion.DocumentElement; } var assertion = new Saml20Assertion(xmlAssertion, null, Saml2Config.GetConfig().AssertionProfile.AssertionValidator, endPoint.QuirksMode); Logger.DebugFormat(TraceMessages.AttrQueryAssertionReceived, xmlAssertion == null ? string.Empty : xmlAssertion.OuterXml); if (!assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endPoint.Metadata.Keys, endPoint))) { Logger.Error(ErrorMessages.AssertionSignatureInvalid); throw new Saml20Exception(ErrorMessages.AssertionSignatureInvalid); } foreach (var attr in assertion.Attributes) { Saml20Identity.Current.AddAttributeFromQuery(attr.Name, attr); } }
/// <summary> /// Performs the attribute query against the specified IdP endpoint and adds the resulting attributes to <c>Saml20Identity.Current</c>. /// </summary> /// <param name="context">The http context.</param> /// <param name="endPoint">The IdP to perform the query against.</param> public void PerformQuery(HttpContext context, IdentityProviderElement endPoint) { var nameIdFormat = StateService.Get<string>(Saml20AbstractEndpointHandler.IdpNameIdFormat); if (string.IsNullOrEmpty(nameIdFormat)) { nameIdFormat = Saml20Constants.NameIdentifierFormats.Persistent; } PerformQuery(context, endPoint, nameIdFormat); }