Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.IdentityModel / System / IdentityModel / Tokens / SamlSecurityTokenHandler.cs
blob0fbf94a87c6f3b563250f16a1e72fbb44ba6398c
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.IdentityModel.Tokens
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
11 using System.IdentityModel.Configuration;
12 using System.IdentityModel.Diagnostics;
13 using System.IdentityModel.Protocols.WSTrust;
14 using System.IdentityModel.Selectors;
15 using System.IO;
16 using System.Linq;
17 using System.Runtime;
18 using System.Security.Claims;
19 using System.Security.Cryptography;
20 using System.Security.Principal;
21 using System.Text;
22 using System.Xml;
23 using System.Xml.Schema;
24 using Claim = System.Security.Claims.Claim;
25 using ClaimTypes = System.Security.Claims.ClaimTypes;
27 /// <summary>
28 /// This class implements a SecurityTokenHandler for a Saml11 token. It contains functionality for: Creating, Serializing and Validating
29 /// a Saml 11 Token.
30 /// </summary>
31 public class SamlSecurityTokenHandler : SecurityTokenHandler
33 #pragma warning disable 1591
34 public const string Namespace = "urn:oasis:names:tc:SAML:1.0";
35 public const string BearerConfirmationMethod = Namespace + ":cm:bearer";
36 public const string UnspecifiedAuthenticationMethod = Namespace + ":am:unspecified";
37 public const string Assertion = Namespace + ":assertion";
38 #pragma warning restore 1591
40 const string Attribute = "saml:Attribute";
41 const string Actor = "Actor";
42 const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
44 // Below are WCF DateTime values for Min and Max. SamlConditions when new'ed up will
45 // have these values as default. To maintin compatability with WCF behavior we will
46 // not write out SamlConditions NotBefore and NotOnOrAfter times which match the below
47 // values.
48 static DateTime WCFMinValue = new DateTime(DateTime.MinValue.Ticks + TimeSpan.TicksPerDay, DateTimeKind.Utc);
49 static DateTime WCFMaxValue = new DateTime(DateTime.MaxValue.Ticks - TimeSpan.TicksPerDay, DateTimeKind.Utc);
51 static string[] _tokenTypeIdentifiers = new string[] { SecurityTokenTypes.SamlTokenProfile11, SecurityTokenTypes.OasisWssSamlTokenProfile11 };
53 SamlSecurityTokenRequirement _samlSecurityTokenRequirement;
55 SecurityTokenSerializer _keyInfoSerializer;
57 object _syncObject = new object();
59 /// <summary>
60 /// Initializes an instance of <see cref="SamlSecurityTokenHandler"/>
61 /// </summary>
62 public SamlSecurityTokenHandler()
63 : this(new SamlSecurityTokenRequirement())
67 /// <summary>
68 /// Initializes an instance of <see cref="SamlSecurityTokenHandler"/>
69 /// </summary>
70 /// <param name="samlSecurityTokenRequirement">The SamlSecurityTokenRequirement to be used by the Saml11SecurityTokenHandler instance when validating tokens.</param>
71 public SamlSecurityTokenHandler(SamlSecurityTokenRequirement samlSecurityTokenRequirement)
73 if (samlSecurityTokenRequirement == null)
75 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSecurityTokenRequirement");
77 _samlSecurityTokenRequirement = samlSecurityTokenRequirement;
80 /// <summary>
81 /// Load custom configuration from Xml
82 /// </summary>
83 /// <param name="customConfigElements">Custom configuration that describes SamlSecurityTokenRequirement.</param>
84 /// <exception cref="ArgumentNullException">Input parameter 'customConfigElements' is null.</exception>
85 /// <exception cref="InvalidOperationException">Custom configuration specified was invalid.</exception>
86 public override void LoadCustomConfiguration(XmlNodeList customConfigElements)
88 if (customConfigElements == null)
90 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfigElements");
93 List<XmlElement> configNodes = XmlUtil.GetXmlElements(customConfigElements);
95 bool foundValidConfig = false;
97 foreach (XmlElement configElement in configNodes)
99 if (configElement.LocalName != ConfigurationStrings.SamlSecurityTokenRequirement)
101 continue;
104 if (foundValidConfig)
106 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7026, ConfigurationStrings.SamlSecurityTokenRequirement));
109 _samlSecurityTokenRequirement = new SamlSecurityTokenRequirement(configElement);
111 foundValidConfig = true;
114 if (!foundValidConfig)
116 _samlSecurityTokenRequirement = new SamlSecurityTokenRequirement();
120 #region TokenCreation
122 /// <summary>
123 /// Creates the security token based on the tokenDescriptor passed in.
124 /// </summary>
125 /// <param name="tokenDescriptor">The security token descriptor that contains the information to build a token.</param>
126 /// <exception cref="ArgumentNullException">Thrown if 'tokenDescriptor' is null.</exception>
127 public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor)
129 if (tokenDescriptor == null)
131 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
134 IEnumerable<SamlStatement> statements = CreateStatements(tokenDescriptor);
136 // - NotBefore / NotAfter
137 // - Audience Restriction
138 SamlConditions conditions = CreateConditions(tokenDescriptor.Lifetime, tokenDescriptor.AppliesToAddress, tokenDescriptor);
140 SamlAdvice advice = CreateAdvice(tokenDescriptor);
142 string issuerName = tokenDescriptor.TokenIssuerName;
144 SamlAssertion assertion = CreateAssertion(issuerName, conditions, advice, statements);
145 if (assertion == null)
147 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4013)));
150 assertion.SigningCredentials = GetSigningCredentials(tokenDescriptor);
152 SecurityToken token = new SamlSecurityToken(assertion);
155 // Encrypt the token if encrypting credentials are set
158 EncryptingCredentials encryptingCredentials = GetEncryptingCredentials(tokenDescriptor);
159 if (encryptingCredentials != null)
161 token = new EncryptedSecurityToken(token, encryptingCredentials);
164 return token;
167 /// <summary>
168 /// Gets the credentials for encrypting the token. Override this method to provide custom encrypting credentials.
169 /// </summary>
170 /// <param name="tokenDescriptor">The Scope property provides access to the encrypting credentials.</param>
171 /// <returns>The token encrypting credentials.</returns>
172 /// <exception cref="ArgumentNullException">Thrown when 'tokenDescriptor' is null.</exception>
173 /// <remarks>The default behavior is to return the SecurityTokenDescriptor.Scope.EncryptingCredentials
174 /// If this key is ----ymmetric, a symmetric key will be generated and wrapped with the asymmetric key.</remarks>
175 protected virtual EncryptingCredentials GetEncryptingCredentials(SecurityTokenDescriptor tokenDescriptor)
177 if (null == tokenDescriptor)
179 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
182 EncryptingCredentials encryptingCredentials = null;
184 if (null != tokenDescriptor.EncryptingCredentials)
186 encryptingCredentials = tokenDescriptor.EncryptingCredentials;
188 if (encryptingCredentials.SecurityKey is AsymmetricSecurityKey)
190 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
191 new SecurityTokenException(SR.GetString(SR.ID4178)));
195 return encryptingCredentials;
198 /// <summary>
199 /// Gets the credentials for the signing the assertion. Override this method to provide custom signing credentials.
200 /// </summary>
201 /// <param name="tokenDescriptor">The Scope property provides access to the signing credentials.</param>
202 /// <exception cref="ArgumentNullException">Thrown when 'tokenDescriptor' is null.</exception>
203 /// <returns>The assertion signing credentials.</returns>
204 /// <remarks>The default behavior is to return the SecurityTokenDescriptor.Scope.SigningCredentials.</remarks>
205 protected virtual SigningCredentials GetSigningCredentials(SecurityTokenDescriptor tokenDescriptor)
207 if (null == tokenDescriptor)
209 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
212 return tokenDescriptor.SigningCredentials;
215 /// <summary>
216 /// Override this method to provide a SamlAdvice to place in the Samltoken.
217 /// </summary>
218 /// <param name="tokenDescriptor">Contains informaiton about the token.</param>
219 /// <returns>SamlAdvice, default is null.</returns>
220 protected virtual SamlAdvice CreateAdvice(SecurityTokenDescriptor tokenDescriptor)
222 return null;
225 /// <summary>
226 /// Override this method to customize the parameters to create a SamlAssertion.
227 /// </summary>
228 /// <param name="issuer">The Issuer of the Assertion.</param>
229 /// <param name="conditions">The SamlConditions to add.</param>
230 /// <param name="advice">The SamlAdvice to add.</param>
231 /// <param name="statements">The SamlStatements to add.</param>
232 /// <returns>A SamlAssertion.</returns>
233 /// <remarks>A unique random id is created for the assertion
234 /// IssueInstance is set to DateTime.UtcNow.</remarks>
235 protected virtual SamlAssertion CreateAssertion(string issuer, SamlConditions conditions, SamlAdvice advice, IEnumerable<SamlStatement> statements)
237 return new SamlAssertion(System.IdentityModel.UniqueId.CreateRandomId(), issuer, DateTime.UtcNow, conditions, advice, statements);
240 /// <summary>
241 /// Creates the security token reference when the token is not attached to the message.
242 /// </summary>
243 /// <param name="token">The saml token.</param>
244 /// <param name="attached">Boolean that indicates if a attached or unattached
245 /// reference needs to be created.</param>
246 /// <returns>A SamlAssertionKeyIdentifierClause.</returns>
247 public override SecurityKeyIdentifierClause CreateSecurityTokenReference(SecurityToken token, bool attached)
249 if (token == null)
251 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
254 return token.CreateKeyIdentifierClause<SamlAssertionKeyIdentifierClause>();
257 /// <summary>
258 /// Generates all the conditions for saml
259 ///
260 /// 1. Lifetime condition
261 /// 2. AudienceRestriction condition
262 ///
263 /// </summary>
264 /// <param name="tokenLifetime">Lifetime of the Token.</param>
265 /// <param name="relyingPartyAddress">The endpoint address to who the token is created. The address
266 /// is modelled as an AudienceRestriction condition.</param>
267 /// <param name="tokenDescriptor">Contains all the other information that is used in token issuance.</param>
268 /// <returns>SamlConditions</returns>
269 protected virtual SamlConditions CreateConditions(Lifetime tokenLifetime, string relyingPartyAddress, SecurityTokenDescriptor tokenDescriptor)
271 SamlConditions conditions = new SamlConditions();
272 if (tokenLifetime != null)
274 if (tokenLifetime.Created != null)
276 conditions.NotBefore = tokenLifetime.Created.Value;
279 if (tokenLifetime.Expires != null)
281 conditions.NotOnOrAfter = tokenLifetime.Expires.Value;
285 if (!string.IsNullOrEmpty(relyingPartyAddress))
287 conditions.Conditions.Add(new SamlAudienceRestrictionCondition(new Uri[] { new Uri(relyingPartyAddress) }));
290 return conditions;
293 /// <summary>
294 /// Generates an enumeration of SamlStatements from a SecurityTokenDescriptor.
295 /// Only SamlAttributeStatements and SamlAuthenticationStatements are generated.
296 /// Overwrite this method to customize the creation of statements.
297 /// <para>
298 /// Calls in order (all are virtual):
299 /// 1. CreateSamlSubject
300 /// 2. CreateAttributeStatements
301 /// 3. CreateAuthenticationStatements
302 /// </para>
303 /// </summary>
304 /// <param name="tokenDescriptor">The SecurityTokenDescriptor to use to build the statements.</param>
305 /// <returns>An enumeration of SamlStatement.</returns>
306 protected virtual IEnumerable<SamlStatement> CreateStatements(SecurityTokenDescriptor tokenDescriptor)
308 Collection<SamlStatement> statements = new Collection<SamlStatement>();
310 SamlSubject subject = CreateSamlSubject(tokenDescriptor);
311 SamlAttributeStatement attributeStatement = CreateAttributeStatement(subject, tokenDescriptor.Subject, tokenDescriptor);
312 if (attributeStatement != null)
314 statements.Add(attributeStatement);
317 SamlAuthenticationStatement authnStatement = CreateAuthenticationStatement(subject, tokenDescriptor.AuthenticationInfo, tokenDescriptor);
318 if (authnStatement != null)
320 statements.Add(authnStatement);
323 return statements;
326 /// <summary>
327 /// Creates a SamlAuthenticationStatement for each AuthenticationInformation found in AuthenticationInformation.
328 /// Override this method to provide a custom implementation.
329 /// </summary>
330 /// <param name="samlSubject">The SamlSubject of the Statement.</param>
331 /// <param name="authInfo">AuthenticationInformation from which to generate the SAML Authentication statement.</param>
332 /// <param name="tokenDescriptor">Contains all the other information that is used in token issuance.</param>
333 /// <returns>SamlAuthenticationStatement</returns>
334 /// <exception cref="ArgumentNullException">Thrown when 'samlSubject' or 'authInfo' is null.</exception>
335 protected virtual SamlAuthenticationStatement CreateAuthenticationStatement(
336 SamlSubject samlSubject,
337 AuthenticationInformation authInfo,
338 SecurityTokenDescriptor tokenDescriptor)
340 if (samlSubject == null)
342 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSubject");
345 if (tokenDescriptor == null)
347 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
350 if (tokenDescriptor.Subject == null)
352 return null;
354 string authenticationMethod = null;
355 string authenticationInstant = null;
357 // Search for an Authentication Claim.
358 IEnumerable<Claim> claimCollection = (from c in tokenDescriptor.Subject.Claims
359 where c.Type == ClaimTypes.AuthenticationMethod
360 select c);
361 if (claimCollection.Count<Claim>() > 0)
363 // We support only one authentication statement and hence we just pick the first authentication type
364 // claim found in the claim collection. Since the spec allows multiple Auth Statements
365 // we do not throw an error.
366 authenticationMethod = claimCollection.First<Claim>().Value;
369 claimCollection = (from c in tokenDescriptor.Subject.Claims
370 where c.Type == ClaimTypes.AuthenticationInstant
371 select c);
372 if (claimCollection.Count<Claim>() > 0)
374 authenticationInstant = claimCollection.First<Claim>().Value;
377 if (authenticationMethod == null && authenticationInstant == null)
379 return null;
381 else if (authenticationMethod == null)
383 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationMethod", "SAML11"));
385 else if (authenticationInstant == null)
387 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationInstant", "SAML11"));
390 DateTime authInstantTime = DateTime.ParseExact(authenticationInstant,
391 DateTimeFormats.Accepted,
392 DateTimeFormatInfo.InvariantInfo,
393 DateTimeStyles.None).ToUniversalTime();
394 if (authInfo == null)
396 return new SamlAuthenticationStatement(samlSubject, DenormalizeAuthenticationType(authenticationMethod), authInstantTime, null, null, null);
398 else
400 return new SamlAuthenticationStatement(samlSubject, DenormalizeAuthenticationType(authenticationMethod), authInstantTime, authInfo.DnsName, authInfo.Address, null);
404 /// <summary>
405 /// Creates SamlAttributeStatements and adds them to a collection.
406 /// Override this method to provide a custom implementation.
407 /// <para>
408 /// Default behavior is to create a new SamlAttributeStatement for each Subject in the tokenDescriptor.Subjects collection.
409 /// </para>
410 /// </summary>
411 /// <param name="samlSubject">The SamlSubject to use in the SamlAttributeStatement that are created.</param>
412 /// <param name="subject">The ClaimsIdentity that contains claims which will be converted to SAML Attributes.</param>
413 /// <param name="tokenDescriptor">Contains all the other information that is used in token issuance.</param>
414 /// <returns>SamlAttributeStatement</returns>
415 /// <exception cref="ArgumentNullException">Thrown when 'samlSubject' is null.</exception>
416 protected virtual SamlAttributeStatement CreateAttributeStatement(
417 SamlSubject samlSubject,
418 ClaimsIdentity subject,
419 SecurityTokenDescriptor tokenDescriptor)
421 if (subject == null)
423 return null;
426 if (samlSubject == null)
428 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSubject");
431 if (subject.Claims != null)
434 List<SamlAttribute> attributes = new List<SamlAttribute>();
435 foreach (Claim claim in subject.Claims)
437 if (claim != null && claim.Type != ClaimTypes.NameIdentifier)
440 // NameIdentifier claim is already processed while creating the samlsubject
441 // AuthenticationInstant and AuthenticationType are not converted to Claims
443 switch (claim.Type)
445 case ClaimTypes.AuthenticationInstant:
446 case ClaimTypes.AuthenticationMethod:
447 break;
448 default:
449 attributes.Add(CreateAttribute(claim, tokenDescriptor));
450 break;
455 AddDelegateToAttributes(subject, attributes, tokenDescriptor);
457 ICollection<SamlAttribute> collectedAttributes = CollectAttributeValues(attributes);
458 if (collectedAttributes.Count > 0)
460 return new SamlAttributeStatement(samlSubject, collectedAttributes);
464 return null;
467 /// <summary>
468 /// Collects attributes with a common claim type, claim value type, and original issuer into a
469 /// single attribute with multiple values.
470 /// </summary>
471 /// <param name="attributes">List of attributes generated from claims.</param>
472 /// <returns>List of attribute values with common attributes collected into value lists.</returns>
473 protected virtual ICollection<SamlAttribute> CollectAttributeValues(ICollection<SamlAttribute> attributes)
475 Dictionary<SamlAttributeKeyComparer.AttributeKey, SamlAttribute> distinctAttributes = new Dictionary<SamlAttributeKeyComparer.AttributeKey, SamlAttribute>(attributes.Count, new SamlAttributeKeyComparer());
477 foreach (SamlAttribute attribute in attributes)
479 SamlAttribute SamlAttribute = attribute as SamlAttribute;
480 if (SamlAttribute != null)
482 // Use unique attribute if name, value type, or issuer differ
483 SamlAttributeKeyComparer.AttributeKey attributeKey = new SamlAttributeKeyComparer.AttributeKey(SamlAttribute);
485 if (distinctAttributes.ContainsKey(attributeKey))
487 foreach (string attributeValue in SamlAttribute.AttributeValues)
489 distinctAttributes[attributeKey].AttributeValues.Add(attributeValue);
492 else
494 distinctAttributes.Add(attributeKey, SamlAttribute);
499 return distinctAttributes.Values;
502 /// <summary>
503 /// Adds all the delegates associated with the ActAs subject into the attribute collection.
504 /// </summary>
505 /// <param name="subject">The delegate of this ClaimsIdentity will be serialized into a SamlAttribute.</param>
506 /// <param name="attributes">Attribute collection to which the ActAs token will be serialized.</param>
507 /// <param name="tokenDescriptor">Contains all the information that is used in token issuance.</param>
508 protected virtual void AddDelegateToAttributes(
509 ClaimsIdentity subject,
510 ICollection<SamlAttribute> attributes,
511 SecurityTokenDescriptor tokenDescriptor)
513 if (subject == null)
515 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
517 if (tokenDescriptor == null)
519 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
521 if (subject.Actor == null)
523 return;
526 List<SamlAttribute> actingAsAttributes = new List<SamlAttribute>();
528 foreach (Claim claim in subject.Actor.Claims)
530 if (claim != null)
532 actingAsAttributes.Add(CreateAttribute(claim, tokenDescriptor));
536 // perform depth first recursion
537 AddDelegateToAttributes(subject.Actor, actingAsAttributes, tokenDescriptor);
539 ICollection<SamlAttribute> collectedAttributes = CollectAttributeValues(actingAsAttributes);
540 attributes.Add(CreateAttribute(new Claim(ClaimTypes.Actor, CreateXmlStringFromAttributes(collectedAttributes), ClaimValueTypes.String), tokenDescriptor));
543 /// <summary>
544 /// Returns the SamlSubject to use for all the statements that will be created.
545 /// Overwrite this method to customize the creation of the SamlSubject.
546 /// </summary>
547 /// <param name="tokenDescriptor">Contains all the information that is used in token issuance.</param>
548 /// <returns>A SamlSubject created from the first subject found in the tokenDescriptor as follows:
549 /// <para>
550 /// 1. Claim of Type NameIdentifier is searched. If found, SamlSubject.Name is set to claim.Value.
551 /// 2. If a non-null tokenDescriptor.proof is found then SamlSubject.KeyIdentifier = tokenDescriptor.Proof.KeyIdentifier AND SamlSubject.ConfirmationMethod is set to 'HolderOfKey'.
552 /// 3. If a null tokenDescriptor.proof is found then SamlSubject.ConfirmationMethod is set to 'BearerKey'.
553 /// </para>
554 /// </returns>
555 /// <exception cref="ArgumentNullException">Thrown when 'tokenDescriptor' is null.</exception>
556 protected virtual SamlSubject CreateSamlSubject(SecurityTokenDescriptor tokenDescriptor)
558 if (tokenDescriptor == null)
560 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
563 SamlSubject samlSubject = new SamlSubject();
565 Claim identityClaim = null;
566 if (tokenDescriptor.Subject != null && tokenDescriptor.Subject.Claims != null)
568 foreach (Claim claim in tokenDescriptor.Subject.Claims)
570 if (claim.Type == ClaimTypes.NameIdentifier)
572 // Do not allow multiple name identifier claim.
573 if (null != identityClaim)
575 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
576 new InvalidOperationException(SR.GetString(SR.ID4139)));
578 identityClaim = claim;
583 if (identityClaim != null)
585 samlSubject.Name = identityClaim.Value;
587 if (identityClaim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierFormat))
589 samlSubject.NameFormat = identityClaim.Properties[ClaimProperties.SamlNameIdentifierFormat];
592 if (identityClaim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierNameQualifier))
594 samlSubject.NameQualifier = identityClaim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier];
598 if (tokenDescriptor.Proof != null)
601 // Add the key and the Holder-Of-Key confirmation method
602 // for both symmetric and asymmetric key case
604 samlSubject.KeyIdentifier = tokenDescriptor.Proof.KeyIdentifier;
605 samlSubject.ConfirmationMethods.Add(SamlConstants.HolderOfKey);
607 else
610 // This is a bearer token
612 samlSubject.ConfirmationMethods.Add(BearerConfirmationMethod);
615 return samlSubject;
618 /// <summary>
619 /// Builds an XML formated string from a collection of saml attributes that represend the Actor.
620 /// </summary>
621 /// <param name="attributes">An enumeration of Saml Attributes.</param>
622 /// <returns>A well formed XML string.</returns>
623 /// <remarks>The string is of the form "&lt;Actor&gt;&lt;SamlAttribute name, ns&gt;&lt;SamlAttributeValue&gt;...&lt;/SamlAttributeValue&gt;, ...&lt;/SamlAttribute&gt;...&lt;/Actor&gt;"</remarks>
624 protected virtual string CreateXmlStringFromAttributes(IEnumerable<SamlAttribute> attributes)
626 bool actorElementWritten = false;
628 using (MemoryStream ms = new MemoryStream())
630 using (XmlDictionaryWriter dicWriter = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8, false))
632 foreach (SamlAttribute samlAttribute in attributes)
634 if (samlAttribute != null)
636 if (!actorElementWritten)
638 dicWriter.WriteStartElement(Actor);
639 actorElementWritten = true;
641 WriteAttribute(dicWriter, samlAttribute);
645 if (actorElementWritten)
647 dicWriter.WriteEndElement();
650 dicWriter.Flush();
652 return Encoding.UTF8.GetString(ms.ToArray());
656 /// <summary>
657 /// Generates a SamlAttribute from a claim.
658 /// </summary>
659 /// <param name="claim">Claim from which to generate a SamlAttribute.</param>
660 /// <param name="tokenDescriptor">Contains all the information that is used in token issuance.</param>
661 /// <returns>The SamlAttribute.</returns>
662 /// <exception cref="ArgumentNullException">The parameter 'claim' is null.</exception>
663 protected virtual SamlAttribute CreateAttribute(Claim claim, SecurityTokenDescriptor tokenDescriptor)
665 if (claim == null)
667 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim");
670 int lastSlashIndex = claim.Type.LastIndexOf('/');
671 string attributeNamespace = null;
672 string attributeName = null;
674 if ((lastSlashIndex == 0) || (lastSlashIndex == -1))
676 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.GetString(SR.ID4216, claim.Type));
678 else if (lastSlashIndex == claim.Type.Length - 1)
680 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.GetString(SR.ID4216, claim.Type));
682 else
684 attributeNamespace = claim.Type.Substring(0, lastSlashIndex);
686 // The WCF SamlAttribute requires that the attributeNamespace and attributeName are both non-null and non-empty.
687 // Furthermore, on deserialization / construction it considers the claimType associated with the SamlAttribute to be attributeNamespace + "/" + attributeName.
689 // IDFX extends the WCF SamlAttribute and hence has to work with an attributeNamespace and attributeName that are both non-null and non-empty.
690 // On serialization, we identify the last slash in the claimtype, and treat everything before the slash as the attributeNamespace and everything after the slash as the attributeName.
691 // On deserialization, we don't always insert a "/" between the attributeNamespace and attributeName (like WCF does); we only do so if the attributeNamespace doesn't have a trailing slash.
693 // Send Receive Behavior
694 // =============================
695 // WCF WCF Works as expected
697 // WCF IDFX In the common case (http://www.claimtypes.com/foo), WCF will not send a trailing slash in the attributeNamespace. IDFX will add one upon deserialization.
698 // In the edge case (http://www.claimtypes.com//foo), WCF will send a trailing slash in the attributeNamespace. IDFX will not add one upon deserialization.
700 // IDFX WCF In the common case (http://www.claimtypes.com/foo), IDFX will not send a trailing slash. WCF will add one upon deserialization.
701 // In the edge case (http://www.claimtypes.com//foo), IDFX will throw (which is what the fix for FIP 6301 is about).
703 // IDFX IDFX Works as expected
705 if (attributeNamespace.EndsWith("/", StringComparison.Ordinal))
707 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claim", SR.GetString(SR.ID4213, claim.Type));
709 attributeName = claim.Type.Substring(lastSlashIndex + 1, claim.Type.Length - (lastSlashIndex + 1));
712 SamlAttribute attribute = new SamlAttribute(attributeNamespace, attributeName, new string[] { claim.Value });
713 if (!StringComparer.Ordinal.Equals(ClaimsIdentity.DefaultIssuer, claim.OriginalIssuer))
715 attribute.OriginalIssuer = claim.OriginalIssuer;
717 attribute.AttributeValueXsiType = claim.ValueType;
719 return attribute;
722 #endregion
724 #region TokenValidation
726 /// <summary>
727 /// Returns value indicates if this handler can validate tokens of type
728 /// SamlSecurityToken.
729 /// </summary>
730 public override bool CanValidateToken
732 get { return true; }
735 /// <summary>
736 /// Gets or sets the X509CeritificateValidator that is used by the current instance.
737 /// </summary>
738 public X509CertificateValidator CertificateValidator
742 if (_samlSecurityTokenRequirement.CertificateValidator == null)
744 if (Configuration != null)
746 return Configuration.CertificateValidator;
748 else
750 return null;
753 else
755 return _samlSecurityTokenRequirement.CertificateValidator;
760 _samlSecurityTokenRequirement.CertificateValidator = value;
764 /// <summary>
765 /// Throws if a token is detected as being replayed. If the token is not found it is added to the <see cref="TokenReplayCache" />.
766 /// </summary>
767 /// <exception cref="ArgumentNullException">The input argument 'token' is null.</exception>
768 /// <exception cref="InvalidOperationException">Configuration or Configuration.TokenReplayCache property is null.</exception>
769 /// <exception cref="ArgumentException">The input argument 'token' is not a SamlSecurityToken.</exception>
770 /// <exception cref="SecurityTokenValidationException">SamlSecurityToken.Assertion.Id is null or empty.</exception>
771 /// <exception cref="SecurityTokenReplayDetectedException">If the token is found in the <see cref="TokenReplayCache" />.</exception>
772 /// <remarks>The default behavior is to only check tokens bearer tokens (tokens that do not have keys).</remarks>
773 protected override void DetectReplayedToken(SecurityToken token)
775 if (token == null)
777 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
780 SamlSecurityToken samlToken = token as SamlSecurityToken;
781 if (null == samlToken)
783 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1067, token.GetType().ToString()));
787 // by default we only check bearer tokens.
790 if (samlToken.SecurityKeys.Count != 0)
792 return;
795 if (Configuration == null)
797 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
800 if (Configuration.Caches.TokenReplayCache == null)
802 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4278));
805 if (string.IsNullOrEmpty(samlToken.Assertion.AssertionId))
807 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID1063)));
810 StringBuilder stringBuilder = new StringBuilder();
812 string key;
814 using (HashAlgorithm hashAlgorithm = CryptoHelper.NewSha256HashAlgorithm())
816 if (string.IsNullOrEmpty(samlToken.Assertion.Issuer))
818 stringBuilder.AppendFormat("{0}{1}", samlToken.Assertion.AssertionId, _tokenTypeIdentifiers[0]);
820 else
822 stringBuilder.AppendFormat("{0}{1}{2}", samlToken.Assertion.AssertionId, samlToken.Assertion.Issuer, _tokenTypeIdentifiers[0]);
825 key = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString())));
828 if (Configuration.Caches.TokenReplayCache.Contains(key))
830 if (string.IsNullOrEmpty(samlToken.Assertion.Issuer))
832 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
833 new SecurityTokenReplayDetectedException(SR.GetString(SR.ID1062, typeof(SamlSecurityToken).ToString(), samlToken.Assertion.AssertionId, "")));
835 else
837 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
838 new SecurityTokenReplayDetectedException(SR.GetString(SR.ID1062, typeof(SamlSecurityToken).ToString(), samlToken.Assertion.AssertionId, samlToken.Assertion.Issuer)));
841 else
843 Configuration.Caches.TokenReplayCache.AddOrUpdate(key, token, DateTimeUtil.Add(GetTokenReplayCacheEntryExpirationTime(samlToken), Configuration.MaxClockSkew));
847 /// <summary>
848 /// Returns the time until which the token should be held in the token replay cache.
849 /// </summary>
850 /// <param name="token">The token to return an expiration time for.</param>
851 /// <exception cref="ArgumentNullException">The input argument 'token' is null.</exception>
852 /// <exception cref="SecurityTokenValidationException">The SamlSecurityToken's validity period is greater than the expiration period set to TokenReplayCache.</exception>
853 /// <returns>A DateTime representing the expiration time.</returns>
854 protected virtual DateTime GetTokenReplayCacheEntryExpirationTime(SamlSecurityToken token)
856 if (token == null)
858 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
862 // DateTimeUtil handles overflows
864 DateTime maximumExpirationTime = DateTimeUtil.Add(DateTime.UtcNow, Configuration.TokenReplayCacheExpirationPeriod);
866 // If the token validity period is greater than the TokenReplayCacheExpirationPeriod, throw
867 if (DateTime.Compare(maximumExpirationTime, token.ValidTo) < 0)
869 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
870 new SecurityTokenValidationException(SR.GetString(SR.ID1069, token.ValidTo.ToString(), Configuration.TokenReplayCacheExpirationPeriod.ToString())));
873 return token.ValidTo;
876 /// <summary>
877 /// Rejects tokens that are not valid.
878 /// </summary>
879 /// <remarks>
880 /// The token may be invalid for a number of reasons. For example, the
881 /// current time may not be within the token's validity period, the
882 /// token may contain invalid or contradictory data, or the token
883 /// may contain unsupported SAML elements.
884 /// </remarks>
885 /// <param name="conditions">SAML condition to be validated.</param>
886 /// <param name="enforceAudienceRestriction">True to check for Audience Restriction condition.</param>
887 protected virtual void ValidateConditions(SamlConditions conditions, bool enforceAudienceRestriction)
889 if (null != conditions)
891 DateTime now = DateTime.UtcNow;
893 if (null != conditions.NotBefore
894 && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < conditions.NotBefore)
896 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
897 new SecurityTokenNotYetValidException(SR.GetString(SR.ID4222, conditions.NotBefore, now)));
900 if (null != conditions.NotOnOrAfter
901 && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= conditions.NotOnOrAfter)
903 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
904 new SecurityTokenExpiredException(SR.GetString(SR.ID4223, conditions.NotOnOrAfter, now)));
909 // Enforce the audience restriction
911 if (enforceAudienceRestriction)
913 if (this.Configuration == null || this.Configuration.AudienceRestriction.AllowedAudienceUris.Count == 0)
915 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID1032)));
919 // Process each condition, enforcing the AudienceRestrictionConditions
921 bool foundAudienceRestriction = false;
923 if (null != conditions && null != conditions.Conditions)
925 foreach (SamlCondition condition in conditions.Conditions)
927 SamlAudienceRestrictionCondition audienceRestriction = condition as SamlAudienceRestrictionCondition;
928 if (null == audienceRestriction)
930 // Skip other conditions
931 continue;
934 _samlSecurityTokenRequirement.ValidateAudienceRestriction(this.Configuration.AudienceRestriction.AllowedAudienceUris, audienceRestriction.Audiences);
935 foundAudienceRestriction = true;
939 if (!foundAudienceRestriction)
941 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException(SR.GetString(SR.ID1035)));
946 /// <summary>
947 /// Validates a <see cref="SamlSecurityToken"/>.
948 /// </summary>
949 /// <param name="token">The <see cref="SamlSecurityToken"/> to validate.</param>
950 /// <returns>The <see cref="ReadOnlyCollection{T}"/> of <see cref="ClaimsIdentity"/> representing the identities contained in the token.</returns>
951 /// <exception cref="ArgumentNullException">The parameter 'token' is null.</exception>
952 /// <exception cref="ArgumentException">The token is not assignable from <see cref="SamlSecurityToken"/>.</exception>
953 /// <exception cref="InvalidOperationException">Configuration <see cref="SecurityTokenHandlerConfiguration"/>is null.</exception>
954 /// <exception cref="ArgumentException">SamlSecurityToken.Assertion is null.</exception>
955 /// <exception cref="SecurityTokenValidationException">Thrown if SamlSecurityToken.Assertion.SigningToken is null.</exception>
956 /// <exception cref="SecurityTokenValidationException">Thrown if the certificate associated with the token issuer does not pass validation.</exception>
957 public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
959 if (token == null)
961 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
964 SamlSecurityToken samlToken = token as SamlSecurityToken;
965 if (samlToken == null)
967 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1033, token.GetType().ToString()));
970 if (this.Configuration == null)
972 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
977 if (samlToken.Assertion == null)
979 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1034));
982 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.Diagnostics, SR.GetString(SR.TraceValidateToken), new SecurityTraceRecordHelper.TokenTraceRecord(token), null, null);
984 // Ensure token was signed and verified at some point
985 if (samlToken.Assertion.SigningToken == null)
987 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4220)));
990 this.ValidateConditions(samlToken.Assertion.Conditions, _samlSecurityTokenRequirement.ShouldEnforceAudienceRestriction(this.Configuration.AudienceRestriction.AudienceMode, samlToken));
992 // We need something like AudienceUriMode and have a setting on Configuration to allow extensibility and custom settings
993 // By default we only check bearer tokens
994 if (this.Configuration.DetectReplayedTokens)
996 this.DetectReplayedToken(samlToken);
1000 // If the backing token is x509, validate trust
1002 X509SecurityToken x509IssuerToken = samlToken.Assertion.SigningToken as X509SecurityToken;
1003 if (x509IssuerToken != null)
1007 CertificateValidator.Validate(x509IssuerToken.Certificate);
1009 catch (SecurityTokenValidationException e)
1011 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4257,
1012 X509Util.GetCertificateId(x509IssuerToken.Certificate)), e));
1017 // Create the claims
1019 ClaimsIdentity claimsIdentity = CreateClaims(samlToken);
1021 if (_samlSecurityTokenRequirement.MapToWindows)
1023 // TFS: 153865, Microsoft WindowsIdentity does not set Authtype. I don't think that authtype should be set here anyway.
1024 // The authtype will be S4U (kerberos) it doesn't really matter that the upn arrived in a SAML token.
1025 WindowsIdentity windowsIdentity = CreateWindowsIdentity(FindUpn(claimsIdentity));
1027 // PARTIAL TRUST: will fail when adding claims, AddClaims is SecurityCritical.
1028 windowsIdentity.AddClaims(claimsIdentity.Claims);
1029 claimsIdentity = windowsIdentity;
1032 if (this.Configuration.SaveBootstrapContext)
1034 claimsIdentity.BootstrapContext = new BootstrapContext(token, this);
1037 this.TraceTokenValidationSuccess(token);
1039 List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1);
1040 identities.Add(claimsIdentity);
1041 return identities.AsReadOnly();
1043 catch (Exception e)
1045 if (Fx.IsFatal(e))
1047 throw;
1050 this.TraceTokenValidationFailure(token, e.Message);
1051 throw e;
1055 /// <summary>
1056 /// Creates a <see cref="WindowsIdentity"/> object using the <paramref name="upn"/> value.
1057 /// </summary>
1058 /// <param name="upn">The upn name.</param>
1059 /// <returns>A <see cref="WindowsIdentity"/> object.</returns>
1060 /// <exception cref="ArgumentException">If <paramref name="upn"/> is null or empty.</exception>
1061 protected virtual WindowsIdentity CreateWindowsIdentity(string upn)
1063 if (string.IsNullOrEmpty(upn))
1065 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("upn");
1068 WindowsIdentity wi = new WindowsIdentity(upn);
1070 return new WindowsIdentity(wi.Token, AuthenticationTypes.Federation, WindowsAccountType.Normal, true);
1073 /// <summary>
1074 /// Finds the UPN claim value in the provided <see cref="ClaimsIdentity" /> object for the purpose
1075 /// of mapping the identity to a <see cref="WindowsIdentity" /> object.
1076 /// </summary>
1077 /// <param name="claimsIdentity">The claims identity object containing the desired UPN claim.</param>
1078 /// <returns>The UPN claim value found.</returns>
1079 /// <exception cref="InvalidOperationException">If more than one UPN claim is contained in
1080 /// <paramref name="claimsIdentity"/></exception>
1081 protected virtual string FindUpn(ClaimsIdentity claimsIdentity)
1083 return ClaimsHelper.FindUpn(claimsIdentity);
1086 /// <summary>
1087 /// Generates SubjectCollection that represents a SamlToken.
1088 /// Only SamlAttributeStatements processed.
1089 /// Overwrite this method to customize the creation of statements.
1090 /// <para>
1091 /// Calls:
1092 /// 1. ProcessAttributeStatement for SamlAttributeStatements.
1093 /// 2. ProcessAuthenticationStatement for SamlAuthenticationStatements.
1094 /// 3. ProcessAuthorizationDecisionStatement for SamlAuthorizationDecisionStatements.
1095 /// 4. ProcessCustomStatement for other SamlStatements.
1096 /// </para>
1097 /// </summary>
1098 /// <param name="samlSecurityToken">The token used to generate the SubjectCollection.</param>
1099 /// <returns>ClaimsIdentity representing the subject of the SamlToken.</returns>
1100 /// <exception cref="ArgumentNullException">Thrown if 'samlSecurityToken' is null.</exception>
1101 protected virtual ClaimsIdentity CreateClaims(SamlSecurityToken samlSecurityToken)
1103 if (samlSecurityToken == null)
1105 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSecurityToken");
1108 if (samlSecurityToken.Assertion == null)
1110 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("samlSecurityToken", SR.GetString(SR.ID1034));
1114 // Construct the subject and issuer identities.
1115 // Use claim types specified in the security token requirements used for IPrincipal.Role and IIdentity.Name
1117 ClaimsIdentity subject = new ClaimsIdentity(AuthenticationTypes.Federation,
1118 _samlSecurityTokenRequirement.NameClaimType,
1119 _samlSecurityTokenRequirement.RoleClaimType);
1121 string issuer = null;
1123 if (this.Configuration == null)
1125 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1128 if (this.Configuration.IssuerNameRegistry == null)
1130 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4277));
1133 // SamlAssertion. The SigningToken may or may not be null.
1134 // The default IssuerNameRegistry will throw if null.
1135 // This callout is provided for extensibility scenarios with custom IssuerNameRegistry.
1136 issuer = this.Configuration.IssuerNameRegistry.GetIssuerName(samlSecurityToken.Assertion.SigningToken, samlSecurityToken.Assertion.Issuer);
1138 if (string.IsNullOrEmpty(issuer))
1140 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4175)));
1143 ProcessStatement(samlSecurityToken.Assertion.Statements, subject, issuer);
1144 return subject;
1147 /// <summary>
1148 /// Returns the Saml11 AuthenticationMethod matching a normalized value.
1149 /// </summary>
1150 /// <param name="normalizedAuthenticationType">Normalized value.</param>
1151 /// <returns><see cref="SamlConstants.AuthenticationMethods"/></returns>
1152 protected virtual string DenormalizeAuthenticationType(string normalizedAuthenticationType)
1154 return AuthenticationTypeMaps.Denormalize(normalizedAuthenticationType, AuthenticationTypeMaps.Saml);
1157 /// <summary>
1158 /// Returns the normalized value matching a Saml11 AuthenticationMethod.
1159 /// </summary>
1160 /// <param name="saml11AuthenticationMethod"><see cref="SamlConstants.AuthenticationMethods"/></param>
1161 /// <returns>Normalized value.</returns>
1162 protected virtual string NormalizeAuthenticationType(string saml11AuthenticationMethod)
1164 return AuthenticationTypeMaps.Normalize(saml11AuthenticationMethod, AuthenticationTypeMaps.Saml);
1167 /// <summary>
1168 /// Processes all statements to generate claims.
1169 /// </summary>
1170 /// <param name="statements">A collection of Saml2Statement.</param>
1171 /// <param name="subject">The subject.</param>
1172 /// <param name="issuer">The issuer.</param>
1173 protected virtual void ProcessStatement(IList<SamlStatement> statements, ClaimsIdentity subject, string issuer)
1175 if (statements == null)
1177 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statements");
1180 Collection<SamlAuthenticationStatement> authStatementCollection = new Collection<SamlAuthenticationStatement>();
1183 // Validate that the Saml subjects in all the statements are the same.
1185 ValidateStatements(statements);
1187 foreach (SamlStatement samlStatement in statements)
1189 SamlAttributeStatement attrStatement = samlStatement as SamlAttributeStatement;
1190 if (attrStatement != null)
1192 ProcessAttributeStatement(attrStatement, subject, issuer);
1194 else
1196 SamlAuthenticationStatement authenStatement = samlStatement as SamlAuthenticationStatement;
1197 if (authenStatement != null)
1199 authStatementCollection.Add(authenStatement);
1201 else
1203 SamlAuthorizationDecisionStatement decisionStatement = samlStatement as SamlAuthorizationDecisionStatement;
1204 if (decisionStatement != null)
1206 ProcessAuthorizationDecisionStatement(decisionStatement, subject, issuer);
1208 else
1210 // We don't process custom statements. Just fall through.
1216 // Processing Authentication statement(s) should be done at the last phase to add the authentication
1217 // information as claims to the ClaimsIdentity
1218 foreach (SamlAuthenticationStatement authStatement in authStatementCollection)
1220 if (authStatement != null)
1222 ProcessAuthenticationStatement(authStatement, subject, issuer);
1227 /// <summary>
1228 /// Override this virtual to provide custom processing of SamlAttributeStatements.
1229 /// </summary>
1230 /// <param name="samlStatement">The SamlAttributeStatement to process.</param>
1231 /// <param name="subject">The identity that should be modified to reflect the statement.</param>
1232 /// <param name="issuer">The subject that identifies the issuer.</param>
1233 /// <exception cref="ArgumentNullException">The input parameter 'samlStatement' or 'subject' is null.</exception>
1234 protected virtual void ProcessAttributeStatement(SamlAttributeStatement samlStatement, ClaimsIdentity subject, string issuer)
1236 if (samlStatement == null)
1238 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlStatement");
1241 if (subject == null)
1243 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1246 // We will be adding the nameid claim only once for multiple attribute and/or authn statements.
1247 // As of now, we put the nameId claim both inside the saml subject and the saml attribute statement as assertion.
1248 // When generating claims, we will only pick up the saml subject of a saml statement, not the attribute statement value.
1249 ProcessSamlSubject(samlStatement.SamlSubject, subject, issuer);
1251 foreach (SamlAttribute attr in samlStatement.Attributes)
1253 string claimType = null;
1254 if (string.IsNullOrEmpty(attr.Namespace))
1256 claimType = attr.Name;
1258 else if (StringComparer.Ordinal.Equals(attr.Name, SamlConstants.ElementNames.NameIdentifier))
1260 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID4094)));
1262 else
1264 // check if Namespace end with slash don't add it
1265 // If no slash or last char is not a slash, add it.
1266 int lastSlashIndex = attr.Namespace.LastIndexOf('/');
1267 if ((lastSlashIndex == -1) || (!(lastSlashIndex == attr.Namespace.Length - 1)))
1269 claimType = attr.Namespace + "/" + attr.Name;
1271 else
1273 claimType = attr.Namespace + attr.Name;
1278 if (claimType == ClaimTypes.Actor)
1280 if (subject.Actor != null)
1282 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4034));
1285 SetDelegateFromAttribute(attr, subject, issuer);
1287 else
1289 for (int k = 0; k < attr.AttributeValues.Count; ++k)
1291 // Check if we already have a nameId claim.
1292 if (StringComparer.Ordinal.Equals(ClaimTypes.NameIdentifier, claimType) && GetClaim(subject, ClaimTypes.NameIdentifier) != null)
1294 continue;
1296 string originalIssuer = issuer;
1297 SamlAttribute SamlAttribute = attr as SamlAttribute;
1298 if ((SamlAttribute != null) && (SamlAttribute.OriginalIssuer != null))
1300 originalIssuer = SamlAttribute.OriginalIssuer;
1302 string claimValueType = ClaimValueTypes.String;
1303 if (SamlAttribute != null)
1305 claimValueType = SamlAttribute.AttributeValueXsiType;
1307 subject.AddClaim(new Claim(claimType, attr.AttributeValues[k], claimValueType, issuer, originalIssuer));
1314 /// <summary>
1315 /// Gets a specific claim of type claimType from the subject's claims collection.
1316 /// </summary>
1317 /// <param name="subject">The subject.</param>
1318 /// <param name="claimType">The type of the claim.</param>
1319 /// <returns>The claim of type claimType if present, else null.</returns>
1320 private static Claim GetClaim(ClaimsIdentity subject, string claimType)
1322 foreach (Claim claim in subject.Claims)
1324 if (StringComparer.Ordinal.Equals(claimType, claim.Type))
1326 return claim;
1329 return null;
1332 /// <summary>
1333 /// For each saml statement (attribute/authentication/authz/custom), we will check if we need to create
1334 /// a nameid claim or a key identifier claim out of its SamlSubject.
1335 /// </summary>
1336 /// <remarks>
1337 /// To make sure that the saml subject within each saml statement are the same, this method does the following comparisons.
1338 /// 1. All the saml subjects' contents are the same.
1339 /// 2. The name identifiers (if present) are the same. The name identifier comparison is done for the name identifier value,
1340 /// name identifier format (if present), and name identifier qualifier (if present).
1341 /// 3. The key identifiers (if present) are the same.
1342 /// </remarks>
1343 /// <param name="samlSubject">The SamlSubject to extract claims from.</param>
1344 /// <param name="subject">The identity that should be modified to reflect the SamlSubject.</param>
1345 /// <param name="issuer">The Issuer claims of the SAML token.</param>
1346 /// <exception cref="ArgumentNullException">The parameter 'samlSubject' is null.</exception>
1347 protected virtual void ProcessSamlSubject(SamlSubject samlSubject, ClaimsIdentity subject, string issuer)
1349 if (samlSubject == null)
1351 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSubject");
1354 Claim nameIdentifierClaim = GetClaim(subject, ClaimTypes.NameIdentifier);
1356 if (nameIdentifierClaim == null)
1358 // first saml subject. so we will create claims for this subject.
1359 // subsequent subjects must have the same content.
1361 // add name identifier claim if present.
1362 if (!string.IsNullOrEmpty(samlSubject.Name))
1364 Claim claim = new Claim(ClaimTypes.NameIdentifier, samlSubject.Name, ClaimValueTypes.String, issuer);
1366 if (samlSubject.NameFormat != null)
1368 claim.Properties[ClaimProperties.SamlNameIdentifierFormat] = samlSubject.NameFormat;
1371 if (samlSubject.NameQualifier != null)
1373 claim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier] = samlSubject.NameQualifier;
1376 subject.AddClaim(claim);
1381 /// <summary>
1382 /// Override this virtual to provide custom processing of the SamlAuthenticationStatement.
1383 /// By default it adds authentication type and instant to each claim.
1384 /// </summary>
1385 /// <param name="samlStatement">The SamlAuthenticationStatement to process</param>
1386 /// <param name="subject">The identity that should be modified to reflect the statement</param>
1387 /// <param name="issuer">issuer Identity.</param>
1388 /// <exception cref="ArgumentNullException">The parameter 'samlSubject' or 'subject' is null.</exception>
1389 protected virtual void ProcessAuthenticationStatement(SamlAuthenticationStatement samlStatement, ClaimsIdentity subject, string issuer)
1391 if (samlStatement == null)
1393 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlStatement");
1396 if (subject == null)
1398 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1401 // When there is only a authentication statement present inside a saml assertion, we need to generate
1402 // a nameId claim. See FIP 4848. We do not support any saml assertion without a attribute statement, but
1403 // we might receive a saml assertion with only a authentication statement.
1404 ProcessSamlSubject(samlStatement.SamlSubject, subject, issuer);
1406 subject.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, NormalizeAuthenticationType(samlStatement.AuthenticationMethod), ClaimValueTypes.String, issuer));
1407 subject.AddClaim(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(samlStatement.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated), ClaimValueTypes.DateTime, issuer));
1410 /// <summary>
1411 /// Override this virtual to provide custom processing of SamlAuthorizationDecisionStatement.
1412 /// By default no processing is performed, you will need to access the token for SamlAuthorizationDecisionStatement information.
1413 /// </summary>
1414 /// <param name="samlStatement">The SamlAuthorizationDecisionStatement to process.</param>
1415 /// <param name="subject">The identity that should be modified to reflect the statement.</param>
1416 /// <param name="issuer">The subject that identifies the issuer.</param>
1417 protected virtual void ProcessAuthorizationDecisionStatement(SamlAuthorizationDecisionStatement samlStatement, ClaimsIdentity subject, string issuer)
1421 /// <summary>
1422 /// This method gets called when a special type of SamlAttribute is detected. The SamlAttribute passed in wraps a SamlAttribute
1423 /// that contains a collection of AttributeValues, each of which are mapped to a claim. All of the claims will be returned
1424 /// in an ClaimsIdentity with the specified issuer.
1425 /// </summary>
1426 /// <param name="attribute">The SamlAttribute to be processed.</param>
1427 /// <param name="subject">The identity that should be modified to reflect the SamlAttribute.</param>
1428 /// <param name="issuer">Issuer Identity.</param>
1429 /// <exception cref="InvalidOperationException">Will be thrown if the SamlAttribute does not contain any valid SamlAttributeValues.</exception>
1430 protected virtual void SetDelegateFromAttribute(SamlAttribute attribute, ClaimsIdentity subject, string issuer)
1432 // bail here nothing to add.
1433 if (subject == null || attribute == null || attribute.AttributeValues == null || attribute.AttributeValues.Count < 1)
1435 return;
1438 Collection<Claim> claims = new Collection<Claim>();
1439 SamlAttribute actingAsAttribute = null;
1441 foreach (string attributeValue in attribute.AttributeValues)
1443 if (attributeValue != null && attributeValue.Length > 0)
1446 using (XmlDictionaryReader xmlReader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(attributeValue), XmlDictionaryReaderQuotas.Max))
1448 xmlReader.MoveToContent();
1449 xmlReader.ReadStartElement(Actor);
1451 while (xmlReader.IsStartElement(Attribute))
1453 SamlAttribute innerAttribute = ReadAttribute(xmlReader);
1454 if (innerAttribute != null)
1456 string claimType = string.IsNullOrEmpty(innerAttribute.Namespace) ? innerAttribute.Name : innerAttribute.Namespace + "/" + innerAttribute.Name;
1457 if (claimType == ClaimTypes.Actor)
1459 // In this case we have two delegates acting as an identity, we do not allow this
1460 if (actingAsAttribute != null)
1462 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4034));
1465 actingAsAttribute = innerAttribute;
1467 else
1469 string claimValueType = ClaimValueTypes.String;
1470 string originalIssuer = null;
1471 SamlAttribute SamlAttribute = innerAttribute as SamlAttribute;
1472 if (SamlAttribute != null)
1474 claimValueType = SamlAttribute.AttributeValueXsiType;
1475 originalIssuer = SamlAttribute.OriginalIssuer;
1477 for (int k = 0; k < innerAttribute.AttributeValues.Count; ++k)
1479 Claim claim = null;
1480 if (string.IsNullOrEmpty(originalIssuer))
1482 claim = new Claim(claimType, innerAttribute.AttributeValues[k], claimValueType, issuer);
1484 else
1486 claim = new Claim(claimType, innerAttribute.AttributeValues[k], claimValueType, issuer, originalIssuer);
1488 claims.Add(claim);
1494 xmlReader.ReadEndElement(); // Actor
1499 subject.Actor = new ClaimsIdentity(claims, AuthenticationTypes.Federation);
1501 SetDelegateFromAttribute(actingAsAttribute, subject.Actor, issuer);
1504 #endregion
1506 #region TokenSerialization
1508 /// <summary>
1509 /// Indicates whether the current XML element can be read as a token
1510 /// of the type handled by this instance.
1511 /// </summary>
1512 /// <param name="reader">An XML reader positioned at a start
1513 /// element. The reader should not be advanced.</param>
1514 /// <returns>'True' if the ReadToken method can the element.</returns>
1515 public override bool CanReadToken(XmlReader reader)
1517 if (reader == null)
1519 return false;
1522 return reader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace);
1525 /// <summary>
1526 /// Deserializes from XML a token of the type handled by this instance.
1527 /// </summary>
1528 /// <param name="reader">An XML reader positioned at the token's start
1529 /// element.</param>
1530 /// <returns>An instance of <see cref="SamlSecurityToken"/>.</returns>
1531 /// <exception cref="InvalidOperationException">Is thrown if 'Configuration' or 'Configruation.IssuerTokenResolver' is null.</exception>
1532 public override SecurityToken ReadToken(XmlReader reader)
1534 if (Configuration == null)
1536 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1539 if (Configuration.IssuerTokenResolver == null)
1541 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
1544 SamlAssertion assertion = ReadAssertion(reader);
1546 // Resolve signing token if one is present. It may be deferred and signed by reference.
1548 SecurityToken token;
1550 TryResolveIssuerToken(assertion, Configuration.IssuerTokenResolver, out token);
1552 assertion.SigningToken = token;
1554 return new SamlSecurityToken(assertion);
1557 /// <summary>
1558 /// Read saml:Action element.
1559 /// </summary>
1560 /// <param name="reader">XmlReader positioned at saml:Action element.</param>
1561 /// <returns>SamlAction</returns>
1562 /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
1563 /// <exception cref="XmlException">The saml:Action element contains unknown elements.</exception>
1564 protected virtual SamlAction ReadAction(XmlReader reader)
1566 if (reader == null)
1568 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1571 if (reader.IsStartElement(SamlConstants.ElementNames.Action, SamlConstants.Namespace))
1573 // The Namespace attribute is optional.
1574 string ns = reader.GetAttribute(SamlConstants.AttributeNames.Namespace, null);
1576 reader.MoveToContent();
1577 string action = reader.ReadString();
1578 if (string.IsNullOrEmpty(action))
1580 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4073)));
1583 reader.MoveToContent();
1584 reader.ReadEndElement();
1586 return new SamlAction(action, ns);
1588 else
1590 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Action, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
1594 /// <summary>
1595 /// Writes the given SamlAction to the XmlWriter.
1596 /// </summary>
1597 /// <param name="writer">XmlWriter to serialize the SamlAction into.</param>
1598 /// <param name="action">SamlAction to serialize.</param>
1599 /// <exception cref="ArgumentNullException">The parameter 'writer' or 'action' is null.</exception>
1600 protected virtual void WriteAction(XmlWriter writer, SamlAction action)
1602 if (writer == null)
1604 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1607 if (action == null)
1609 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("action");
1612 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Action, SamlConstants.Namespace);
1613 if (!string.IsNullOrEmpty(action.Namespace))
1615 writer.WriteAttributeString(SamlConstants.AttributeNames.Namespace, null, action.Namespace);
1617 writer.WriteString(action.Action);
1618 writer.WriteEndElement();
1621 /// <summary>
1622 /// Read saml:Advice element from the given XmlReader.
1623 /// </summary>
1624 /// <param name="reader">XmlReader positioned at a SAML Advice element.</param>
1625 /// <returns>SamlAdvice</returns>
1626 /// <exception cref="ArgumentNullException">Parameter 'reader' is null.</exception>
1627 /// <exception cref="XmlException">The reder is not positioned at a saml:Advice element.</exception>
1628 protected virtual SamlAdvice ReadAdvice(XmlReader reader)
1630 if (reader == null)
1632 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1635 if (!reader.IsStartElement(SamlConstants.ElementNames.Advice, SamlConstants.Namespace))
1637 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Advice, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
1640 // SAML Advice is an optional element and all its child elements are optional
1641 // too. So we may have an empty saml:Advice element in the saml token.
1642 if (reader.IsEmptyElement)
1644 // Just issue a read for the empty element.
1645 reader.MoveToContent();
1646 reader.Read();
1647 return new SamlAdvice();
1650 reader.MoveToContent();
1651 reader.Read();
1652 Collection<string> assertionIdReferences = new Collection<string>();
1653 Collection<SamlAssertion> assertions = new Collection<SamlAssertion>();
1654 while (reader.IsStartElement())
1657 if (reader.IsStartElement(SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace))
1659 assertionIdReferences.Add(reader.ReadString());
1660 reader.ReadEndElement();
1662 else if (reader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace))
1664 SamlAssertion assertion = ReadAssertion(reader);
1665 assertions.Add(assertion);
1667 else
1669 TraceUtility.TraceString(TraceEventType.Warning, SR.GetString(SR.ID8005, reader.LocalName, reader.NamespaceURI));
1670 reader.Skip();
1675 reader.MoveToContent();
1676 reader.ReadEndElement();
1678 return new SamlAdvice(assertionIdReferences, assertions);
1682 /// <summary>
1683 /// Serialize the given SamlAdvice to the given XmlWriter.
1684 /// </summary>
1685 /// <param name="writer">XmlWriter to serialize the SamlAdvice.</param>
1686 /// <param name="advice">SamlAdvice to be serialized.</param>
1687 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'advice' is null.</exception>
1688 protected virtual void WriteAdvice(XmlWriter writer, SamlAdvice advice)
1690 if (writer == null)
1692 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1695 if (advice == null)
1697 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("advice");
1700 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Advice, SamlConstants.Namespace);
1701 if (advice.AssertionIdReferences.Count > 0)
1703 foreach (string assertionIdReference in advice.AssertionIdReferences)
1705 if (string.IsNullOrEmpty(assertionIdReference))
1707 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4079)));
1709 writer.WriteElementString(SamlConstants.Prefix, SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace, assertionIdReference);
1713 if (advice.Assertions.Count > 0)
1715 foreach (SamlAssertion assertion in advice.Assertions)
1717 WriteAssertion(writer, assertion);
1721 writer.WriteEndElement();
1724 /// <summary>
1725 /// Read saml:Assertion element from the given reader.
1726 /// </summary>
1727 /// <param name="reader">XmlReader to deserialize the Assertion from.</param>
1728 /// <returns>SamlAssertion</returns>
1729 /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
1730 /// <exception cref="XmlException">The XmlReader is not positioned at a saml:Assertion element or the Assertion
1731 /// contains unknown child elements.</exception>
1732 protected virtual SamlAssertion ReadAssertion(XmlReader reader)
1734 if (reader == null)
1736 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1739 if (this.Configuration == null)
1741 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1744 if (this.Configuration.IssuerTokenResolver == null)
1746 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
1749 SamlAssertion assertion = new SamlAssertion();
1751 EnvelopedSignatureReader wrappedReader = new EnvelopedSignatureReader(reader, new WrappedSerializer(this, assertion), this.Configuration.IssuerTokenResolver, false, true, false);
1754 if (!wrappedReader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace))
1756 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Assertion, SamlConstants.Namespace, wrappedReader.LocalName, wrappedReader.NamespaceURI)));
1759 string attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.MajorVersion, null);
1760 if (string.IsNullOrEmpty(attributeValue))
1762 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.MajorVersion)));
1765 int majorVersion = XmlConvert.ToInt32(attributeValue);
1767 attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.MinorVersion, null);
1768 if (string.IsNullOrEmpty(attributeValue))
1770 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.MinorVersion)));
1773 int minorVersion = XmlConvert.ToInt32(attributeValue);
1775 if ((majorVersion != SamlConstants.MajorVersionValue) || (minorVersion != SamlConstants.MinorVersionValue))
1777 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4076, majorVersion, minorVersion, SamlConstants.MajorVersionValue, SamlConstants.MinorVersionValue)));
1780 attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.AssertionId, null);
1781 if (string.IsNullOrEmpty(attributeValue))
1783 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.AssertionId)));
1786 if (!XmlUtil.IsValidXmlIDValue(attributeValue))
1788 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4077, attributeValue)));
1791 assertion.AssertionId = attributeValue;
1793 attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.Issuer, null);
1794 if (string.IsNullOrEmpty(attributeValue))
1796 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.Issuer)));
1799 assertion.Issuer = attributeValue;
1801 attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.IssueInstant, null);
1802 if (!string.IsNullOrEmpty(attributeValue))
1804 assertion.IssueInstant = DateTime.ParseExact(
1805 attributeValue, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1808 wrappedReader.MoveToContent();
1809 wrappedReader.Read();
1811 if (wrappedReader.IsStartElement(SamlConstants.ElementNames.Conditions, SamlConstants.Namespace))
1813 assertion.Conditions = ReadConditions(wrappedReader);
1816 if (wrappedReader.IsStartElement(SamlConstants.ElementNames.Advice, SamlConstants.Namespace))
1818 assertion.Advice = ReadAdvice(wrappedReader);
1821 while (wrappedReader.IsStartElement())
1823 assertion.Statements.Add(ReadStatement(wrappedReader));
1826 if (assertion.Statements.Count == 0)
1828 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4078)));
1831 wrappedReader.MoveToContent();
1832 wrappedReader.ReadEndElement();
1834 // Reading the end element will complete the signature;
1835 // capture the signing creds
1836 assertion.SigningCredentials = wrappedReader.SigningCredentials;
1838 // Save the captured on-the-wire data, which can then be used
1839 // to re-emit this assertion, preserving the same signature.
1840 assertion.CaptureSourceData(wrappedReader);
1842 return assertion;
1845 /// <summary>
1846 /// Serializes a given SamlAssertion to the XmlWriter.
1847 /// </summary>
1848 /// <param name="writer">XmlWriter to use for the serialization.</param>
1849 /// <param name="assertion">Assertion to be serialized into the XmlWriter.</param>
1850 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'assertion' is null.</exception>
1851 protected virtual void WriteAssertion(XmlWriter writer, SamlAssertion assertion)
1853 if (writer == null)
1855 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1858 if (assertion == null)
1860 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
1863 SamlAssertion SamlAssertion = assertion as SamlAssertion;
1864 if (SamlAssertion != null)
1866 if (SamlAssertion.CanWriteSourceData)
1868 SamlAssertion.WriteSourceData(writer);
1869 return;
1873 if (assertion.SigningCredentials != null)
1875 writer = new EnvelopedSignatureWriter(writer, assertion.SigningCredentials, assertion.AssertionId, new WrappedSerializer(this, assertion));
1877 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Assertion, SamlConstants.Namespace);
1879 writer.WriteAttributeString(SamlConstants.AttributeNames.MajorVersion, null, Convert.ToString(SamlConstants.MajorVersionValue, CultureInfo.InvariantCulture));
1880 writer.WriteAttributeString(SamlConstants.AttributeNames.MinorVersion, null, Convert.ToString(SamlConstants.MinorVersionValue, CultureInfo.InvariantCulture));
1881 writer.WriteAttributeString(SamlConstants.AttributeNames.AssertionId, null, assertion.AssertionId);
1882 writer.WriteAttributeString(SamlConstants.AttributeNames.Issuer, null, assertion.Issuer);
1883 writer.WriteAttributeString(SamlConstants.AttributeNames.IssueInstant, null, assertion.IssueInstant.ToUniversalTime().ToString(DateTimeFormats.Generated, CultureInfo.InvariantCulture));
1885 // Write out conditions
1886 if (assertion.Conditions != null)
1888 WriteConditions(writer, assertion.Conditions);
1891 // Write out advice if there is one
1892 if (assertion.Advice != null)
1894 WriteAdvice(writer, assertion.Advice);
1897 // Write statements.
1898 for (int i = 0; i < assertion.Statements.Count; i++)
1900 WriteStatement(writer, assertion.Statements[i]);
1903 writer.WriteEndElement();
1906 /// <summary>
1907 /// Read saml:Conditions from the given XmlReader.
1908 /// </summary>
1909 /// <param name="reader">XmlReader to read the SAML conditions from.</param>
1910 /// <returns>SamlConditions</returns>
1911 /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
1912 /// <exception cref="XmlException">The reader is not positioned at saml:Conditions element or contains
1913 /// elements that are not recognized.</exception>
1914 protected virtual SamlConditions ReadConditions(XmlReader reader)
1916 if (reader == null)
1918 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1921 SamlConditions conditions = new SamlConditions();
1922 string time = reader.GetAttribute(SamlConstants.AttributeNames.NotBefore, null);
1923 if (!string.IsNullOrEmpty(time))
1925 conditions.NotBefore = DateTime.ParseExact(
1926 time, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1929 time = reader.GetAttribute(SamlConstants.AttributeNames.NotOnOrAfter, null);
1930 if (!string.IsNullOrEmpty(time))
1932 conditions.NotOnOrAfter = DateTime.ParseExact(
1933 time, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1936 // Saml Conditions element is an optional element and all its child element
1937 // are optional as well. So we can have a empty <saml:Conditions /> element
1938 // in a valid Saml token.
1939 if (reader.IsEmptyElement)
1941 // Just issue a read to read the Empty element.
1942 reader.MoveToContent();
1943 reader.Read();
1944 return conditions;
1947 reader.ReadStartElement();
1949 while (reader.IsStartElement())
1951 conditions.Conditions.Add(ReadCondition(reader));
1954 reader.ReadEndElement();
1956 return conditions;
1959 /// <summary>
1960 /// Serialize SamlConditions to the given XmlWriter.
1961 /// </summary>
1962 /// <param name="writer">XmlWriter to which the SamlConditions is serialized.</param>
1963 /// <param name="conditions">SamlConditions to be serialized.</param>
1964 /// <exception cref="ArgumentNullException">The parameter 'writer' or 'conditions' is null.</exception>
1965 protected virtual void WriteConditions(XmlWriter writer, SamlConditions conditions)
1967 if (writer == null)
1969 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1972 if (conditions == null)
1974 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("conditions");
1977 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Conditions, SamlConstants.Namespace);
1979 // SamlConditions when new'ed up will have the min and max values defined in WCF
1980 // which is different than our defaults. To maintin compatability with WCF behavior we will
1981 // not write out SamlConditions NotBefore and NotOnOrAfter times which match the WCF
1982 // min and max default values as well.
1983 if (conditions.NotBefore != DateTimeUtil.GetMinValue(DateTimeKind.Utc) &&
1984 conditions.NotBefore != WCFMinValue)
1986 writer.WriteAttributeString(
1987 SamlConstants.AttributeNames.NotBefore,
1988 null,
1989 conditions.NotBefore.ToUniversalTime().ToString(DateTimeFormats.Generated, DateTimeFormatInfo.InvariantInfo));
1992 if (conditions.NotOnOrAfter != DateTimeUtil.GetMaxValue(DateTimeKind.Utc) &&
1993 conditions.NotOnOrAfter != WCFMaxValue)
1995 writer.WriteAttributeString(
1996 SamlConstants.AttributeNames.NotOnOrAfter,
1997 null,
1998 conditions.NotOnOrAfter.ToUniversalTime().ToString(DateTimeFormats.Generated, DateTimeFormatInfo.InvariantInfo));
2001 for (int i = 0; i < conditions.Conditions.Count; i++)
2003 WriteCondition(writer, conditions.Conditions[i]);
2006 writer.WriteEndElement();
2009 /// <summary>
2010 /// Read saml:AudienceRestrictionCondition or saml:DoNotCacheCondition from the given reader.
2011 /// </summary>
2012 /// <param name="reader">XmlReader to read the SamlCondition from.</param>
2013 /// <returns>SamlCondition</returns>
2014 /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
2015 /// <exception cref="XmlException">XmlReader is positioned at an unknown element.</exception>
2016 protected virtual SamlCondition ReadCondition(XmlReader reader)
2018 if (reader == null)
2020 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2023 if (reader.IsStartElement(SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace))
2025 return ReadAudienceRestrictionCondition(reader);
2027 else if (reader.IsStartElement(SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace))
2029 return ReadDoNotCacheCondition(reader);
2031 else
2033 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4080, reader.LocalName, reader.NamespaceURI)));
2037 /// <summary>
2038 /// Serializes the given SamlCondition to the given XmlWriter.
2039 /// </summary>
2040 /// <param name="writer">XmlWriter to serialize the condition.</param>
2041 /// <param name="condition">SamlConditon to be serialized.</param>
2042 /// <exception cref="ArgumentNullException">The parameter 'condition' is null.</exception>
2043 /// <exception cref="SecurityTokenException">The given condition is unknown. By default only SamlAudienceRestrictionCondition
2044 /// and SamlDoNotCacheCondition are serialized.</exception>
2045 protected virtual void WriteCondition(XmlWriter writer, SamlCondition condition)
2047 if (condition == null)
2049 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("condition");
2052 SamlAudienceRestrictionCondition audienceRestrictionCondition = condition as SamlAudienceRestrictionCondition;
2053 if (audienceRestrictionCondition != null)
2055 WriteAudienceRestrictionCondition(writer, audienceRestrictionCondition);
2056 return;
2059 SamlDoNotCacheCondition doNotCacheCondition = condition as SamlDoNotCacheCondition;
2060 if (doNotCacheCondition != null)
2062 WriteDoNotCacheCondition(writer, doNotCacheCondition);
2063 return;
2066 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4081, condition.GetType())));
2069 /// <summary>
2070 /// Read saml:AudienceRestrictionCondition from the given XmlReader.
2071 /// </summary>
2072 /// <param name="reader">XmlReader positioned at a saml:AudienceRestrictionCondition.</param>
2073 /// <returns>SamlAudienceRestrictionCondition</returns>
2074 /// <exception cref="ArgumentNullException">The inpur parameter 'reader' is null.</exception>
2075 /// <exception cref="XmlException">The XmlReader is not positioned at saml:AudienceRestrictionCondition.</exception>
2076 protected virtual SamlAudienceRestrictionCondition ReadAudienceRestrictionCondition(XmlReader reader)
2078 if (reader == null)
2080 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2083 if (!reader.IsStartElement(SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace))
2085 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2088 reader.ReadStartElement();
2090 SamlAudienceRestrictionCondition audienceRestrictionCondition = new SamlAudienceRestrictionCondition();
2091 while (reader.IsStartElement())
2093 if (reader.IsStartElement(SamlConstants.ElementNames.Audience, SamlConstants.Namespace))
2095 string audience = reader.ReadString();
2096 if (string.IsNullOrEmpty(audience))
2098 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4083)));
2101 audienceRestrictionCondition.Audiences.Add(new Uri(audience, UriKind.RelativeOrAbsolute));
2102 reader.MoveToContent();
2103 reader.ReadEndElement();
2105 else
2107 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.Audience, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2111 if (audienceRestrictionCondition.Audiences.Count == 0)
2113 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4084)));
2116 reader.MoveToContent();
2117 reader.ReadEndElement();
2119 return audienceRestrictionCondition;
2122 /// <summary>
2123 /// Serialize SamlAudienceRestrictionCondition to a XmlWriter.
2124 /// </summary>
2125 /// <param name="writer">XmlWriter to serialize the SamlAudienceRestrictionCondition.</param>
2126 /// <param name="condition">SamlAudienceRestrictionCondition to serialize.</param>
2127 /// <exception cref="ArgumentNullException">The parameter 'writer' or 'condition' is null.</exception>
2128 protected virtual void WriteAudienceRestrictionCondition(XmlWriter writer, SamlAudienceRestrictionCondition condition)
2130 if (writer == null)
2132 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2135 if (condition == null)
2137 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("condition");
2140 // Schema requires at least one audience.
2141 if (condition.Audiences == null || condition.Audiences.Count == 0)
2143 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2144 new InvalidOperationException(SR.GetString(SR.ID4269)));
2147 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace);
2149 for (int i = 0; i < condition.Audiences.Count; i++)
2151 // When writing out the audience uri we use the OriginalString property to preserve the value that was initially passed down during token creation as-is.
2152 writer.WriteElementString(SamlConstants.ElementNames.Audience, SamlConstants.Namespace, condition.Audiences[i].OriginalString);
2155 writer.WriteEndElement();
2158 /// <summary>
2159 /// Read saml:DoNotCacheCondition from the given XmlReader.
2160 /// </summary>
2161 /// <param name="reader">XmlReader positioned at a saml:DoNotCacheCondition element.</param>
2162 /// <returns>SamlDoNotCacheCondition</returns>
2163 /// <exception cref="ArgumentNullException">The inpur parameter 'reader' is null.</exception>
2164 /// <exception cref="XmlException">The XmlReader is not positioned at saml:DoNotCacheCondition.</exception>
2165 protected virtual SamlDoNotCacheCondition ReadDoNotCacheCondition(XmlReader reader)
2167 if (reader == null)
2169 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2172 if (!reader.IsStartElement(SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace))
2174 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2177 SamlDoNotCacheCondition doNotCacheCondition = new SamlDoNotCacheCondition();
2178 // saml:DoNotCacheCondition is a empty element. So just issue a read for
2179 // the empty element.
2180 if (reader.IsEmptyElement)
2182 reader.MoveToContent();
2183 reader.Read();
2184 return doNotCacheCondition;
2187 reader.MoveToContent();
2188 reader.ReadStartElement();
2189 reader.ReadEndElement();
2191 return doNotCacheCondition;
2194 /// <summary>
2195 /// Serialize SamlDoNotCacheCondition to a XmlWriter.
2196 /// </summary>
2197 /// <param name="writer">XmlWriter to serialize the SamlDoNotCacheCondition.</param>
2198 /// <param name="condition">SamlDoNotCacheCondition to serialize.</param>
2199 /// <exception cref="ArgumentNullException">The parameter 'writer' or 'condition' is null.</exception>
2200 protected virtual void WriteDoNotCacheCondition(XmlWriter writer, SamlDoNotCacheCondition condition)
2202 if (writer == null)
2204 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2207 if (condition == null)
2209 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("condition");
2212 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace);
2213 writer.WriteEndElement();
2216 /// <summary>
2217 /// Read a SamlStatement from the given XmlReader.
2218 /// </summary>
2219 /// <param name="reader">XmlReader positioned at a SamlStatement.</param>
2220 /// <returns>SamlStatement</returns>
2221 /// <exception cref="ArgumentNullException">The inpur parameter 'reader' is null.</exception>
2222 /// <exception cref="XmlException">The XmlReader is not positioned at recognized SamlStatement. By default,
2223 /// only saml:AuthenticationStatement, saml:AttributeStatement and saml:AuthorizationDecisionStatement.</exception>
2224 protected virtual SamlStatement ReadStatement(XmlReader reader)
2226 if (reader == null)
2228 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2231 if (reader.IsStartElement(SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace))
2233 return ReadAuthenticationStatement(reader);
2235 else if (reader.IsStartElement(SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace))
2237 return ReadAttributeStatement(reader);
2239 else if (reader.IsStartElement(SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace))
2241 return ReadAuthorizationDecisionStatement(reader);
2243 else
2245 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4085, reader.LocalName, reader.NamespaceURI)));
2250 /// <summary>
2251 /// Serialize the SamlStatement to the XmlWriter.
2252 /// </summary>
2253 /// <param name="writer">XmlWriter to serialize the SamlStatement.</param>
2254 /// <param name="statement">The SamlStatement to serialize.</param>
2255 /// <exception cref="ArgumentNullException">The parameter 'writer' or 'statement' is null.</exception>
2256 /// <exception cref="SecurityTokenException">The SamlStatement is not recognized. Only SamlAuthenticationStatement,
2257 /// SamlAuthorizationStatement and SamlAttributeStatement are recognized.</exception>
2258 protected virtual void WriteStatement(XmlWriter writer, SamlStatement statement)
2260 if (writer == null)
2262 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2265 if (statement == null)
2267 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
2270 SamlAuthenticationStatement authnStatement = statement as SamlAuthenticationStatement;
2271 if (authnStatement != null)
2273 WriteAuthenticationStatement(writer, authnStatement);
2274 return;
2277 SamlAuthorizationDecisionStatement authzStatement = statement as SamlAuthorizationDecisionStatement;
2278 if (authzStatement != null)
2280 WriteAuthorizationDecisionStatement(writer, authzStatement);
2281 return;
2284 SamlAttributeStatement attributeStatement = statement as SamlAttributeStatement;
2285 if (attributeStatement != null)
2287 WriteAttributeStatement(writer, attributeStatement);
2288 return;
2291 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4086, statement.GetType())));
2294 /// <summary>
2295 /// Read the SamlSubject from the XmlReader.
2296 /// </summary>
2297 /// <param name="reader">XmlReader to read the SamlSubject from.</param>
2298 /// <returns>SamlSubject</returns>
2299 /// <exception cref="ArgumentNullException">The input argument 'reader' is null.</exception>
2300 /// <exception cref="XmlException">The reader is not positioned at a SamlSubject.</exception>
2301 protected virtual SamlSubject ReadSubject(XmlReader reader)
2303 if (reader == null)
2305 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2308 if (!reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
2310 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.Subject, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2313 SamlSubject subject = new SamlSubject();
2315 reader.ReadStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace);
2316 if (reader.IsStartElement(SamlConstants.ElementNames.NameIdentifier, SamlConstants.Namespace))
2318 subject.NameFormat = reader.GetAttribute(SamlConstants.AttributeNames.NameIdentifierFormat, null);
2319 subject.NameQualifier = reader.GetAttribute(SamlConstants.AttributeNames.NameIdentifierNameQualifier, null);
2321 reader.MoveToContent();
2322 subject.Name = reader.ReadElementString();
2324 if (string.IsNullOrEmpty(subject.Name))
2326 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4087)));
2330 if (reader.IsStartElement(SamlConstants.ElementNames.SubjectConfirmation, SamlConstants.Namespace))
2332 reader.ReadStartElement();
2334 while (reader.IsStartElement(SamlConstants.ElementNames.SubjectConfirmationMethod, SamlConstants.Namespace))
2336 string method = reader.ReadElementString();
2337 if (string.IsNullOrEmpty(method))
2339 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4088)));
2342 subject.ConfirmationMethods.Add(method);
2345 if (subject.ConfirmationMethods.Count == 0)
2347 // A SubjectConfirmaton clause should specify at least one
2348 // ConfirmationMethod.
2349 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4088)));
2352 if (reader.IsStartElement(SamlConstants.ElementNames.SubjectConfirmationData, SamlConstants.Namespace))
2354 // An Authentication protocol specified in the confirmation method might need this
2355 // data. Just store this content value as string.
2356 subject.SubjectConfirmationData = reader.ReadElementString();
2359 if (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace))
2361 subject.KeyIdentifier = ReadSubjectKeyInfo(reader);
2362 SecurityKey key = ResolveSubjectKeyIdentifier(subject.KeyIdentifier);
2363 if (key != null)
2365 subject.Crypto = key;
2367 else
2369 subject.Crypto = new SecurityKeyElement(subject.KeyIdentifier, this.Configuration.ServiceTokenResolver);
2374 if ((subject.ConfirmationMethods.Count == 0) && (string.IsNullOrEmpty(subject.Name)))
2376 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4089)));
2379 reader.MoveToContent();
2380 reader.ReadEndElement();
2383 reader.MoveToContent();
2384 reader.ReadEndElement();
2386 return subject;
2389 /// <summary>
2390 /// Serialize the given SamlSubject into an XmlWriter.
2391 /// </summary>
2392 /// <param name="writer">XmlWriter into which the SamlSubject is serialized.</param>
2393 /// <param name="subject">SamlSubject to be serialized.</param>
2394 /// <exception cref="ArgumentNullException">The input parameter 'subject' or 'writer' is null.</exception>
2395 protected virtual void WriteSubject(XmlWriter writer, SamlSubject subject)
2397 if (writer == null)
2399 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2402 if (subject == null)
2404 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
2407 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Subject, SamlConstants.Namespace);
2408 if (!string.IsNullOrEmpty(subject.Name))
2410 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.NameIdentifier, SamlConstants.Namespace);
2411 if (!string.IsNullOrEmpty(subject.NameFormat))
2413 writer.WriteAttributeString(SamlConstants.AttributeNames.NameIdentifierFormat, null, subject.NameFormat);
2415 if (subject.NameQualifier != null)
2417 writer.WriteAttributeString(SamlConstants.AttributeNames.NameIdentifierNameQualifier, null, subject.NameQualifier);
2419 writer.WriteString(subject.Name);
2420 writer.WriteEndElement();
2423 if (subject.ConfirmationMethods.Count > 0)
2425 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.SubjectConfirmation, SamlConstants.Namespace);
2427 foreach (string method in subject.ConfirmationMethods)
2429 writer.WriteElementString(SamlConstants.ElementNames.SubjectConfirmationMethod, SamlConstants.Namespace, method);
2432 if (!string.IsNullOrEmpty(subject.SubjectConfirmationData))
2434 writer.WriteElementString(SamlConstants.ElementNames.SubjectConfirmationData, SamlConstants.Namespace, subject.SubjectConfirmationData);
2437 if (subject.KeyIdentifier != null)
2439 WriteSubjectKeyInfo(writer, subject.KeyIdentifier);
2442 writer.WriteEndElement();
2445 writer.WriteEndElement();
2448 /// <summary>
2449 /// Read the SamlSubject KeyIdentifier from a XmlReader.
2450 /// </summary>
2451 /// <param name="reader">XmlReader positioned at the SamlSubject KeyIdentifier.</param>
2452 /// <returns>SamlSubject Key as a SecurityKeyIdentifier.</returns>
2453 /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
2454 /// <exception cref="XmlException">XmlReader is not positioned at a valid SecurityKeyIdentifier.</exception>
2455 protected virtual SecurityKeyIdentifier ReadSubjectKeyInfo(XmlReader reader)
2457 if (reader == null)
2459 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2462 if (KeyInfoSerializer.CanReadKeyIdentifier(reader))
2464 return KeyInfoSerializer.ReadKeyIdentifier(reader);
2467 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4090)));
2470 /// <summary>
2471 /// Write the SamlSubject SecurityKeyIdentifier to the XmlWriter.
2472 /// </summary>
2473 /// <param name="writer">XmlWriter to write the SecurityKeyIdentifier.</param>
2474 /// <param name="subjectSki">SecurityKeyIdentifier to serialize.</param>
2475 /// <exception cref="ArgumentNullException">The inpur parameter 'writer' or 'subjectSki' is null.</exception>
2476 protected virtual void WriteSubjectKeyInfo(XmlWriter writer, SecurityKeyIdentifier subjectSki)
2478 if (writer == null)
2480 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2483 if (subjectSki == null)
2485 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subjectSki");
2488 if (KeyInfoSerializer.CanWriteKeyIdentifier(subjectSki))
2490 KeyInfoSerializer.WriteKeyIdentifier(writer, subjectSki);
2491 return;
2494 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("subjectSki", SR.GetString(SR.ID4091, subjectSki.GetType()));
2497 /// <summary>
2498 /// Read saml:AttributeStatement from the given XmlReader.
2499 /// </summary>
2500 /// <param name="reader">XmlReader positioned at a saml:AttributeStatement element.</param>
2501 /// <returns>SamlAttributeStatement</returns>
2502 /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
2503 /// <exception cref="XmlException">XmlReader is not positioned at a saml:AttributeStatement element or
2504 /// the AttributeStatement contains unrecognized elements.</exception>
2505 protected virtual SamlAttributeStatement ReadAttributeStatement(XmlReader reader)
2507 if (reader == null)
2509 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2512 if (!reader.IsStartElement(SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace))
2514 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2517 reader.ReadStartElement();
2519 SamlAttributeStatement attributeStatement = new SamlAttributeStatement();
2520 if (reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
2522 attributeStatement.SamlSubject = ReadSubject(reader);
2524 else
2526 // SAML Subject is a required Attribute Statement clause.
2527 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4092)));
2530 while (reader.IsStartElement())
2532 if (reader.IsStartElement(SamlConstants.ElementNames.Attribute, SamlConstants.Namespace))
2534 // SAML Attribute is a extensibility point. So ask the SAML serializer
2535 // to load this part.
2536 attributeStatement.Attributes.Add(ReadAttribute(reader));
2538 else
2540 break;
2544 if (attributeStatement.Attributes.Count == 0)
2546 // Each Attribute statement should have at least one attribute.
2547 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4093)));
2550 reader.MoveToContent();
2551 reader.ReadEndElement();
2553 return attributeStatement;
2556 /// <summary>
2557 /// Serialize a SamlAttributeStatement.
2558 /// </summary>
2559 /// <param name="writer">XmlWriter to serialize the given statement.</param>
2560 /// <param name="statement">SamlAttributeStatement to write to the XmlWriter.</param>
2561 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'statement' is null.</exception>
2562 protected virtual void WriteAttributeStatement(XmlWriter writer, SamlAttributeStatement statement)
2564 if (writer == null)
2566 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2569 if (statement == null)
2571 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
2574 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace);
2576 WriteSubject(writer, statement.SamlSubject);
2578 for (int i = 0; i < statement.Attributes.Count; i++)
2580 WriteAttribute(writer, statement.Attributes[i]);
2583 writer.WriteEndElement();
2586 /// <summary>
2587 /// Read an saml:Attribute element.
2588 /// </summary>
2589 /// <param name="reader">XmlReader positioned at a saml:Attribute element.</param>
2590 /// <returns>SamlAttribute</returns>
2591 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
2592 /// <exception cref="XmlException">The XmlReader is not positioned on a valid saml:Attribute element.</exception>
2593 protected virtual SamlAttribute ReadAttribute(XmlReader reader)
2595 if (reader == null)
2597 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2600 SamlAttribute attribute = new SamlAttribute();
2602 attribute.Name = reader.GetAttribute(SamlConstants.AttributeNames.AttributeName, null);
2603 if (string.IsNullOrEmpty(attribute.Name))
2605 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4094)));
2608 attribute.Namespace = reader.GetAttribute(SamlConstants.AttributeNames.AttributeNamespace, null);
2609 if (string.IsNullOrEmpty(attribute.Namespace))
2611 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4095)));
2615 // OriginalIssuer is an optional attribute.
2616 // We are lax on read here, and will accept the following namespaces for original issuer, in order:
2617 // http://schemas.xmlsoap.org/ws/2009/09/identity/claims
2618 // http://schemas.microsoft.com/ws/2008/06/identity
2620 string originalIssuer = reader.GetAttribute(SamlConstants.AttributeNames.OriginalIssuer, ClaimType2009Namespace);
2622 if (originalIssuer == null)
2624 originalIssuer = reader.GetAttribute(SamlConstants.AttributeNames.OriginalIssuer, ProductConstants.NamespaceUri);
2627 if (originalIssuer == String.Empty)
2629 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4252)));
2631 attribute.OriginalIssuer = originalIssuer;
2633 reader.MoveToContent();
2634 reader.Read();
2635 while (reader.IsStartElement(SamlConstants.ElementNames.AttributeValue, SamlConstants.Namespace))
2637 // FIP 9570 - ENTERPRISE SCENARIO: Saml11SecurityTokenHandler.ReadAttribute is not checking the AttributeValue XSI type correctly.
2638 // Lax on receive. If we dont find the AttributeValueXsiType in the format we are looking for in the xml, we default to string.
2639 // Read the xsi:type. We are expecting a value of the form "some-non-empty-string" or "some-non-empty-local-prefix:some-non-empty-string".
2640 // ":some-non-empty-string" and "some-non-empty-string:" are edge-cases where defaulting to string is reasonable.
2641 string attributeValueXsiTypePrefix = null;
2642 string attributeValueXsiTypeSuffix = null;
2643 string attributeValueXsiTypeSuffixWithLocalPrefix = reader.GetAttribute("type", XmlSchema.InstanceNamespace);
2644 if (!string.IsNullOrEmpty(attributeValueXsiTypeSuffixWithLocalPrefix))
2646 if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) == -1) // "some-non-empty-string" case
2648 attributeValueXsiTypePrefix = reader.LookupNamespace(String.Empty);
2649 attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix;
2651 else if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) > 0 &&
2652 attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) < attributeValueXsiTypeSuffixWithLocalPrefix.Length - 1) // "some-non-empty-local-prefix:some-non-empty-string" case
2654 string localPrefix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(0, attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal));
2655 attributeValueXsiTypePrefix = reader.LookupNamespace(localPrefix);
2656 // For attributeValueXsiTypeSuffix, we want the portion after the local prefix in "some-non-empty-local-prefix:some-non-empty-string"
2657 attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) + 1);
2660 if (attributeValueXsiTypePrefix != null && attributeValueXsiTypeSuffix != null)
2662 attribute.AttributeValueXsiType = String.Concat(attributeValueXsiTypePrefix, "#", attributeValueXsiTypeSuffix);
2665 if (reader.IsEmptyElement)
2667 reader.Read();
2668 attribute.AttributeValues.Add(string.Empty);
2670 else
2672 attribute.AttributeValues.Add(ReadAttributeValue(reader, attribute));
2676 if (attribute.AttributeValues.Count == 0)
2678 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4212)));
2681 reader.MoveToContent();
2682 reader.ReadEndElement();
2684 return attribute;
2688 /// <summary>
2689 /// Reads an attribute value.
2690 /// </summary>
2691 /// <param name="reader">XmlReader to read from.</param>
2692 /// <param name="attribute">The current attribute that is being read.</param>
2693 /// <returns>The attribute value as a string.</returns>
2694 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
2695 protected virtual string ReadAttributeValue(XmlReader reader, SamlAttribute attribute)
2697 // This code was designed realizing that the writter of the xml controls how our
2698 // reader will report the NodeType. A completely differnet system (IBM, etc) could write the values.
2699 // Considering NodeType is important, because we need to read the entire value, end element and not loose anything significant.
2701 // Couple of cases to help understand the design choices.
2703 // 1.
2704 // "<MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>"
2705 // Could result in the our reader reporting the NodeType as Text OR Element, depending if '<' was entitized to '&lt;'
2707 // 2.
2708 // " <MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>"
2709 // Could result in the our reader reporting the NodeType as Text OR Whitespace. Post Whitespace processing, the NodeType could be
2710 // reported as Text or Element, depending if '<' was entitized to '&lt;'
2712 // 3.
2713 // "/r/n/t "
2714 // Could result in the our reader reporting the NodeType as whitespace.
2716 // Since an AttributeValue with ONLY Whitespace and a complex Element proceeded by whitespace are reported as the same NodeType (2. and 3.)
2717 // the whitespace is remembered and discarded if an found is found, otherwise it becomes the value. This is to help users who accidently put a space when adding claims in ADFS
2718 // If we just skipped the Whitespace, then an AttributeValue that started with Whitespace would loose that part and claims generated from the AttributeValue
2719 // would be missing that part.
2722 if (reader == null)
2724 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2727 string result = String.Empty;
2728 string whiteSpace = String.Empty;
2730 reader.ReadStartElement(Saml2Constants.Elements.AttributeValue, SamlConstants.Namespace);
2732 while (reader.NodeType == XmlNodeType.Whitespace)
2734 whiteSpace += reader.Value;
2735 reader.Read();
2738 reader.MoveToContent();
2739 if (reader.NodeType == XmlNodeType.Element)
2741 while (reader.NodeType == XmlNodeType.Element)
2743 result += reader.ReadOuterXml();
2744 reader.MoveToContent();
2747 else
2749 result = whiteSpace;
2750 result += reader.ReadContentAsString();
2753 reader.ReadEndElement();
2754 return result;
2757 /// <summary>
2758 /// Serializes a given SamlAttribute.
2759 /// </summary>
2760 /// <param name="writer">XmlWriter to serialize the SamlAttribute.</param>
2761 /// <param name="attribute">SamlAttribute to be serialized.</param>
2762 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'attribute' is null.</exception>
2763 protected virtual void WriteAttribute(XmlWriter writer, SamlAttribute attribute)
2765 if (writer == null)
2767 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2770 if (attribute == null)
2772 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("attribute");
2775 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Attribute, SamlConstants.Namespace);
2777 writer.WriteAttributeString(SamlConstants.AttributeNames.AttributeName, null, attribute.Name);
2778 writer.WriteAttributeString(SamlConstants.AttributeNames.AttributeNamespace, null, attribute.Namespace);
2780 SamlAttribute SamlAttribute = attribute as SamlAttribute;
2781 if ((SamlAttribute != null) && (SamlAttribute.OriginalIssuer != null))
2783 writer.WriteAttributeString(SamlConstants.AttributeNames.OriginalIssuer, ClaimType2009Namespace, SamlAttribute.OriginalIssuer);
2786 string xsiTypePrefix = null;
2787 string xsiTypeSuffix = null;
2788 if (SamlAttribute != null && !StringComparer.Ordinal.Equals(SamlAttribute.AttributeValueXsiType, ClaimValueTypes.String))
2790 // ClaimValueTypes are URIs of the form prefix#suffix, while xsi:type should be a QName.
2791 // Hence, the tokens-to-claims spec requires that ClaimValueTypes be serialized as xmlns:tn="prefix" xsi:type="tn:suffix"
2792 int indexOfHash = SamlAttribute.AttributeValueXsiType.IndexOf('#');
2793 xsiTypePrefix = SamlAttribute.AttributeValueXsiType.Substring(0, indexOfHash);
2794 xsiTypeSuffix = SamlAttribute.AttributeValueXsiType.Substring(indexOfHash + 1);
2797 for (int i = 0; i < attribute.AttributeValues.Count; i++)
2799 if (attribute.AttributeValues[i] == null)
2801 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4096)));
2804 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AttributeValue, SamlConstants.Namespace);
2805 if ((xsiTypePrefix != null) && (xsiTypeSuffix != null))
2807 writer.WriteAttributeString("xmlns", ProductConstants.ClaimValueTypeSerializationPrefix, null, xsiTypePrefix);
2808 writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, String.Concat(ProductConstants.ClaimValueTypeSerializationPrefixWithColon, xsiTypeSuffix));
2810 WriteAttributeValue(writer, attribute.AttributeValues[i], attribute);
2811 writer.WriteEndElement();
2814 writer.WriteEndElement();
2817 /// <summary>
2818 /// Writes the saml:Attribute value.
2819 /// </summary>
2820 /// <param name="writer">XmlWriter to which to write.</param>
2821 /// <param name="value">Attribute value to be written.</param>
2822 /// <param name="attribute">The SAML attribute whose value is being written.</param>
2823 /// <remarks>By default the method writes the value as a string.</remarks>
2824 /// <exception cref="ArgumentNullException">The input parameter 'writer' is null.</exception>
2825 protected virtual void WriteAttributeValue(XmlWriter writer, string value, SamlAttribute attribute)
2827 if (writer == null)
2829 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2832 writer.WriteString(value);
2835 /// <summary>
2836 /// Read the saml:AuthenticationStatement.
2837 /// </summary>
2838 /// <param name="reader">XmlReader positioned at a saml:AuthenticationStatement.</param>
2839 /// <returns>SamlAuthenticationStatement</returns>
2840 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
2841 /// <exception cref="XmlException">The XmlReader is not positioned on a saml:AuthenticationStatement
2842 /// or the statement contains a unknown child element.</exception>
2843 protected virtual SamlAuthenticationStatement ReadAuthenticationStatement(XmlReader reader)
2845 if (reader == null)
2847 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2850 if (!reader.IsStartElement(SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace))
2852 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2855 SamlAuthenticationStatement authnStatement = new SamlAuthenticationStatement();
2856 string authInstance = reader.GetAttribute(SamlConstants.AttributeNames.AuthenticationInstant, null);
2857 if (string.IsNullOrEmpty(authInstance))
2859 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4097)));
2861 authnStatement.AuthenticationInstant = DateTime.ParseExact(
2862 authInstance, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
2864 authnStatement.AuthenticationMethod = reader.GetAttribute(SamlConstants.AttributeNames.AuthenticationMethod, null);
2865 if (string.IsNullOrEmpty(authnStatement.AuthenticationMethod))
2867 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4098)));
2870 reader.MoveToContent();
2871 reader.Read();
2873 if (reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
2875 authnStatement.SamlSubject = ReadSubject(reader);
2877 else
2879 // Subject is a required element for a Authentication Statement clause.
2880 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4099)));
2883 if (reader.IsStartElement(SamlConstants.ElementNames.SubjectLocality, SamlConstants.Namespace))
2885 authnStatement.DnsAddress = reader.GetAttribute(SamlConstants.AttributeNames.SubjectLocalityDNSAddress, null);
2886 authnStatement.IPAddress = reader.GetAttribute(SamlConstants.AttributeNames.SubjectLocalityIPAddress, null);
2888 if (reader.IsEmptyElement)
2890 reader.MoveToContent();
2891 reader.Read();
2893 else
2895 reader.MoveToContent();
2896 reader.Read();
2897 reader.ReadEndElement();
2901 while (reader.IsStartElement())
2903 if (reader.IsStartElement(SamlConstants.ElementNames.AuthorityBinding, SamlConstants.Namespace))
2905 authnStatement.AuthorityBindings.Add(ReadAuthorityBinding(reader));
2907 else
2909 // We do not understand this element.
2910 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AuthorityBinding, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2914 reader.MoveToContent();
2915 reader.ReadEndElement();
2917 return authnStatement;
2920 /// <summary>
2921 /// Serializes a given SamlAuthenticationStatement.
2922 /// </summary>
2923 /// <param name="writer">XmlWriter to which SamlAuthenticationStatement is serialized.</param>
2924 /// <param name="statement">SamlAuthenticationStatement to be serialized.</param>
2925 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'statement' is null.</exception>
2926 protected virtual void WriteAuthenticationStatement(XmlWriter writer, SamlAuthenticationStatement statement)
2928 if (writer == null)
2930 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2933 if (statement == null)
2935 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
2938 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace);
2940 writer.WriteAttributeString(SamlConstants.AttributeNames.AuthenticationMethod, null, statement.AuthenticationMethod);
2942 writer.WriteAttributeString(SamlConstants.AttributeNames.AuthenticationInstant, null,
2943 XmlConvert.ToString(statement.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated));
2946 WriteSubject(writer, statement.SamlSubject);
2948 if ((statement.IPAddress != null) || (statement.DnsAddress != null))
2950 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.SubjectLocality, SamlConstants.Namespace);
2952 if (statement.IPAddress != null)
2954 writer.WriteAttributeString(SamlConstants.AttributeNames.SubjectLocalityIPAddress, null, statement.IPAddress);
2957 if (statement.DnsAddress != null)
2959 writer.WriteAttributeString(SamlConstants.AttributeNames.SubjectLocalityDNSAddress, null, statement.DnsAddress);
2962 writer.WriteEndElement();
2965 for (int i = 0; i < statement.AuthorityBindings.Count; i++)
2967 WriteAuthorityBinding(writer, statement.AuthorityBindings[i]);
2970 writer.WriteEndElement();
2973 /// <summary>
2974 /// Read the saml:AuthorityBinding element.
2975 /// </summary>
2976 /// <param name="reader">XmlReader positioned at the saml:AuthorityBinding element.</param>
2977 /// <returns>SamlAuthorityBinding</returns>
2978 /// <exception cref="ArgumentNullException">The inpur parameter 'reader' is null.</exception>
2979 /// <exception cref="XmlException">XmlReader is not positioned at a saml:AuthorityBinding element or
2980 /// contains a unrecognized or invalid child element.</exception>
2981 protected virtual SamlAuthorityBinding ReadAuthorityBinding(XmlReader reader)
2983 if (reader == null)
2985 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2988 SamlAuthorityBinding authorityBinding = new SamlAuthorityBinding();
2989 string authKind = reader.GetAttribute(SamlConstants.AttributeNames.AuthorityKind, null);
2990 if (string.IsNullOrEmpty(authKind))
2992 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4200)));
2995 string[] authKindParts = authKind.Split(':');
2996 if (authKindParts.Length > 2)
2998 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4201, authKind)));
3001 string localName;
3002 string prefix;
3003 string nameSpace;
3004 if (authKindParts.Length == 2)
3006 prefix = authKindParts[0];
3007 localName = authKindParts[1];
3009 else
3011 prefix = String.Empty;
3012 localName = authKindParts[0];
3015 nameSpace = reader.LookupNamespace(prefix);
3017 authorityBinding.AuthorityKind = new XmlQualifiedName(localName, nameSpace);
3019 authorityBinding.Binding = reader.GetAttribute(SamlConstants.AttributeNames.Binding, null);
3020 if (string.IsNullOrEmpty(authorityBinding.Binding))
3022 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4202)));
3025 authorityBinding.Location = reader.GetAttribute(SamlConstants.AttributeNames.Location, null);
3026 if (string.IsNullOrEmpty(authorityBinding.Location))
3028 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4203)));
3031 if (reader.IsEmptyElement)
3033 reader.MoveToContent();
3034 reader.Read();
3036 else
3038 reader.MoveToContent();
3039 reader.Read();
3040 reader.ReadEndElement();
3043 return authorityBinding;
3046 /// <summary>
3047 /// Serialize a SamlAuthorityBinding.
3048 /// </summary>
3049 /// <param name="writer">XmlWriter to serialize the SamlAuthorityBinding</param>
3050 /// <param name="authorityBinding">SamlAuthoriyBinding to be serialized.</param>
3051 protected virtual void WriteAuthorityBinding(XmlWriter writer, SamlAuthorityBinding authorityBinding)
3053 if (writer == null)
3055 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3058 if (authorityBinding == null)
3060 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
3063 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AuthorityBinding, SamlConstants.Namespace);
3065 string prefix = null;
3066 if (!string.IsNullOrEmpty(authorityBinding.AuthorityKind.Namespace))
3068 writer.WriteAttributeString(String.Empty, SamlConstants.AttributeNames.NamespaceAttributePrefix, null, authorityBinding.AuthorityKind.Namespace);
3069 prefix = writer.LookupPrefix(authorityBinding.AuthorityKind.Namespace);
3072 writer.WriteStartAttribute(SamlConstants.AttributeNames.AuthorityKind, null);
3073 if (string.IsNullOrEmpty(prefix))
3075 writer.WriteString(authorityBinding.AuthorityKind.Name);
3077 else
3079 writer.WriteString(prefix + ":" + authorityBinding.AuthorityKind.Name);
3081 writer.WriteEndAttribute();
3083 writer.WriteAttributeString(SamlConstants.AttributeNames.Location, null, authorityBinding.Location);
3085 writer.WriteAttributeString(SamlConstants.AttributeNames.Binding, null, authorityBinding.Binding);
3087 writer.WriteEndElement();
3090 /// <summary>
3091 /// Gets a boolean indicating if the SecurityTokenHandler can Serialize Tokens. Return true by default.
3092 /// </summary>
3093 public override bool CanWriteToken
3097 return true;
3101 /// <summary>
3102 /// Serializes the given SecurityToken to the XmlWriter.
3103 /// </summary>
3104 /// <param name="writer">XmlWriter into which the token is serialized.</param>
3105 /// <param name="token">SecurityToken to be serialized.</param>
3106 /// <exception cref="ArgumentNullException">Input parameter 'writer' or 'token' is null.</exception>
3107 /// <exception cref="SecurityTokenException">The given 'token' is not a SamlSecurityToken.</exception>
3108 public override void WriteToken(XmlWriter writer, SecurityToken token)
3110 if (writer == null)
3112 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3115 if (token == null)
3117 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
3120 SamlSecurityToken samlSecurityToken = token as SamlSecurityToken;
3121 if (samlSecurityToken == null)
3123 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4217, token.GetType(), typeof(SamlSecurityToken))));
3126 WriteAssertion(writer, samlSecurityToken.Assertion);
3129 /// <summary>
3130 /// Read the saml:AuthorizationDecisionStatement element.
3131 /// </summary>
3132 /// <param name="reader">XmlReader position at saml:AuthorizationDecisionStatement.</param>
3133 /// <returns>SamlAuthorizationDecisionStatement</returns>
3134 /// <exception cref="ArgumentNullException">The inpur parameter 'reader' is null.</exception>
3135 /// <exception cref="XmlException">The XmlReader is not positioned at a saml:AuthorizationDecisionStatement or
3136 /// the statement contains child elments that are unknown or invalid.</exception>
3137 protected virtual
3138 SamlAuthorizationDecisionStatement ReadAuthorizationDecisionStatement(XmlReader reader)
3140 if (reader == null)
3142 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3145 if (!reader.IsStartElement(SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace))
3147 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
3150 SamlAuthorizationDecisionStatement authzStatement = new SamlAuthorizationDecisionStatement();
3151 authzStatement.Resource = reader.GetAttribute(SamlConstants.AttributeNames.Resource, null);
3152 if (string.IsNullOrEmpty(authzStatement.Resource))
3154 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4205)));
3157 string decisionString = reader.GetAttribute(SamlConstants.AttributeNames.Decision, null);
3158 if (string.IsNullOrEmpty(decisionString))
3160 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4204)));
3163 if (decisionString.Equals(SamlAccessDecision.Deny.ToString(), StringComparison.OrdinalIgnoreCase))
3165 authzStatement.AccessDecision = SamlAccessDecision.Deny;
3167 else if (decisionString.Equals(SamlAccessDecision.Permit.ToString(), StringComparison.OrdinalIgnoreCase))
3169 authzStatement.AccessDecision = SamlAccessDecision.Permit;
3171 else
3173 authzStatement.AccessDecision = SamlAccessDecision.Indeterminate;
3176 reader.MoveToContent();
3177 reader.Read();
3179 if (reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
3181 authzStatement.SamlSubject = ReadSubject(reader);
3183 else
3185 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4206)));
3188 while (reader.IsStartElement())
3190 if (reader.IsStartElement(SamlConstants.ElementNames.Action, SamlConstants.Namespace))
3192 authzStatement.SamlActions.Add(ReadAction(reader));
3194 else if (reader.IsStartElement(SamlConstants.ElementNames.Evidence, SamlConstants.Namespace))
3196 if (authzStatement.Evidence != null)
3198 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4207)));
3201 authzStatement.Evidence = ReadEvidence(reader);
3203 else
3205 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4208, reader.LocalName, reader.NamespaceURI)));
3209 if (authzStatement.SamlActions.Count == 0)
3211 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4209)));
3214 reader.MoveToContent();
3215 reader.ReadEndElement();
3217 return authzStatement;
3220 /// <summary>
3221 /// Serialize a SamlAuthorizationDecisionStatement.
3222 /// </summary>
3223 /// <param name="writer">XmlWriter to which the SamlAuthorizationStatement is serialized.</param>
3224 /// <param name="statement">SamlAuthorizationDecisionStatement to serialize.</param>
3225 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'statement' is null.</exception>
3226 protected virtual void WriteAuthorizationDecisionStatement(XmlWriter writer, SamlAuthorizationDecisionStatement statement)
3228 if (writer == null)
3230 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3233 if (statement == null)
3235 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
3238 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace);
3240 writer.WriteAttributeString(SamlConstants.AttributeNames.Decision, null, statement.AccessDecision.ToString());
3242 writer.WriteAttributeString(SamlConstants.AttributeNames.Resource, null, statement.Resource);
3244 WriteSubject(writer, statement.SamlSubject);
3246 foreach (SamlAction action in statement.SamlActions)
3248 WriteAction(writer, action);
3251 if (statement.Evidence != null)
3253 WriteEvidence(writer, statement.Evidence);
3256 writer.WriteEndElement();
3259 /// <summary>
3260 /// Read the saml:Evidence element.
3261 /// </summary>
3262 /// <param name="reader">XmlReader positioned at saml:Evidence element.</param>
3263 /// <returns>SamlEvidence</returns>
3264 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
3265 /// <exception cref="XmlException">The XmlReader is not positioned at a saml:Evidence element or
3266 /// the element contains unrecognized or invalid child elements.</exception>
3267 protected virtual SamlEvidence ReadEvidence(XmlReader reader)
3269 if (reader == null)
3271 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3274 if (!reader.IsStartElement(SamlConstants.ElementNames.Evidence, SamlConstants.Namespace))
3276 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.Evidence, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
3279 SamlEvidence evidence = new SamlEvidence();
3280 reader.ReadStartElement();
3282 while (reader.IsStartElement())
3284 if (reader.IsStartElement(SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace))
3286 evidence.AssertionIdReferences.Add(reader.ReadElementString());
3288 else if (reader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace))
3290 evidence.Assertions.Add(ReadAssertion(reader));
3292 else
3294 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4210, reader.LocalName, reader.NamespaceURI)));
3298 if ((evidence.AssertionIdReferences.Count == 0) && (evidence.Assertions.Count == 0))
3300 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4211)));
3303 reader.MoveToContent();
3304 reader.ReadEndElement();
3306 return evidence;
3309 /// <summary>
3310 /// Serializes a given SamlEvidence.
3311 /// </summary>
3312 /// <param name="writer">XmlWriter to serialize the SamlEvidence.</param>
3313 /// <param name="evidence">SamlEvidence to be serialized.</param>
3314 /// <exception cref="ArgumentNullException">The input parameter 'evidence' is null.</exception>
3315 protected virtual void WriteEvidence(XmlWriter writer, SamlEvidence evidence)
3317 if (writer == null)
3319 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3322 if (evidence == null)
3324 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("evidence");
3327 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Evidence, SamlConstants.Namespace);
3329 for (int i = 0; i < evidence.AssertionIdReferences.Count; i++)
3331 writer.WriteElementString(SamlConstants.Prefix, SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace, evidence.AssertionIdReferences[i]);
3334 for (int i = 0; i < evidence.Assertions.Count; i++)
3336 WriteAssertion(writer, evidence.Assertions[i]);
3339 writer.WriteEndElement();
3342 /// <summary>
3343 /// Resolves the SecurityKeyIdentifier specified in a saml:Subject element.
3344 /// </summary>
3345 /// <param name="subjectKeyIdentifier">SecurityKeyIdentifier to resolve into a key.</param>
3346 /// <returns>SecurityKey</returns>
3347 /// <exception cref="ArgumentNullException">The input parameter 'subjectKeyIdentifier' is null.</exception>
3348 protected virtual SecurityKey ResolveSubjectKeyIdentifier(SecurityKeyIdentifier subjectKeyIdentifier)
3350 if (subjectKeyIdentifier == null)
3352 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subjectKeyIdentifier");
3355 if (this.Configuration == null)
3357 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
3360 if (this.Configuration.ServiceTokenResolver == null)
3362 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
3365 SecurityKey key = null;
3366 foreach (SecurityKeyIdentifierClause clause in subjectKeyIdentifier)
3368 if (this.Configuration.ServiceTokenResolver.TryResolveSecurityKey(clause, out key))
3370 return key;
3374 if (subjectKeyIdentifier.CanCreateKey)
3376 return subjectKeyIdentifier.CreateKey();
3379 return null;
3382 /// <summary>
3383 /// Resolves the Signing Key Identifier to a SecurityToken.
3384 /// </summary>
3385 /// <param name="assertion">The Assertion for which the Issuer token is to be resolved.</param>
3386 /// <param name="issuerResolver">The current SecurityTokenResolver associated with this handler.</param>
3387 /// <returns>Instance of SecurityToken</returns>
3388 /// <exception cref="ArgumentNullException">Input parameter 'assertion' is null.</exception>
3389 /// <exception cref="ArgumentNullException">Input parameter 'issuerResolver' is null.</exception>///
3390 /// <exception cref="SecurityTokenException">Unable to resolve token.</exception>
3391 protected virtual SecurityToken ResolveIssuerToken(SamlAssertion assertion, SecurityTokenResolver issuerResolver)
3393 if (null == assertion)
3395 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
3398 if (null == issuerResolver)
3400 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuerResolver");
3403 SecurityToken token;
3404 if (TryResolveIssuerToken(assertion, issuerResolver, out token))
3406 return token;
3408 else
3410 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4220)));
3414 /// <summary>
3415 /// Resolves the Signing Key Identifier to a SecurityToken.
3416 /// </summary>
3417 /// <param name="assertion">The Assertion for which the Issuer token is to be resolved.</param>
3418 /// <param name="issuerResolver">The current SecurityTokenResolver associated with this handler.</param>
3419 /// <param name="token">Resolved token.</param>
3420 /// <returns>True if token is resolved.</returns>
3421 /// <exception cref="ArgumentNullException">Input parameter 'assertion' is null.</exception>
3422 protected virtual bool TryResolveIssuerToken(SamlAssertion assertion, SecurityTokenResolver issuerResolver, out SecurityToken token)
3424 if (null == assertion)
3426 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
3429 if (assertion.SigningCredentials != null
3430 && assertion.SigningCredentials.SigningKeyIdentifier != null
3431 && issuerResolver != null)
3433 SecurityKeyIdentifier keyIdentifier = assertion.SigningCredentials.SigningKeyIdentifier;
3434 return issuerResolver.TryResolveToken(keyIdentifier, out token);
3436 else
3438 token = null;
3439 return false;
3443 /// <summary>
3444 /// Reads the ds:KeyInfo element inside the Saml Signature.
3445 /// </summary>
3446 /// <param name="reader">An XmlReader that can be positioned at a ds:KeyInfo element.</param>
3447 /// <param name="assertion">The assertion that is having the signature checked.</param>
3448 /// <returns>The <see cref="SecurityKeyIdentifier"/> that defines the key to use to check the signature.</returns>
3449 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
3450 /// <exception cref="InvalidOperationException">Unable to read the KeyIdentifier from the XmlReader.</exception>
3451 /// <remarks>If the reader is not positioned at a ds:KeyInfo element, the <see cref="SecurityKeyIdentifier"/> returned will
3452 /// contain a single <see cref="SecurityKeyIdentifierClause"/> of type <see cref="EmptySecurityKeyIdentifierClause"/></remarks>
3453 protected virtual SecurityKeyIdentifier ReadSigningKeyInfo(XmlReader reader, SamlAssertion assertion)
3455 if (reader == null)
3457 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3460 SecurityKeyIdentifier ski;
3462 if (KeyInfoSerializer.CanReadKeyIdentifier(reader))
3464 ski = KeyInfoSerializer.ReadKeyIdentifier(reader);
3466 else
3468 KeyInfo keyInfo = new KeyInfo(KeyInfoSerializer);
3469 keyInfo.ReadXml(XmlDictionaryReader.CreateDictionaryReader(reader));
3470 ski = keyInfo.KeyIdentifier;
3473 // no key info
3474 if (ski.Count == 0)
3476 return new SecurityKeyIdentifier(new SamlSecurityKeyIdentifierClause(assertion));
3479 return ski;
3482 /// <summary>
3483 /// Serializes the Signing SecurityKeyIdentifier.
3484 /// </summary>
3485 /// <param name="writer">XmlWriter to serialize the SecurityKeyIdentifier.</param>
3486 /// <param name="signingKeyIdentifier">Signing SecurityKeyIdentifier.</param>
3487 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'signingKeyIdentifier' is null.</exception>
3488 protected virtual void WriteSigningKeyInfo(XmlWriter writer, SecurityKeyIdentifier signingKeyIdentifier)
3490 if (writer == null)
3492 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3495 if (signingKeyIdentifier == null)
3497 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("signingKeyIdentifier");
3500 if (KeyInfoSerializer.CanWriteKeyIdentifier(signingKeyIdentifier))
3502 KeyInfoSerializer.WriteKeyIdentifier(writer, signingKeyIdentifier);
3503 return;
3506 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4221, signingKeyIdentifier));
3509 /// <summary>
3510 /// Validates the subject in each statement in the collection of Saml statements.
3511 /// </summary>
3512 /// <param name="statements"></param>
3513 private void ValidateStatements(IList<SamlStatement> statements)
3515 if (statements == null)
3517 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statements");
3519 List<SamlSubject> subjects = new List<SamlSubject>();
3520 foreach (SamlStatement statement in statements)
3522 if (statement is SamlAttributeStatement)
3524 subjects.Add((statement as SamlAttributeStatement).SamlSubject);
3527 if (statement is SamlAuthenticationStatement)
3529 subjects.Add((statement as SamlAuthenticationStatement).SamlSubject);
3532 if (statement is SamlAuthorizationDecisionStatement)
3534 subjects.Add((statement as SamlAuthorizationDecisionStatement).SamlSubject);
3537 // skip all custom statements
3542 if (subjects.Count == 0)
3545 // All statements are custom and we cannot validate
3547 return;
3549 string requiredSubjectName = subjects[0].Name;
3550 string requiredSubjectFormat = subjects[0].NameFormat;
3551 string requiredSubjectQualifier = subjects[0].NameQualifier;
3553 foreach (SamlSubject subject in subjects)
3555 if (!StringComparer.Ordinal.Equals(subject.Name, requiredSubjectName) ||
3556 !StringComparer.Ordinal.Equals(subject.NameFormat, requiredSubjectFormat) ||
3557 !StringComparer.Ordinal.Equals(subject.NameQualifier, requiredSubjectQualifier))
3560 // The SamlSubjects in the statements do not match
3562 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4225, subject));
3567 #endregion
3569 /// <summary>
3570 /// Returns the saml token's token type that is supported by this handler.
3571 /// </summary>
3572 public override string[] GetTokenTypeIdentifiers()
3574 return _tokenTypeIdentifiers;
3577 /// <summary>
3578 /// Gets or Sets a SecurityTokenSerializers that will be used to serialize and deserializer
3579 /// SecurtyKeyIdentifier. For example, SamlSubject SecurityKeyIdentifier or Signature
3580 /// SecurityKeyIdentifier.
3581 /// </summary>
3582 public SecurityTokenSerializer KeyInfoSerializer
3586 if (_keyInfoSerializer == null)
3588 lock (_syncObject)
3590 if (_keyInfoSerializer == null)
3592 SecurityTokenHandlerCollection sthc = (ContainingCollection != null) ?
3593 ContainingCollection : SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
3594 _keyInfoSerializer = new SecurityTokenSerializerAdapter(sthc);
3599 return _keyInfoSerializer;
3603 if (value == null)
3605 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
3608 _keyInfoSerializer = value;
3612 /// <summary>
3613 /// Gets the System.Type of the SecurityToken is supported by ththis handler.
3614 /// </summary>
3615 public override Type TokenType
3617 get { return typeof(SamlSecurityToken); }
3620 /// <summary>
3621 /// Gets or sets the <see cref="SamlSecurityTokenRequirement"/>
3622 /// </summary>
3623 public SamlSecurityTokenRequirement SamlSecurityTokenRequirement
3627 return _samlSecurityTokenRequirement;
3631 if (value == null)
3633 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
3635 _samlSecurityTokenRequirement = value;
3639 // This thin wrapper is used to pass a serializer down into the
3640 // EnvelopedSignatureReader that will use the Saml11SecurityTokenHandlers's
3641 // ReadKeyInfo method to read the KeyInfo.
3642 class WrappedSerializer : SecurityTokenSerializer
3644 SamlSecurityTokenHandler _parent;
3645 SamlAssertion _assertion;
3647 public WrappedSerializer(SamlSecurityTokenHandler parent, SamlAssertion assertion)
3649 if (parent == null)
3651 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent");
3654 _parent = parent;
3655 _assertion = assertion;
3658 protected override bool CanReadKeyIdentifierClauseCore(XmlReader reader)
3660 return false;
3663 protected override bool CanReadKeyIdentifierCore(XmlReader reader)
3665 return true;
3668 protected override bool CanReadTokenCore(XmlReader reader)
3670 return false;
3673 protected override bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause)
3675 return false;
3678 protected override bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier)
3680 return false;
3683 protected override bool CanWriteTokenCore(SecurityToken token)
3685 return false;
3688 protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
3690 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3693 protected override SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader)
3695 return _parent.ReadSigningKeyInfo(reader, _assertion);
3698 protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
3700 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3703 protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
3705 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3708 protected override void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier)
3710 _parent.WriteSigningKeyInfo(writer, keyIdentifier);
3713 protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
3715 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());