/// <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)
        {
            Trace.TraceMethodCalled(GetType(), "ProcessRequest()");
            SAML20FederationConfig config = ConfigurationReader.GetConfig <SAML20FederationConfig>();

            if (config == null)
            {
                throw new Saml20Exception("Missing SAML20Federation config section in web.config.");
            }

            Saml20ServiceEndpoint endp
                = config.ServiceProvider.serviceEndpoints.Find(delegate(Saml20ServiceEndpoint ep) { return(ep.endpointType == EndpointType.SIGNON); });

            if (endp == null)
            {
                throw new Saml20Exception("Signon endpoint not found in configuration");
            }

            string redirectUrl = (string)context.Session["RedirectUrl"];

            if (!string.IsNullOrEmpty(redirectUrl))
            {
                context.Session.Remove("RedirectUrl");
                context.Response.Redirect(redirectUrl);
            }
            else if (string.IsNullOrEmpty(endp.RedirectUrl))
            {
                context.Response.Redirect("~/");
            }
            else
            {
                context.Response.Redirect(endp.RedirectUrl);
            }
        }
        /// <summary>
        /// Looks through the Identity Provider configurations and
        /// </summary>
        public IDPEndPoint RetrieveIDPConfiguration(string IDPId)
        {
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

            config.Endpoints.Refresh();
            return(config.FindEndPoint(IDPId));
        }
 protected void Page_Load(object sender, EventArgs e)
 {
     litIDPList.Text = "<ul>";
     SAML20FederationConfig.GetConfig().Endpoints.IDPEndPoints.ForEach(idp =>
                                                                       litIDPList.Text += "<li><a href=\"" + idp.GetIDPLoginUrl(false, false) + "\">" + (string.IsNullOrEmpty(idp.Name) ? idp.Id : idp.Name) + "</a></li>");
     litIDPList.Text += "</ul>";
 }
        /// <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(IDPEndPointElement destination, Int16 localEndpointIndex, XmlDocument signedSamlMessage, string relayState)
        {
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();
            string sourceId = config.ServiceProvider.ID;

            byte[] sourceIdHash  = ArtifactUtil.GenerateSourceIdHash(sourceId);
            byte[] messageHandle = ArtifactUtil.GenerateMessageHandle();

            string artifact = ArtifactUtil.CreateArtifact(HttpArtifactBindingConstants.ArtifactTypeCode, localEndpointIndex, sourceIdHash, messageHandle);

            _context.Cache.Insert(artifact, signedSamlMessage, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration);

            string destinationUrl = destination.Url + "?" + HttpArtifactBindingConstants.ArtifactQueryStringName + "=" +
                                    HttpUtility.UrlEncode(artifact);

            if (!string.IsNullOrEmpty(relayState))
            {
                destinationUrl += "&relayState=" + relayState;
            }

            if (Trace.ShouldTrace(TraceEventType.Information))
            {
                Trace.TraceData(TraceEventType.Information, string.Format(Tracing.CreatedArtifact, artifact, signedSamlMessage.OuterXml));
            }

            _context.Response.Redirect(destinationUrl);
        }
예제 #5
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(IDPEndPointElement destination, Saml20LogoutRequest request, string relayState)
 {
     SAML20FederationConfig config = SAML20FederationConfig.GetConfig();
     Int16 index = (Int16)config.ServiceProvider.LogoutEndpoint.endPointIndex;
     XmlDocument doc = request.GetXml();
     XmlSignatureUtils.SignDocument(doc, request.Request.ID);
     ArtifactRedirect(destination, index, doc, relayState);
 }
예제 #6
0
 /// <summary>
 /// Helper method for generating URL to a link, that the user can click to select that particular IDPEndPoint for authorization.
 /// Usually not called directly, but called from IDPEndPoint.GetIDPLoginUrl()
 /// </summary>
 /// <param name="idpId">Id of IDP that an authentication URL is needed for</param>
 /// <param name="forceAuthn">Specifies wether or not the user is forced to login even if the user is already logged in. True means that the user must login into the federation again even if the user was already logged in.</param>
 /// <param name="isPassive">Specifies wether or not the user must be promthed with a login screen at IdP if user is not already logged into the federation. True means that the user must not be promthed with a login screen.</param>
 /// <param name="desiredNsisLoa">Specifies the desired level of assurance.</param>
 /// <param name="desiredProfile">Specifies the desired type of profile (Person or Professional)</param>
 /// <returns>A URL that can be used for logging in at the IDP</returns>
 public static string GetIDPLoginUrl(string idpId, bool forceAuthn, bool isPassive, string desiredNsisLoa, string desiredProfile)
 {
     return(string.Format("{0}?{1}={2}&{3}={4}&{5}={6}&{7}={8}&{9}={10}", SAML20FederationConfig.GetConfig().ServiceProvider.SignOnEndpoint.localPath,
                          Saml20SignonHandler.IDPChoiceParameterName, HttpUtility.UrlEncode(idpId),
                          Saml20SignonHandler.IDPForceAuthn, forceAuthn.ToString(),
                          Saml20SignonHandler.IDPIsPassive, isPassive.ToString(),
                          Saml20SignonHandler.NsisLoa, desiredNsisLoa,
                          Saml20SignonHandler.Profile, desiredProfile));
 }
