예제 #1
0
        /// <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);
        }
예제 #2
0
        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);
            }
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        /// <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));
        }
예제 #7
0
        /// <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());
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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"]);
        }
예제 #10
0
        /// <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);
        }
예제 #11
0
        /// <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);
        }
예제 #12
0
        /// <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);
        }
예제 #13
0
        /// <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
예제 #14
0
 /// <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);
     }
 }
예제 #15
0
        /// <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);
        }
예제 #16
0
        /// <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);
        }
예제 #17
0
        /// <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);
                }
            }
        }
예제 #19
0
        /// <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);
            }
        }
예제 #20
0
        /// <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.");
            }
예제 #22
0
        /// <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/>"));
                }
            }
        }
예제 #23
0
        /// <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);
        }
예제 #24
0
        /// <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);
        }
예제 #25
0
        /// <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);
        }
예제 #26
0
        /// <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);
        }
예제 #27
0
        /// <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);
        }
예제 #28
0
        /// <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);
        }
예제 #29
0
        /// <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);
                }
            }
        }
예제 #30
0
        /// <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);
        }