/// <summary> /// Creates an instance of CustomSecurityTokenServiceConfiguration. /// </summary> public CustomSecurityTokenServiceConfiguration(string idp) : base(idp) { CustomAdsTextTraceSource ts = new CustomAdsTextTraceSource("IdpAds.CustomSecurityTokenServiceConfiguration.CustomSecurityTokenServiceConfiguration", "AdsTraceSource", SourceLevels.Information); SecurityTokenService = typeof(CustomSecurityTokenService); // GFIPM S2S 8.8.2.6.e Modify the "NotOnOrAfter" this.DefaultTokenLifetime = TimeSpan.FromSeconds(Constants.AdsAssertionValidityPeriod); ts.TraceInformation("DefaultTokenLifetime: " + this.DefaultTokenLifetime.TotalSeconds.ToString()); }
// Handle Condition/Delegates protected override IClaimsIdentity CreateClaims(Saml2SecurityToken samlToken) { CustomAdsTextTraceSource ts = new CustomAdsTextTraceSource("IdpAds.OnBehalfOfSaml2SecurityTokenHandler.CreateClaims", "AdsTraceSource", SourceLevels.Information); // process subject in base IClaimsIdentity subject = base.CreateClaims(samlToken); Saml2Assertion assertion = samlToken.Assertion; // process Condition/Delegation ts.TraceInformation("assertion.Conditions Type: " + assertion.Conditions.GetType().Name); if (assertion.Conditions is Saml2ConditionsDelegateWrapper) { Saml2ConditionsDelegateWrapper delegateData = assertion.Conditions as Saml2ConditionsDelegateWrapper; //iterate over the Condition delegate elements //Check if there are delegates within an incoming assertion IClaimsIdentity currentSubject = subject; if (delegateData != null && delegateData.Delegates != null) { // Add the delegates in the DelegationRestrictionType ts.TraceInformation("Number of Delegates: " + delegateData.Delegates.Delegate.Length); for (int i = 0; i < delegateData.Delegates.Delegate.Length; i++) { DelegateType del = delegateData.Delegates.Delegate[i]; if (del != null) { string nameId = del.Item.ToString(); var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.Name, nameId)); claims.Add(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(del.DelegationInstant, DateTimeFormats.Generated))); // now add to current subject currentSubject.Actor = new ClaimsIdentity(claims); currentSubject = currentSubject.Actor; } } } } return subject; }
//string TokenToString(SecurityToken token) //{ // StringBuilder stringBuilder = new StringBuilder(); // XmlWriter xr = XmlWriter.Create(new StringWriter(stringBuilder), new XmlWriterSettings { OmitXmlDeclaration = true }); // this.WriteToken(xr, token); // xr.Flush(); // return stringBuilder.ToString(); //} //void SerializeTokenToStream(Saml2SecurityToken saml2Token, StreamWriter file, string section) //{ // // serialize the token // file.WriteLine(section); // if (saml2Token.Assertion.Issuer != null) // { // file.WriteLine("\tIssuer: " + saml2Token.Assertion.Issuer.Value); // } // else // { // file.WriteLine("Saml 2 Assertion Issuer: NULL"); // } // if (saml2Token.Assertion.Issuer != null) // { // file.WriteLine("\tSigningCredentials: " + saml2Token.Assertion.SigningCredentials.ToString()); // } // else // { // file.WriteLine("Saml 2 Assertion SigningCredentials: NULL"); // } // if (saml2Token.Assertion.Subject != null) // { // if (saml2Token.Assertion.Subject.NameId != null) // { // file.WriteLine("\tSubject NameId: " + saml2Token.Assertion.Subject.NameId.Value); // } // else // { // file.WriteLine("\tSubject NameId: NULL"); // } // } // else // { // file.WriteLine("Saml 2 Assertion Subject: NULL"); // } // file.WriteLine("Saml Token:\n"); // file.WriteLine(TokenToString(saml2Token)); //} #endregion public override ClaimsIdentityCollection ValidateToken(SecurityToken token) { CustomAdsTextTraceSource ts = new CustomAdsTextTraceSource("IdpAds.OnBehalfOfSaml2SecurityTokenHandler.ValidateToken", "AdsTraceSource", SourceLevels.Information); Saml2SecurityToken saml2Token = token as Saml2SecurityToken; if (saml2Token != null && saml2Token.Assertion != null) { // GFIPM S2S 8.8.2.5.a.v Saml2AuthenticationStatement authnStatement = null; foreach (Saml2Statement st in saml2Token.Assertion.Statements) { authnStatement = st as Saml2AuthenticationStatement; if (authnStatement != null) { break; } } if (authnStatement != null) { ts.TraceInformation("Assertion Age: " + DateTime.UtcNow.Subtract(authnStatement.AuthenticationInstant).TotalSeconds.ToString() + " seconds"); ts.TraceInformation("Max Assertion Age:" + TimeSpan.FromSeconds(Constants.AdsMaxAssertionAge).TotalHours.ToString() + " hours"); if (DateTime.UtcNow.Subtract(authnStatement.AuthenticationInstant) > TimeSpan.FromSeconds(Constants.AdsMaxAssertionAge)) { throw new SecurityTokenException("The authentication instant of the SAML2 assertion cannot be older than " + TimeSpan.FromSeconds(Constants.AdsMaxAssertionAge).TotalHours.ToString() + " hours"); } } else { ts.TraceInformation("authnStatement: NULL"); } // TODO: Refactor X509Certificate2 signingCert = Common.CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, WebConfigurationManager.AppSettings["SigningCertificateName"]); // TODO: What if the KeyIdentifierClause changes? // How to prevent recompiling? SecurityKeyIdentifier secKeyIdentifier = saml2Token.Assertion.SigningCredentials.SigningKeyIdentifier; SecurityKeyIdentifierClause keyIdentifierClause = secKeyIdentifier.Find<X509RawDataKeyIdentifierClause>(); if (keyIdentifierClause is X509RawDataKeyIdentifierClause) { //X509SubjectKeyIdentifierClause x509IdentClause = keyIdentifierClause as X509SubjectKeyIdentifierClause; X509RawDataKeyIdentifierClause x509IdentClause = keyIdentifierClause as X509RawDataKeyIdentifierClause; // GFIPM S2S 8.8.2.5.a.iv if (!x509IdentClause.Matches(signingCert)) { ts.TraceInformation("The OnBehalfOf SAML 2 assertion was not signed by this IDP"); throw new SecurityTokenException("The OnBehalfOf SAML 2 assertion was not signed by this IDP"); } } // Seems this is not needed! See note below #region Deprecate? if (saml2Token.Assertion.Subject != null && saml2Token.Assertion.Subject.SubjectConfirmations != null) { // first serialize before //SerializeTokenToStream(saml2Token, file, "Saml 2 Assertion Before Removing: "); ts.TraceInformation("Saml 2 Assertion: SubjectConfirmations"); // GFIPM S2S 8.8.2.6.b: Remove <SubjectConfirmationData> if present // TODO: Need to test Holder-Of-Key confirmation method to make sure this works. // TODO: This is actually not needed. When the new assertion is created, the // <SubjectConfirmationData> element is not created which is the same effect as deleting it. for (int i = 0; i < saml2Token.Assertion.Subject.SubjectConfirmations.Count; i++) { saml2Token.Assertion.Subject.SubjectConfirmations[i].SubjectConfirmationData = null; } } #endregion } return base.ValidateToken(token); }