예제 #7
0
        /// <summary>
        /// Looks through the Identity Provider configurations and
        /// </summary>
        public IDPEndPoint RetrieveIDPConfiguration(string IDPId)
        {
            if (IDPId == null)
            {
                return(null);
            }
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

            return(config.FindEndPoint(IDPId));
        }
        /// <summary>
        /// Decrypts an assertion we received from "fælles-offentlige brugerstyring".
        /// </summary>
        private static void DecryptFOBSAssertion(string file)
        {
            string assertionBase64 = File.ReadAllText(file);

            byte[] assertionBytes = Convert.FromBase64String(assertionBase64);

            XmlDocument doc = new XmlDocument();

            doc.PreserveWhitespace = true;
            doc.Load(new MemoryStream(assertionBytes));

            XmlNodeList encryptedList =
                doc.GetElementsByTagName(EncryptedAssertion.ELEMENT_NAME, Saml20Constants.ASSERTION);

            Assert.That(encryptedList.Count == 1);

            // Do some mock configuration.
            FederationConfig config = FederationConfig.GetConfig();

            config.AllowedAudienceUris.Audiences.Add("https://saml.safewhere.net");

            SAML20FederationConfig descr = SAML20FederationConfig.GetConfig();

            descr.Endpoints.MetadataLocation = @"Saml20\Protocol\MetadataDocs\FOBS"; // Set it manually.
            Assert.That(Directory.Exists(descr.Endpoints.MetadataLocation));

            X509Certificate2         cert   = new X509Certificate2(@"Saml20\Certificates\SafewhereTest_SFS.pfx", "test1234");
            Saml20EncryptedAssertion encass =
                new Saml20EncryptedAssertion((RSA)cert.PrivateKey);

            encass.LoadXml((XmlElement)encryptedList[0]);
            encass.Decrypt();

            // Retrieve metadata
            Saml20Assertion assertion = new Saml20Assertion(encass.Assertion.DocumentElement, null, false);

            IDPEndPoint endp = descr.FindEndPoint(assertion.Issuer);

            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.");
            IEnumerable <string> validationFailures;

            Assert.That(assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endp.metadata.GetKeys(KeyTypes.signing), endp, out validationFailures)));
            Assert.IsNotNull(assertion.SigningKey, "Signing key was not set on assertion instance.");
        }
예제 #9
0
        /// <summary>
        /// Creates an artifact for the LogoutRequest and redirects the user to the IdP.
        /// </summary>
        /// <param name="idpEndPoint">The IdP endpoint</param>
        /// <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(IDPEndPoint idpEndPoint, IDPEndPointElement destination, Saml20LogoutRequest request, string relayState)
        {
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();
            Int16       index             = (Int16)config.ServiceProvider.LogoutEndpoint.endPointIndex;
            XmlDocument doc = request.GetXml();
            var         signingCertificate  = FederationConfig.GetConfig().GetFirstValidCertificate();
            var         shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(idpEndPoint.ShaHashingAlgorithm);
            var         signatureProvider   = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm);

            signatureProvider.SignAssertion(doc, request.Request.ID, signingCertificate);
            ArtifactRedirect(destination, index, doc, relayState);
        }
예제 #10
0
        private void GenerateMetadataExtensionDocument(HttpContext context)
        {
            SAML20FederationConfig configuration = SAML20FederationConfig.GetConfig();

            string sdID = configuration.ServiceProvider.ID;

            string xmlTemplatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Metadata/Templates/metadata.extended.template.xml");


            string fileContents = File.ReadAllText(xmlTemplatePath).Replace("[[SP.ID]]", sdID);

            context.Response.Write(fileContents); //, doc.ToXml(context.Response.ContentEncoding)
        }
        private void CreateMetadataDocument(HttpContext context, bool sign)
        {
            SAML20FederationConfig configuration = SAML20FederationConfig.GetConfig();

            KeyInfo         keyinfo   = new KeyInfo();
            KeyInfoX509Data keyClause = new KeyInfoX509Data(FederationConfig.GetConfig().SigningCertificate.GetCertificate(), X509IncludeOption.EndCertOnly);

            keyinfo.AddClause(keyClause);

            Saml20MetadataDocument doc = new Saml20MetadataDocument(configuration, keyinfo, sign);

            context.Response.Write(doc.ToXml(context.Response.ContentEncoding));
        }
