/// <summary> /// Signs the document. /// </summary> /// <param name="doc">The doc.</param> private static void SignDocument(XmlDocument doc) { var cert = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate(); if (!cert.HasPrivateKey) { throw new InvalidOperationException("Private key access to the signing certificate is required."); } var signedXml = new SignedXml(doc); signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; signedXml.SigningKey = cert.PrivateKey; // Retrieve the value of the "ID" attribute on the root assertion element. var reference = new Reference("#" + doc.DocumentElement.GetAttribute("ID")); reference.AddTransform(new XmlDsigEnvelopedSignatureTransform()); reference.AddTransform(new XmlDsigExcC14NTransform()); signedXml.AddReference(reference); // Include the public key of the certificate in the assertion. signedXml.KeyInfo = new KeyInfo(); signedXml.KeyInfo.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.WholeChain)); signedXml.ComputeSignature(); // Append the computed signature. The signature must be placed as the sibling of the Issuer element. doc.DocumentElement.InsertBefore(doc.ImportNode(signedXml.GetXml(), true), doc.DocumentElement.FirstChild); }
public static string CreateSamlResponse(SamlResponseFactoryArgs args) { if (!args.Certificate.HasPrivateKey) { throw new Exception("Certificate does not not contains a private key"); } var section = Saml2SectionFactory.Create(args.Audience); var config = Saml2ConfigFactory.Create(section); Saml2Config.Init(config); var assertionDocument = CreateAssertionDocument(args).DocumentElement; var cert = args.Certificate; var assertion20 = new Saml20Assertion(assertionDocument, null, false); assertion20.Sign(cert); assertion20.CheckValid(new[] { cert.PublicKey.Key }); var assertionString = assertion20.GetXml().OuterXml; var deserializedAssertionDoc = new XmlDocument { PreserveWhitespace = true }; deserializedAssertionDoc.Load(new StringReader(assertionString)); var deserializedAssertion = new Saml20Assertion(deserializedAssertionDoc.DocumentElement, null, false); deserializedAssertion.CheckValid(new[] { cert.PublicKey.Key }); var issueInstant = new IssueInstant(deserializedAssertion.Assertion.IssueInstant.Value); var result = CompoundResponse(issueInstant, args.Recipient, args.Issuer, assertionString, args.RequestId); return(result); }
/// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> public override void ProcessRequest(HttpContext context) { Logger.DebugFormat("{0}.{1} called", GetType(), "ProcessRequest()"); var config = Saml2Config.GetConfig(); if (config == null) { throw new Saml20Exception(ErrorMessages.ConfigMissingSaml2Element); } var endp = config.ServiceProvider.Endpoints.FirstOrDefault(ep => ep.Type == EndpointType.SignOn); if (endp == null) { throw new Saml20Exception(ErrorMessages.ConfigServiceProviderMissingSignOnEndpoint); } var redirectUrl = StateService.Get <string>("RedirectUrl"); if (!string.IsNullOrEmpty(redirectUrl)) { StateService.Remove("RedirectUrl"); context.Response.Redirect(redirectUrl); } else if (string.IsNullOrEmpty(endp.RedirectUrl)) { context.Response.Redirect("~/"); } else { context.Response.Redirect(endp.RedirectUrl); } }
/// <summary> /// Initializes static members of the <see cref="LoggerProvider"/> class. /// </summary> static LoggerProvider() { var loggerClass = Saml2Config.GetConfigElement().Logging.LoggingFactory; var loggerFactory = string.IsNullOrEmpty(loggerClass) ? new NoLoggingLoggerFactory() : GetLoggerFactory(loggerClass); SetLoggerFactory(loggerFactory); }
/// <summary> /// Initializes static members of the <see cref="StateServiceProvider" /> class. /// </summary> static StateServiceProvider() { var stateServiceClass = Saml2Config.GetConfig().State.StateServiceFactory; var stateServiceFactory = string.IsNullOrEmpty(stateServiceClass) ? new SessionStateServiceFactory() : GetStateServiceFactory(stateServiceClass); SetStateServiceFactory(stateServiceFactory); }
/// <summary> /// Handles the selection of an IDP. If only one IDP is found, the user is automatically redirected to it. /// If several are found, and nothing indicates to which one the user should be sent, this method returns null. /// </summary> /// <param name="context">The context.</param> /// <returns>The <see cref="IdentityProviderElement"/>.</returns> public IdentityProviderElement RetrieveIDP(HttpContext context) { var config = Saml2Config.GetConfig(); // If idpChoice is set, use it value if (!string.IsNullOrEmpty(context.Request.Params[IdpChoiceParameterName])) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromQueryString, context.Request.Params[IdpChoiceParameterName]); var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == context.Request.Params[IdpChoiceParameterName]); if (endPoint != null) { return(endPoint); } } // If we have a common domain cookie, use it's value // It must have been returned from the local common domain cookie reader endpoint. if (!string.IsNullOrEmpty(context.Request.QueryString["_saml_idp"])) { var cdc = new CommonDomainCookie(context.Request.QueryString["_saml_idp"]); if (cdc.IsSet) { var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == cdc.PreferredIDP); if (endPoint != null) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromCommonDomainCookie, cdc.PreferredIDP); return(endPoint); } Logger.WarnFormat(ErrorMessages.CommonDomainCookieIdentityProviderInvalid, cdc.PreferredIDP); } } // If there is only one configured IdentityProviderEndpointElement lets just use that if (config.IdentityProviders.Count == 1 && config.IdentityProviders[0].Metadata != null) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, config.IdentityProviders[0].Name); return(config.IdentityProviders[0]); } // If one of the endpoints are marked with default, use that one var defaultIDP = config.IdentityProviders.FirstOrDefault(idp => idp.Default); if (defaultIDP != null) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, defaultIDP.Id); return(defaultIDP); } // In case an IDP selection url has been configured, redirect to that one. if (!string.IsNullOrEmpty(config.IdentityProviders.SelectionUrl)) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromSelection, config.IdentityProviders.SelectionUrl); context.Response.Redirect(config.IdentityProviders.SelectionUrl); } // If an IDPSelectionEvent handler is present, request the handler for an IDP endpoint to use. return(IdpSelectionUtil.InvokeIDPSelectionEventHandler(config.IdentityProviders)); }
/// <summary> /// Gets the actions. /// </summary> /// <returns>The currently configured Action list.</returns> public static List <IAction> GetActions() { var config = Saml2Config.GetConfig(); return(config.Actions == null || config.Actions.Count == 0 ? GetDefaultActions() : config.Actions.Select(ac => (IAction)Activator.CreateInstance(Type.GetType(ac.Type))).ToList()); }
/// <summary> /// Gets a default instance of this class with meaningful default values set. /// </summary> /// <returns>The default <see cref="Saml20AttributeQuery"/>.</returns> public static Saml20AttributeQuery GetDefault() { var config = Saml2Config.GetConfig(); var result = new Saml20AttributeQuery { Issuer = config.ServiceProvider.Id }; return(result); }
/// <summary> /// Creates an artifact and redirects the user to the IdP /// </summary> /// <param name="destination">The destination of the request.</param> /// <param name="request">The authentication request.</param> public void RedirectFromLogin(IdentityProviderEndpointElement destination, Saml20AuthnRequest request) { var config = Saml2Config.GetConfig(); var index = (short)config.ServiceProvider.Endpoints.SignOnEndpoint.Index; var doc = request.GetXml(); XmlSignatureUtils.SignDocument(doc, request.Request.Id); ArtifactRedirect(destination, index, doc, Context.Request.Params["relayState"]); }
/// <summary> /// Gets a default instance of this class with proper values set. /// </summary> /// <returns>The default <see cref="Saml20ArtifactResolve"/>.</returns> public static Saml20ArtifactResolve GetDefault() { var config = Saml2Config.GetConfig(); var result = new Saml20ArtifactResolve { Issuer = config.ServiceProvider.Id }; return(result); }
/// <summary> /// Creates an artifact for the LogoutRequest and redirects the user to the IdP. /// </summary> /// <param name="destination">The destination of the request.</param> /// <param name="request">The logout request.</param> /// <param name="relayState">The query string relay state value to add to the communication</param> public void RedirectFromLogout(IdentityProviderEndpointElement destination, Saml20LogoutRequest request, string relayState) { var config = Saml2Config.GetConfig(); var index = (short)config.ServiceProvider.Endpoints.LogoutEndpoint.Index; var doc = request.GetXml(); XmlSignatureUtils.SignDocument(doc, request.Request.Id); ArtifactRedirect(destination, index, doc, relayState); }
/// <summary> /// Returns an instance of the class with meaningful default values set. /// </summary> /// <returns>The <see cref="Saml20LogoutRequest"/>.</returns> public static Saml20LogoutRequest GetDefault() { var config = Saml2Config.GetConfig(); var result = new Saml20LogoutRequest { SubjectToLogOut = new NameId(), Issuer = config.ServiceProvider.Id }; return(result); }
/// <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.SigningCertificat
/// <summary> /// Initializes a new instance of the <see cref="Saml20LogoutHandler"/> class. /// </summary> public Saml20LogoutHandler() { // Read the proper redirect url from config try { RedirectUrl = Saml2Config.GetConfig().ServiceProvider.Endpoints.LogoutEndpoint.RedirectUrl; } catch (Exception e) { Logger.Error(e.Message, e); } }
/// <summary> /// Validates the <see cref="Saml2Config"/>. /// </summary> /// <param name="config">The <see cref="Saml2Config"/> to validate.</param> public void ValidateConfig(Saml2Config config) { ValidateActions(config.Actions); ValidateAllowedAudienceUris(config.AllowedAudienceUris); ValidateAssertionProfile(config.AssertionProfile); ValidateCommonDomainCookie(config.CommonDomainCookie); ValidateLogging(config.Logging); ValidateMetadata(config.Metadata); ValidateState(config.State); _serviceProviderConfigValidator.ValidateServiceProviderConfig(config.ServiceProvider); ValidateIdentityProviders(config.IdentityProviders); }
/// <summary> /// Gets the decrypted assertion. /// </summary> /// <param name="elem">The elem.</param> /// <returns>The decrypted <see cref="Saml20EncryptedAssertion"/>.</returns> private static Saml20EncryptedAssertion GetDecryptedAssertion(XmlElement elem) { Logger.Debug(TraceMessages.EncryptedAssertionDecrypting); var decryptedAssertion = new Saml20EncryptedAssertion((RSA)Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().PrivateKey); decryptedAssertion.LoadXml(elem); decryptedAssertion.Decrypt(); Logger.DebugFormat(TraceMessages.EncryptedAssertionDecrypted, decryptedAssertion.Assertion.DocumentElement.OuterXml); return(decryptedAssertion); }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> /// <param name="context">The context.</param> /// <param name="elem">The elem.</param> private void HandleAssertion(HttpContext context, XmlElement elem) { Logger.DebugFormat(TraceMessages.AssertionProcessing, elem.OuterXml); var issuer = GetIssuer(elem); var endp = RetrieveIDPConfiguration(issuer); PreHandleAssertion(context, elem, endp); if (endp == null || endp.Metadata == null) { Logger.Error(ErrorMessages.AssertionIdentityProviderUnknown); throw new Saml20Exception(ErrorMessages.AssertionIdentityProviderUnknown); } var quirksMode = endp.QuirksMode; var assertion = new Saml20Assertion(elem, null, Saml2Config.GetConfig().AssertionProfile.AssertionValidator, quirksMode); // Check signatures if (!endp.OmitAssertionSignatureCheck) { if (!assertion.CheckSignature(GetTrustedSigners(endp.Metadata.GetKeys(KeyTypes.Signing), endp))) { Logger.Error(ErrorMessages.AssertionSignatureInvalid); throw new Saml20Exception(ErrorMessages.AssertionSignatureInvalid); } } // Check expiration if (assertion.IsExpired) { Logger.Error(ErrorMessages.AssertionExpired); throw new Saml20Exception(ErrorMessages.AssertionExpired); } // Check one time use if (assertion.IsOneTimeUse) { if (context.Cache[assertion.Id] != null) { Logger.Error(ErrorMessages.AssertionOneTimeUseExceeded); throw new Saml20Exception(ErrorMessages.AssertionOneTimeUseExceeded); } context.Cache.Insert(assertion.Id, string.Empty, null, assertion.NotOnOrAfter, Cache.NoSlidingExpiration); } Logger.DebugFormat(TraceMessages.AssertionParsed, assertion.Id); DoSignOn(context, assertion); }
/// <summary> /// Called when Application starts. /// </summary> /// <param name="context">The context.</param> public void OnStart(HttpApplication context) { var logger = Logging.LoggerProvider.LoggerFor(GetType()); logger.Debug("Attempting to fetch SAML metadata files"); var config = Saml2Config.GetConfig(); var metadataLocation = config.IdentityProviders.MetadataLocation; var identityProviders = config.IdentityProviders; if (!Directory.Exists(metadataLocation)) { throw new DirectoryNotFoundException("Metadata directory does not exist: " + metadataLocation); } // Get new metadata files foreach (var identityProvider in identityProviders) { logger.DebugFormat("Attempting to fetch SAML metadata file for identity provider {0}", identityProvider.Id); var metadataEndpoint = identityProvider.Endpoints.FirstOrDefault(x => x.Type == EndpointType.Metadata); if (metadataEndpoint == null) { continue; } var metadataEndpointUrl = metadataEndpoint.Url; var metadataFile = Path.Combine(metadataLocation, identityProvider.Id + ".xml"); // Fetch new file try { var client = new WebClient(); client.DownloadFile(metadataEndpointUrl, metadataFile + ".new"); // Wipe old file if (File.Exists(metadataFile)) { File.Delete(metadataFile); } // Move new file into place File.Move(metadataFile + ".new", metadataFile); logger.DebugFormat("Successfully updated SAML metadata file for identity provider {0}", identityProvider.Id); } catch (WebException ex) { logger.Warn(string.Format("Unable to fetch SAML metadata file for identity provider {0}", identityProvider.Id), ex); } } }
/// <summary> /// Initializes a new instance of the <see cref="Saml20SignonHandler"/> class. /// </summary> public Saml20SignonHandler() { _certificate = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate(); // Read the proper redirect url from config try { RedirectUrl = Saml2Config.GetConfig().ServiceProvider.Endpoints.SignOnEndpoint.RedirectUrl; } catch (Exception e) { Logger.Error(e.Message, e); } }
/// <summary> /// Creates the metadata document. /// </summary> /// <param name="context">The context.</param> /// <param name="sign">if set to <c>true</c> sign the document.</param> private void CreateMetadataDocument(HttpContext context, bool sign) { Logger.Debug(TraceMessages.MetadataDocumentBeingCreated); var configuration = Saml2Config.GetConfig(); var keyinfo = new KeyInfo(); var keyClause = new KeyInfoX509Data(Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate(), X509IncludeOption.EndCertOnly); keyinfo.AddClause(keyClause); var doc = new Saml20MetadataDocument(configuration, keyinfo, sign); Logger.Debug(TraceMessages.MetadataDocumentCreated); context.Response.Write(doc.ToXml(context.Response.ContentEncoding)); }
public void CanDecryptFOBSAssertion() { // Arrange var doc = AssertionUtil.LoadBase64EncodedXmlDocument(@"Assertions\fobs-assertion2"); var encryptedList = doc.GetElementsByTagName(EncryptedAssertion.ElementName, Saml20Constants.Assertion); // Do some mock configuration. var config = Saml2Config.GetConfig(); config.AllowedAudienceUris.Add(new AudienceUriElement { Uri = "https://saml.safewhere.net" }); config.IdentityProviders.MetadataLocation = @"Protocol\MetadataDocs\FOBS"; // Set it manually. Assert.That(Directory.Exists(config.IdentityProviders.MetadataLocation)); var cert = new X509Certificate2(@"Certificates\SafewhereTest_SFS.pfx", "test1234"); var encryptedAssertion = new Saml20EncryptedAssertion((RSA)cert.PrivateKey); encryptedAssertion.LoadXml((XmlElement)encryptedList[0]); // Act encryptedAssertion.Decrypt(); // Retrieve metadata var assertion = new Saml20Assertion(encryptedAssertion.Assertion.DocumentElement, null, false); var endp = config.IdentityProviders.FirstOrDefault(x => x.Id == assertion.Issuer); // Assert Assert.That(encryptedList.Count == 1); Assert.IsNotNull(endp, "Endpoint not found"); Assert.IsNotNull(endp.Metadata, "Metadata not found"); try { assertion.CheckValid(AssertionUtil.GetTrustedSigners(assertion.Issuer)); Assert.Fail("Verification should fail. Token does not include its signing key."); } catch (InvalidOperationException) { } Assert.IsNull(assertion.SigningKey, "Signing key is already present on assertion. Modify test."); Assert.That(assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endp.Metadata.GetKeys(KeyTypes.Signing), endp))); Assert.IsNotNull(assertion.SigningKey, "Signing key was not set on assertion instance."); }
/// <summary> /// Raises the <see cref="E:System.Web.UI.Control.Load"/> event. /// </summary> /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param> protected override void OnLoad(EventArgs e) { TitleText = Resources.PageIdentityProviderSelectTitle; HeaderText = Resources.PageIdentityProviderSelectTitle; BodyPanel.Controls.Add(new LiteralControl(Resources.PageIdentityProviderSelectDescription)); BodyPanel.Controls.Add(new LiteralControl("<br/><br/>")); var config = Saml2Config.GetConfig(); foreach (var endPoint in config.IdentityProviders) { if (endPoint.Metadata != null) { var link = new HyperLink { Text = string.IsNullOrEmpty(endPoint.Name) ? endPoint.Metadata.EntityId : endPoint.Name, NavigateUrl = IdpSelectionUtil.GetIdpLoginUrl(endPoint.Id) }; // Link text. If a name has been specified in web.config, use it. Otherwise, use id from metadata. BodyPanel.Controls.Add(link); BodyPanel.Controls.Add(new LiteralControl("<br/>")); } else { var label = new Label { Text = endPoint.Name }; label.Style.Add(HtmlTextWriterStyle.TextDecoration, "line-through"); BodyPanel.Controls.Add(label); label = new Label { Text = " (Metadata not found)" }; label.Style.Add(HtmlTextWriterStyle.FontSize, "x-small"); BodyPanel.Controls.Add(label); BodyPanel.Controls.Add(new LiteralControl("<br/>")); } } }
/// <summary> /// Builds a <see cref="Saml2Config"/> based on the current builder properties. /// </summary> /// <returns>A <see cref="Saml2Config"/>.</returns> public Saml2Config Build() { var config = new Saml2Config(); config.AssertionProfile.AssertionValidator = _assertionValidator; config.CommonDomainCookie.Enabled = !string.IsNullOrEmpty(_commonDomainCookieLocalReaderEndpoint); config.CommonDomainCookie.LocalReaderEndpoint = _commonDomainCookieLocalReaderEndpoint; config.IdentityProviderSelectionUrl = _identityProviderSelectionUrl; config.IdentityProviders.MetadataLocation = _identityProviderMetadataLocation; config.State.StateServiceFactory = _stateServiceFactory; config.Logging.LoggingFactory = _loggingFactory; if (_metadataConfig != null) { config.Metadata = _metadataConfig; } if (_serviceProviderConfig != null) { config.ServiceProvider = _serviceProviderConfig; } foreach (var action in _actions) { config.Actions.Add(action); } foreach (var allowedAudienceUri in _allowedAudienceUris) { config.AllowedAudienceUris.Add(allowedAudienceUri); } foreach (var identityProvider in _identityProviders) { config.IdentityProviders.Add(identityProvider); } foreach (var key in _stateSettings.Keys) { config.State.Settings.Add(key, _stateSettings[key]); } return(config); }
/// <summary> /// Validates the SAML20Federation configuration. /// </summary> /// <returns>True if validation passes, false otherwise</returns> public static bool ValidateConfiguration() { var config = Saml2Config.GetConfig(); if (config == null) { throw new ConfigurationErrorsException(ErrorMessages.ConfigMissingSaml2Element); } if (config.ServiceProvider == null) { throw new ConfigurationErrorsException(ErrorMessages.ConfigMissingServiceProviderElement); } if (string.IsNullOrEmpty(config.ServiceProvider.Id)) { throw new ConfigurationErrorsException(ErrorMessages.ConfigMissingServiceProviderIdAttribute); } if (config.ServiceProvider.SigningCertificate == null) { throw new ConfigurationErrorsException(ErrorMessages.ConfigMissingSigningCertificateElement); } // This will throw if no certificate or multiple certificates are found var certificate = config.ServiceProvider.SigningCertificate.GetCertificate(); if (!certificate.HasPrivateKey) { throw new ConfigurationErrorsException(ErrorMessages.ConfigSigningCertificateMissingPrivateKey); } if (config.IdentityProviders == null) { throw new ConfigurationErrorsException(ErrorMessages.ConfigMissingIdentityProvidersElement); } if (config.IdentityProviders.MetadataLocation == null) { throw new ConfigurationErrorsException(ErrorMessages.ConfigMissingMetadataLocation); } return(true); }
/// <summary> /// Gets the trusted signers. /// </summary> /// <param name="issuer">The issuer.</param> /// <returns>A list of trusted signing certificates.</returns> public static IEnumerable <AsymmetricAlgorithm> GetTrustedSigners(string issuer) { if (issuer == null) { throw new ArgumentNullException("issuer"); } var config = Saml2Config.GetConfig(); config.IdentityProviders.Refresh(); var idpEndpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == issuer); if (idpEndpoint == null) { throw new InvalidOperationException(string.Format("No idp endpoint found for issuer {0}", issuer)); } if (idpEndpoint.Metadata == null) { throw new InvalidOperationException(string.Format("No metadata found for issuer {0}", issuer)); } if (idpEndpoint.Metadata.Keys == null) { throw new InvalidOperationException(string.Format("No key descriptors found in metadata found for issuer {0}", issuer)); } var result = new List <AsymmetricAlgorithm>(1); foreach (var key in idpEndpoint.Metadata.Keys) { foreach (KeyInfoClause clause in (KeyInfo)key.KeyInfo) { var aa = XmlSignatureUtils.ExtractKey(clause); result.Add(aa); } } return(result); }
/// <summary> /// Performs the attribute query and adds the resulting attributes to <c>Saml20Identity.Current</c>. /// </summary> /// <param name="context">The http context.</param> public void PerformQuery(HttpContext context) { var config = Saml2Config.GetConfig(); var endpointId = StateService.Get <string>(Saml20AbstractEndpointHandler.IdpLoginSessionKey); if (string.IsNullOrEmpty(endpointId)) { Logger.Error(ErrorMessages.AttrQueryNoLogin); throw new InvalidOperationException(ErrorMessages.AttrQueryNoLogin); } var ep = config.IdentityProviders.FirstOrDefault(x => x.Id == endpointId); if (ep == null) { throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, endpointId)); } PerformQuery(context, ep); }
/// <summary> /// Determines which IdP an artifact has been sent from. /// </summary> /// <param name="artifact">The artifact.</param> /// <returns>An IdP configuration element</returns> private IdentityProviderElement DetermineIdp(string artifact) { var config = Saml2Config.GetConfig(); short typeCodeValue = -1; short endPointIndex = -1; var sourceIdHash = new byte[20]; var messageHandle = new byte[20]; if (ArtifactUtil.TryParseArtifact(artifact, ref typeCodeValue, ref endPointIndex, ref sourceIdHash, ref messageHandle)) { foreach (IdentityProviderElement ep in config.IdentityProviders) { var hash = ArtifactUtil.GenerateSourceIdHash(ep.Id); if (ByteArraysAreEqual(sourceIdHash, hash)) { return(ep); } } } return(null); }
/// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> public override void ProcessRequest(HttpContext context) { Logger.DebugFormat("{0}.{1} called", GetType(), "ProcessRequest()"); var config = Saml2Config.GetConfig(); if (config == null) { throw new Saml20Exception("Missing saml2 config section in web.config."); } var endp = config.ServiceProvider.Endpoints.FirstOrDefault(ep => ep.Type == EndpointType.SignOn); if (endp == null) { throw new Saml20Exception("Signon endpoint not found in configuration"); } var returnUrl = config.ServiceProvider.Server + endp.LocalPath + "?r=1"; var samlIdp = context.Request.Cookies[CommonDomainCookie.CommonDomainCookieName]; if (samlIdp != null) { returnUrl += "&_saml_idp=" + HttpUtility.UrlEncode(samlIdp.Value); Logger.DebugFormat(TraceMessages.CommonDomainCookieReceived, samlIdp.Value); Logger.Debug(TraceMessages.CommonDomainCookieRedirect); } else { Logger.DebugFormat(TraceMessages.CommonDomainCookieRedirectNotFound, returnUrl); } context.Response.Redirect(returnUrl); }
/// <summary> /// Handles a request. /// </summary> /// <param name="context">The context.</param> protected override void Handle(HttpContext context) { Logger.Debug(TraceMessages.SignOnHandlerCalled); // Some IdP's are known to fail to set an actual value in the SOAPAction header // so we just check for the existence of the header field. if (Array.Exists(context.Request.Headers.AllKeys, s => s == SoapConstants.SoapAction)) { HandleSoap(context, context.Request.InputStream); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) { HandleArtifact(context); } if (!string.IsNullOrEmpty(context.Request.Params["SamlResponse"])) { HandleResponse(context); } else { if (Saml2Config.GetConfig().CommonDomainCookie.Enabled&& context.Request.QueryString["r"] == null && context.Request.Params["cidp"] == null) { Logger.Debug(TraceMessages.CommonDomainCookieRedirectForDiscovery); context.Response.Redirect(Saml2Config.GetConfig().CommonDomainCookie.LocalReaderEndpoint); } else { Logger.WarnFormat(ErrorMessages.UnauthenticatedAccess, context.Request.RawUrl); SendRequest(context); } } }
/// <summary> /// Handles all artifact creations and redirects. /// </summary> /// <param name="destination">The destination.</param> /// <param name="localEndpointIndex">Index of the local endpoint.</param> /// <param name="signedSamlMessage">The signed SAML message.</param> /// <param name="relayState">The query string relay state value to add to the communication</param> private void ArtifactRedirect(IdentityProviderEndpointElement destination, short localEndpointIndex, XmlDocument signedSamlMessage, string relayState) { Logger.DebugFormat(TraceMessages.ArtifactRedirectReceived, signedSamlMessage.OuterXml); var config = Saml2Config.GetConfig(); var sourceId = config.ServiceProvider.Id; var sourceIdHash = ArtifactUtil.GenerateSourceIdHash(sourceId); var messageHandle = ArtifactUtil.GenerateMessageHandle(); var artifact = ArtifactUtil.CreateArtifact(HttpArtifactBindingConstants.ArtifactTypeCode, localEndpointIndex, sourceIdHash, messageHandle); Context.Cache.Insert(artifact, signedSamlMessage, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration); var destinationUrl = destination.Url + "?" + HttpArtifactBindingConstants.ArtifactQueryStringName + "=" + HttpUtility.UrlEncode(artifact); if (!string.IsNullOrEmpty(relayState)) { destinationUrl += "&relayState=" + relayState; } Logger.DebugFormat(TraceMessages.ArtifactCreated, artifact); Context.Response.Redirect(destinationUrl); }