예제 #12
0
 protected void Page_Load(object sender, EventArgs e)
 {
     Title = "My page on SP " + SAML20FederationConfig.GetConfig().ServiceProvider.ID;
     if (Request.QueryString["action"] == "sso")
     {
         // Example of logging required by the requirements BSA6/SSO6 ("Id of internal account that is matched to SAML Assertion")
         // Since FormsAuthentication is used in this sample, the user name to log can be found in context.User.Identity.Name.
         // This user will not be set until after a new redirect, so unfortunately we cannot just log it in our LogAction.LoginAction
         AuditLogging.logEntry(Direction.IN, Operation.LOGIN, "ServiceProvider login",
                               "SP internal user id: " +
                               (Context.User.Identity.IsAuthenticated ? Context.User.Identity.Name : "(not logged in)"));
     }
 }
예제 #13
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Saml20LogoutHandler"/> class.
 /// </summary>
 public Saml20LogoutHandler()
 {
     // Read the proper redirect url from config
     try
     {
         RedirectUrl = SAML20FederationConfig.GetConfig().ServiceProvider.LogoutEndpoint.RedirectUrl;
         ErrorBehaviour = SAML20FederationConfig.GetConfig().ServiceProvider.LogoutEndpoint.ErrorBehaviour.ToString();
     }
     catch (Exception e)
     {
         if (Trace.ShouldTrace(TraceEventType.Error))
             Trace.TraceData(TraceEventType.Error, e.ToString());
     }
 }
예제 #14
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.ChooseIDP;
            HeaderText = Resources.ChooseIDP;

            BodyPanel.Controls.Add(new LiteralControl(Resources.ChooseDesc));
            BodyPanel.Controls.Add(new LiteralControl("<br/><br/>"));
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

            foreach (IDPEndPoint endPoint in config.IDPEndPoints)
            {
                if (endPoint.metadata != null)
                {
                    HyperLink link = new HyperLink();

                    // Link text. If a name has been specified in web.config, use it. Otherwise, use id from metadata.
                    link.Text = string.IsNullOrEmpty(endPoint.Name) ? endPoint.metadata.EntityId : endPoint.Name;

                    string forceAuthnAsString = HttpContext.Current.Request.Params[Saml20SignonHandler.IDPForceAuthn];
                    bool   forceAuthn;
                    bool.TryParse(forceAuthnAsString, out forceAuthn);

                    string isPassiveAsString = HttpContext.Current.Request.Params[Saml20SignonHandler.IDPIsPassive];
                    bool   isPassive;
                    bool.TryParse(isPassiveAsString, out isPassive);

                    string desiredNsisLoa = HttpContext.Current.Request.Params[Saml20SignonHandler.NsisLoa];
                    string desiredProfile = HttpContext.Current.Request[Saml20SignonHandler.Profile];

                    link.NavigateUrl = endPoint.GetIDPLoginUrl(forceAuthn, isPassive, desiredNsisLoa, desiredProfile);
                    BodyPanel.Controls.Add(link);
                    BodyPanel.Controls.Add(new LiteralControl("<br/>"));
                }
                else
                {
                    Label label = new Label();
                    label.Text = endPoint.Name;
                    label.Style.Add(HtmlTextWriterStyle.TextDecoration, "line-through");
                    BodyPanel.Controls.Add(label);

                    label      = new Label();
                    label.Text = " (Metadata not found)";
                    label.Style.Add(HtmlTextWriterStyle.FontSize, "x-small");
                    BodyPanel.Controls.Add(label);

                    BodyPanel.Controls.Add(new LiteralControl("<br/>"));
                }
            }
        }
예제 #15
0
        /// <summary>
        /// Validates if a AssuranceLevel is equals to or higher than a minimum required AssuranceLevel.
        /// If validation fails, response is modified to display an error page.
        /// </summary>
        /// <returns>True if valid, otherwise false (and modified response).</returns>
        private bool ValidateAssuranceLevel(string assouranceLevel, HttpContext context, XmlElement assertionXml)
        {
            var minAL = SAML20FederationConfig.GetConfig().MinimumAssuranceLevel;

            if (assouranceLevel != null &&
                int.TryParse(assouranceLevel, out var sourceLoaInt) &&
                int.TryParse(minAL, out var minLoaInt) &&
                sourceLoaInt >= minLoaInt)
            {
                return(true);
            }

            HandleLoaValidationError(Resources.NsisLoaTooLow, assouranceLevel, minAL, context, assertionXml);
            return(false);
        }
예제 #16
0
        /// <summary>
        /// Gets a default instance of this class with meaningful default values set.
        /// </summary>
        /// <returns></returns>
        public static Saml20AttributeQuery GetDefault()
        {
            Saml20AttributeQuery result = new Saml20AttributeQuery();

            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

            if (config.ServiceProvider == null || string.IsNullOrEmpty(config.ServiceProvider.ID))
            {
                throw new Saml20FormatException(Resources.ServiceProviderNotSet);
            }

            result.Issuer = config.ServiceProvider.ID;

            return(result);
        }
예제 #17
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.SamlContext"/> 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(SamlHttpContext context)
        {
            try
            {
                Trace.TraceMethodCalled(GetType(), "ProcessRequest()");
                SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

                if (config == null)
                {
                    throw new Saml20Exception("Missing SAML20Federation config section in web.config.");
                }

                Saml20ServiceEndpoint endp
                    = config.ServiceProvider.serviceEndpoints.Find(delegate(Saml20ServiceEndpoint ep) { return(ep.endpointType == EndpointType.SIGNON); });

                if (endp == null)
                {
                    throw new Saml20Exception("Signon endpoint not found in configuration");
                }

                string returnUrl = config.ServiceProvider.Server + endp.localPath + "?r=1";

                SamlHttpCookie samlIdp = context.Request.Cookies[CommonDomainCookie.COMMON_DOMAIN_COOKIE_NAME];

                if (samlIdp != null)
                {
                    returnUrl += "&_saml_idp=" + HttpUtility.UrlEncode(samlIdp.Value);

                    if (Trace.ShouldTrace(TraceEventType.Information))
                    {
                        Trace.TraceData(TraceEventType.Information, string.Format(Tracing.CDC, samlIdp.Value));
                    }

                    AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT,
                                          "Redirection to Signon endpoint found in Common Domain Cookie: " + samlIdp.Value);
                }
                else
                {
                    AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT,
                                          "Redirection to Signon endpoint, no Common Domain Cookie found: " + returnUrl);
                }
                context.Response.Redirect(returnUrl);
            }
            catch (Exception ex)
            {
                HandleError(context, ex);
            }
        }
예제 #18
0
        /// <summary>
        /// Handles a request.
        /// </summary>
        /// <param name="context">The context.</param>
        protected override void Handle(HttpContext context)
        {
            Trace.TraceMethodCalled(GetType(), "Handle()");

            //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, delegate(string s) { return(s == SOAPConstants.SOAPAction); }))
            {
                SessionStore.AssertSessionExists();

                HandleSOAP(context, context.Request.InputStream);
                return;
            }

            if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"]))
            {
                SessionStore.AssertSessionExists();

                HandleArtifact(context);
            }

            if (!string.IsNullOrEmpty(context.Request.Params["SamlResponse"]))
            {
                SessionStore.AssertSessionExists();

                HandleResponse(context);
            }
            else
            {
                if (SAML20FederationConfig.GetConfig().CommonDomain.Enabled&& context.Request.QueryString["r"] == null &&
                    context.Request.Params["cidp"] == null)
                {
                    AuditLogging.logEntry(Direction.OUT, Operation.DISCOVER, "Redirecting to Common Domain for IDP discovery");
                    context.Response.Redirect(SAML20FederationConfig.GetConfig().CommonDomain.LocalReaderEndpoint);
                }
                else
                {
                    AuditLogging.logEntry(Direction.IN, Operation.ACCESS,
                                          "User accessing resource: " + context.Request.RawUrl +
                                          " without authentication.");

                    SessionStore.CreateSessionIfNotExists();

                    SendRequest(context);
                }
            }
        }
예제 #19
0
        /// <summary>
        /// Returns an instance of the class with meaningful default values set.
        /// </summary>
        /// <returns></returns>
        public static Saml20LogoutRequest GetDefault()
        {
            Saml20LogoutRequest result = new Saml20LogoutRequest();

            result.SubjectToLogOut = new NameID();
            //format
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

            if (config.ServiceProvider == null || string.IsNullOrEmpty(config.ServiceProvider.ID))
            {
                throw new Saml20FormatException(Resources.ServiceProviderNotSet);
            }

            result.Issuer = config.ServiceProvider.ID;

            return(result);
        }
예제 #20
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Saml20SignonHandler"/> class.
        /// </summary>
        public Saml20SignonHandler()
        {
            _certificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate();

            // Read the proper redirect url from config
            try
            {
                RedirectUrl    = SAML20FederationConfig.GetConfig().ServiceProvider.SignOnEndpoint.RedirectUrl;
                ErrorBehaviour = SAML20FederationConfig.GetConfig().ServiceProvider.SignOnEndpoint.ErrorBehaviour.ToString();
            }
            catch (Exception e)
            {
                if (Trace.ShouldTrace(TraceEventType.Error))
                {
                    Trace.TraceData(TraceEventType.Error, e.ToString());
                }
            }
        }
        /// <summary>
        /// This method converts the received Saml assertion into a .Net principal.
        /// </summary>
        internal static IPrincipal InitSaml20Identity(Saml20AssertionLite assertion)
        {
            // Find IDPEndPoint
            IDPEndPoint point = SAML20FederationConfig.GetConfig().FindEndPoint(assertion.Issuer);

            bool isPersistentPseudonym = assertion.Subject.Format == Saml20Constants.NameIdentifierFormats.Persistent;
            // Protocol-level support for persistent pseudonyms: If a mapper has been configured, use it here before constructing the principal.
            string subjectIdentifier = assertion.Subject.Value;

            if (isPersistentPseudonym && point.PersistentPseudonym != null)
            {
                subjectIdentifier = point.PersistentPseudonym.GetMapper().MapIdentity(assertion.Subject);
            }

            // Create identity
            var identity = new Saml20Identity(subjectIdentifier, assertion.Attributes, isPersistentPseudonym ? assertion.Subject.Value : null);

            return(new GenericPrincipal(identity, new string[] { }));
        }
예제 #22
0
        /// <summary>
        /// Performs the attribute query and adds the resulting attributes to Saml20Identity.Current.
        /// </summary>
        /// <param name="context">The http context.</param>
        public void PerformQuery(HttpContext context)
        {
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();
            string endpointId             = Saml20PrincipalCache.GetSaml20AssertionLite().Issuer;

            if (string.IsNullOrEmpty(endpointId))
            {
                Trace.TraceData(TraceEventType.Information, Tracing.AttrQueryNoLogin);
                throw new InvalidOperationException(Tracing.AttrQueryNoLogin);
            }

            IDPEndPoint ep = config.FindEndPoint(endpointId);

            if (ep == null)
            {
                throw new Saml20Exception(string.Format("Unable to find information about the IdP with id \"{0}\"", endpointId));
            }

            PerformQuery(context, ep);
        }
예제 #23
0
        /// <summary>
        /// Performs the attribute query and adds the resulting attributes to Saml20Identity.Current.
        /// </summary>
        /// <param name="context">The http context.</param>
        public void PerformQuery(HttpContext context)
        {
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();
            string endpointId             = context.Session[Saml20AbstractEndpointHandler.IDPLoginSessionKey].ToString();

            if (string.IsNullOrEmpty(endpointId))
            {
                Trace.TraceData(TraceEventType.Information, Tracing.AttrQueryNoLogin);
                throw new InvalidOperationException(Tracing.AttrQueryNoLogin);
            }

            IDPEndPoint ep = config.FindEndPoint(endpointId);

            if (ep == null)
            {
                throw new Saml20Exception(string.Format("Unable to find information about the IdP with id \"{0}\"", endpointId));
            }

            PerformQuery(context, ep);
        }
        /// <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)
        {
            try
            {
                Trace.TraceMethodCalled(GetType(), "ProcessRequest()");
                SAML20FederationConfig config = SAML20FederationConfig.GetConfig();

                if (config == null)
                {
                    throw new Saml20Exception("Missing SAML20Federation config section in web.config.");
                }

                Saml20ServiceEndpoint endp
                    = config.ServiceProvider.serviceEndpoints.Find(delegate(Saml20ServiceEndpoint ep) { return(ep.endpointType == EndpointType.SIGNON); });

                if (endp == null)
                {
                    throw new Saml20Exception("Signon endpoint not found in configuration");
                }

                string redirectUrl = (string)SessionFactory.SessionContext.Current[SessionConstants.RedirectUrl];

                if (!string.IsNullOrEmpty(redirectUrl))
                {
                    SessionFactory.SessionContext.Current.Remove(SessionConstants.RedirectUrl);
                    context.Response.Redirect(redirectUrl);
                }
                else if (string.IsNullOrEmpty(endp.RedirectUrl))
                {
                    context.Response.Redirect("~/");
                }
                else
                {
                    context.Response.Redirect(endp.RedirectUrl);
                }
            }
            catch (Exception ex)
            {
                HandleError(context, ex);
            }
        }
예제 #25
0
        public static IEnumerable <AsymmetricAlgorithm> GetTrustedSigners(string issuer)
        {
            if (issuer == null)
            {
                throw new ArgumentNullException("issuer");
            }

            SAML20FederationConfig config = ConfigurationReader.GetConfig <SAML20FederationConfig>();

            config.Endpoints.Refresh();
            IDPEndPoint idpEndpoint = config.FindEndPoint(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));
            }

            List <AsymmetricAlgorithm> result = new List <AsymmetricAlgorithm>(1);

            foreach (KeyDescriptor key in idpEndpoint.metadata.Keys)
            {
                KeyInfo ki = (KeyInfo)key.KeyInfo;
                foreach (KeyInfoClause clause in ki)
                {
                    AsymmetricAlgorithm aa = XmlSignatureUtils.ExtractKey(clause);
                    result.Add(aa);
                }
            }

            return(result);
        }
예제 #26
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 IDPEndPoint DetermineIdp(string artifact)
        {
            SAML20FederationConfig config = SAML20FederationConfig.GetConfig();
            
            short typeCodeValue = -1;
            short endPointIndex = -1;
            byte[] sourceIdHash = new byte[20];
            byte[] messageHandle = new byte[20];

            if (ArtifactUtil.TryParseArtifact(artifact, ref typeCodeValue, ref endPointIndex, ref sourceIdHash, ref messageHandle))
            {
                foreach(IDPEndPoint ep in config.IDPEndPoints)
                {
                    byte[] hash = ArtifactUtil.GenerateSourceIdHash(ep.Id);
                    if (AreEqual(sourceIdHash, hash))
                        return ep;
                }
            }
            
            return null;
        }
예제 #27
0
        /// <summary>
        /// Displays an error page.
        /// </summary>
        /// <param name="context">The current HTTP context.</param>
        /// <param name="errorMessage">The error message.</param>
        /// <param name="overrideConfigSetting">if set to <c>true</c> [override config setting].</param>
        /// <param name="exceptionCreatorFunc"></param>
        public void HandleError(HttpContext context, string errorMessage, bool overrideConfigSetting, Func <string, Saml20Exception> exceptionCreatorFunc)
        {
            Trace.TraceData(TraceEventType.Error, "Error: " + errorMessage);

            Boolean showError       = SAML20FederationConfig.GetConfig().ShowError;
            String  DEFAULT_MESSAGE = "Unable to validate SAML message!";

            if (!string.IsNullOrEmpty(ErrorBehaviour) && ErrorBehaviour.Equals(dk.nita.saml20.config.ErrorBehaviour.THROWEXCEPTION.ToString()))
            {
                var exception = showError ? exceptionCreatorFunc(errorMessage) : exceptionCreatorFunc(DEFAULT_MESSAGE);
                throw exception;
            }
            else
            {
                ErrorPage page = new ErrorPage();
                page.OverrideConfig = overrideConfigSetting;
                page.ErrorText      = (showError) ? errorMessage?.Replace("\n", "<br />") : DEFAULT_MESSAGE;
                page.ProcessRequest(context);
                context.Response.End();
            }
        }
예제 #28
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.ChooseIDP;
            HeaderText = Resources.ChooseIDP;

            BodyPanel.Controls.Add(new LiteralControl(Resources.ChooseDesc));
            BodyPanel.Controls.Add(new LiteralControl("<br/><br/>"));
            SAML20FederationConfig config = ConfigurationReader.GetConfig <SAML20FederationConfig>();

            config.Endpoints.Refresh();

            foreach (IDPEndPoint endPoint in config.IDPEndPoints)
            {
                if (endPoint.metadata != null)
                {
                    HyperLink link = new HyperLink();

                    // Link text. If a name has been specified in web.config, use it. Otherwise, use id from metadata.
                    link.Text = string.IsNullOrEmpty(endPoint.Name) ? endPoint.metadata.EntityId : endPoint.Name;

                    link.NavigateUrl = endPoint.GetIDPLoginUrl();
                    BodyPanel.Controls.Add(link);
                    BodyPanel.Controls.Add(new LiteralControl("<br/>"));
                }
                else
                {
                    Label label = new Label();
                    label.Text = endPoint.Name;
                    label.Style.Add(HtmlTextWriterStyle.TextDecoration, "line-through");
                    BodyPanel.Controls.Add(label);

                    label      = new Label();
                    label.Text = " (Metadata not found)";
                    label.Style.Add(HtmlTextWriterStyle.FontSize, "x-small");
                    BodyPanel.Controls.Add(label);

                    BodyPanel.Controls.Add(new LiteralControl("<br/>"));
                }
            }
        }
예제 #29
0
        /// <summary>
        /// Validates the LoA of the session to determine if it adheres to the configured requirements of the service provider.
        /// If validation fails, response is modified to display an error page.
        /// </summary>
        /// <returns>True if valid, otherwise false.</returns>
        private bool ValidateLoA(HttpContext context, Saml20Assertion assertion, XmlElement assertionXml)
        {
            // If AssuranceLevel is allowed, and it's present in assertion, validate.
            var allowAL     = SAML20FederationConfig.GetConfig().AllowAssuranceLevel;
            var assertionAL = GetAssuranceLevel(assertion);

            if (allowAL && assertionAL != null)
            {
                return(ValidateAssuranceLevel(assertionAL, context, assertionXml));
            }

            // If NSIS LoA is missing, invalidate.
            var assertionNsisLoa = GetNsisLoa(assertion);

            if (assertionNsisLoa == null)
            {
                AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, Resources.NsisLoaMissing + " Assertion: " + assertionXml.OuterXml);
                HandleError(context, Resources.NsisLoaMissing);
                return(false);
            }

            return(ValidateNsisLoa(assertionNsisLoa, context, assertionXml));
        }
예제 #30
0
        /// <summary>
        /// Validates if a NSIS LoA is equals to or higher than a minimum required LoA.
        /// If validation fails, response is modified to display an error page.
        /// </summary>
        /// <returns>True if valid, otherwise false (and modified response).</returns>
        private bool ValidateNsisLoa(string loa, HttpContext context, XmlElement assertionXml)
        {
            var demandedNsisLoa = SessionStore.CurrentSession[SessionConstants.ExpectedNsisLoa]?.ToString();
            var minLoa          = demandedNsisLoa ?? SAML20FederationConfig.GetConfig().MinimumNsisLoa;

            if (loa == minLoa)
            {
                return(true);
            }

            switch (minLoa)
            {
            case "High" when loa != "High":
            case "Substantial" when loa != "High" && loa != "Substantial":
                var msgTemplate = demandedNsisLoa != null ?
                                  Resources.NsisLoaTooLowAccordingToDemand :
                                  Resources.NsisLoaTooLow;
                HandleLoaValidationError(msgTemplate, loa, demandedNsisLoa, context, assertionXml);
                return(false);

            default:
                return(true);
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="Saml20MetadataDocument"/> class.
 /// </summary>
 /// <param name="config">The config.</param>
 /// <param name="keyinfo">key information for the service provider certificate.</param>
 /// <param name="sign">if set to <c>true</c> the metadata document will be signed.</param>
 public Saml20MetadataDocument(SAML20FederationConfig config, KeyInfo keyinfo, bool sign)
     : this(sign)
 {
     ConvertToMetadata(config, keyinfo);
 }
        /// <summary>
        /// Takes the Safewhere configuration class and converts it to a SAML2.0 metadata document.
        /// </summary>        
        private void ConvertToMetadata(SAML20FederationConfig config, KeyInfo keyinfo)
        {
            EntityDescriptor entity = CreateDefaultEntity();
            entity.entityID = config.ServiceProvider.ID;
            entity.validUntil = DateTime.Now.AddDays(7);

            SPSSODescriptor spDescriptor = new SPSSODescriptor();
            spDescriptor.protocolSupportEnumeration = new string[] { Saml20Constants.PROTOCOL };
            spDescriptor.AuthnRequestsSigned = XmlConvert.ToString(true);
            spDescriptor.WantAssertionsSigned = XmlConvert.ToString(true);
            if(config.ServiceProvider.NameIdFormats.All)
            {
                spDescriptor.NameIDFormat = new string[] {Saml20Constants.NameIdentifierFormats.Email,
                                                          Saml20Constants.NameIdentifierFormats.Entity,
                                                          Saml20Constants.NameIdentifierFormats.Kerberos,
                                                          Saml20Constants.NameIdentifierFormats.Persistent,
                                                          Saml20Constants.NameIdentifierFormats.Transient,
                                                          Saml20Constants.NameIdentifierFormats.Unspecified,
                                                          Saml20Constants.NameIdentifierFormats.Windows,
                                                          Saml20Constants.NameIdentifierFormats.X509SubjectName};
            }else
            {
                spDescriptor.NameIDFormat = new string[config.ServiceProvider.NameIdFormats.NameIdFormats.Count];
                int count = 0;
                foreach(NameIdFormatElement elem in config.ServiceProvider.NameIdFormats.NameIdFormats)
                {
                    spDescriptor.NameIDFormat[count++] = elem.NameIdFormat;
                }
            }

            Uri baseURL = new Uri(config.ServiceProvider.Server);
            List<Endpoint> logoutServiceEndpoints = new List<Endpoint>();
            List<IndexedEndpoint> signonServiceEndpoints = new List<IndexedEndpoint>();

            List<IndexedEndpoint> artifactResolutionEndpoints = new List<IndexedEndpoint>(2);

            // Include endpoints.
            foreach (Saml20ServiceEndpoint endpoint in config.ServiceProvider.serviceEndpoints)
            {
                if (endpoint.endpointType == EndpointType.SIGNON)
                {
                    IndexedEndpoint loginEndpoint = new IndexedEndpoint();
                    loginEndpoint.index = endpoint.endPointIndex;
                    loginEndpoint.isDefault = true;
                    loginEndpoint.Location = new Uri(baseURL, endpoint.localPath).ToString();
                    loginEndpoint.Binding = GetBinding(endpoint.Binding, Saml20Constants.ProtocolBindings.HTTP_Post);
                    signonServiceEndpoints.Add(loginEndpoint);

                    IndexedEndpoint artifactSignonEndpoint = new IndexedEndpoint();
                    artifactSignonEndpoint.Binding = Saml20Constants.ProtocolBindings.HTTP_SOAP;
                    artifactSignonEndpoint.index = loginEndpoint.index;
                    artifactSignonEndpoint.Location = loginEndpoint.Location;
                    artifactResolutionEndpoints.Add(artifactSignonEndpoint);

                    continue;
                }

                if (endpoint.endpointType == EndpointType.LOGOUT)
                {
                    Endpoint logoutEndpoint = new Endpoint();
                    logoutEndpoint.Location = new Uri(baseURL, endpoint.localPath).ToString();
                    logoutEndpoint.ResponseLocation = logoutEndpoint.Location;
                    logoutEndpoint.Binding = GetBinding(endpoint.Binding, Saml20Constants.ProtocolBindings.HTTP_Post);
                    logoutServiceEndpoints.Add(logoutEndpoint);

                    logoutEndpoint = new Endpoint();
                    logoutEndpoint.Location = new Uri(baseURL, endpoint.localPath).ToString();
                    logoutEndpoint.ResponseLocation = logoutEndpoint.Location;
                    logoutEndpoint.Binding = GetBinding(endpoint.Binding, Saml20Constants.ProtocolBindings.HTTP_Redirect);
                    logoutServiceEndpoints.Add(logoutEndpoint);

                    IndexedEndpoint artifactLogoutEndpoint = new IndexedEndpoint();
                    artifactLogoutEndpoint.Binding = Saml20Constants.ProtocolBindings.HTTP_SOAP;
                    artifactLogoutEndpoint.index = endpoint.endPointIndex;
                    artifactLogoutEndpoint.Location = logoutEndpoint.Location;
                    artifactResolutionEndpoints.Add(artifactLogoutEndpoint);

                    continue;
                }
            }

            spDescriptor.SingleLogoutService = logoutServiceEndpoints.ToArray();
            spDescriptor.AssertionConsumerService = signonServiceEndpoints.ToArray();

            // NameIdFormat
            if (!string.IsNullOrEmpty(config.NameIdFormat))
            {
                spDescriptor.NameIDFormat = new string[] { config.NameIdFormat };
            }

            // Attribute consuming service.
            if (config.RequestedAttributes.Attributes.Count > 0)
            {
                AttributeConsumingService attConsumingService = new AttributeConsumingService();
                spDescriptor.AttributeConsumingService = new AttributeConsumingService[] { attConsumingService };
                attConsumingService.index = signonServiceEndpoints[0].index;
                attConsumingService.isDefault = true;
                attConsumingService.ServiceName = new LocalizedName[] { new LocalizedName("SP", "da") };

                attConsumingService.RequestedAttribute =
                    new RequestedAttribute[config.RequestedAttributes.Attributes.Count];

                for (int i = 0; i < config.RequestedAttributes.Attributes.Count; i++)
                {
                    attConsumingService.RequestedAttribute[i] = new RequestedAttribute();
                    attConsumingService.RequestedAttribute[i].Name = config.RequestedAttributes.Attributes[i].name;
                    if (config.RequestedAttributes.Attributes[i].IsRequired)
                        attConsumingService.RequestedAttribute[i].isRequired = true;
                    attConsumingService.RequestedAttribute[i].NameFormat = SamlAttribute.NAMEFORMAT_BASIC;
                }
            }
            else
            {
                spDescriptor.AttributeConsumingService = new AttributeConsumingService[0];
            }

            if(config.Metadata != null && config.Metadata.IncludeArtifactEndpoints)
                spDescriptor.ArtifactResolutionService = artifactResolutionEndpoints.ToArray();

            entity.Items = new object[] { spDescriptor };

            // Keyinfo
            KeyDescriptor keySigning = new KeyDescriptor();
            KeyDescriptor keyEncryption = new KeyDescriptor();
            spDescriptor.KeyDescriptor = new KeyDescriptor[] { keySigning, keyEncryption };

            keySigning.use = KeyTypes.signing;
            keySigning.useSpecified = true;

            keyEncryption.use = KeyTypes.encryption;
            keyEncryption.useSpecified = true;

            // Ugly conversion between the .Net framework classes and our classes ... avert your eyes!!
            keySigning.KeyInfo = Serialization.DeserializeFromXmlString<Schema.XmlDSig.KeyInfo>(keyinfo.GetXml().OuterXml);
            keyEncryption.KeyInfo = keySigning.KeyInfo;

            // apply the <Organization> element
            if (config.ServiceProvider.Organization != null)
                entity.Organization = config.ServiceProvider.Organization;

            if (config.ServiceProvider.ContactPerson != null && config.ServiceProvider.ContactPerson.Count > 0)
                entity.ContactPerson = config.ServiceProvider.ContactPerson.ToArray();
        }