0fbf94a87c6f3b563250f16a1e72fbb44ba6398c
[mono.git] / mcs / class / referencesource / System.IdentityModel / System / IdentityModel / Tokens / SamlSecurityTokenHandler.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.IdentityModel.Tokens
6 {
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;
26
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
32     {
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
39
40         const string Attribute = "saml:Attribute";
41         const string Actor = "Actor";
42         const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
43
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);
50
51         static string[] _tokenTypeIdentifiers = new string[] { SecurityTokenTypes.SamlTokenProfile11, SecurityTokenTypes.OasisWssSamlTokenProfile11 };
52
53         SamlSecurityTokenRequirement _samlSecurityTokenRequirement;
54
55         SecurityTokenSerializer _keyInfoSerializer;
56
57         object _syncObject = new object();
58
59         /// <summary>
60         /// Initializes an instance of <see cref="SamlSecurityTokenHandler"/>
61         /// </summary>
62         public SamlSecurityTokenHandler()
63             : this(new SamlSecurityTokenRequirement())
64         {
65         }
66
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)
72         {
73             if (samlSecurityTokenRequirement == null)
74             {
75                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSecurityTokenRequirement");
76             }
77             _samlSecurityTokenRequirement = samlSecurityTokenRequirement;
78         }
79
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)
87         {
88             if (customConfigElements == null)
89             {
90                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfigElements");
91             }
92
93             List<XmlElement> configNodes = XmlUtil.GetXmlElements(customConfigElements);
94
95             bool foundValidConfig = false;
96
97             foreach (XmlElement configElement in configNodes)
98             {
99                 if (configElement.LocalName != ConfigurationStrings.SamlSecurityTokenRequirement)
100                 {
101                     continue;
102                 }
103
104                 if (foundValidConfig)
105                 {
106                     throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7026, ConfigurationStrings.SamlSecurityTokenRequirement));
107                 }
108
109                 _samlSecurityTokenRequirement = new SamlSecurityTokenRequirement(configElement);
110
111                 foundValidConfig = true;
112             }
113
114             if (!foundValidConfig)
115             {
116                 _samlSecurityTokenRequirement = new SamlSecurityTokenRequirement();
117             }
118         }
119
120         #region TokenCreation
121
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)
128         {
129             if (tokenDescriptor == null)
130             {
131                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
132             }
133
134             IEnumerable<SamlStatement> statements = CreateStatements(tokenDescriptor);
135
136             // - NotBefore / NotAfter
137             // - Audience Restriction
138             SamlConditions conditions = CreateConditions(tokenDescriptor.Lifetime, tokenDescriptor.AppliesToAddress, tokenDescriptor);
139
140             SamlAdvice advice = CreateAdvice(tokenDescriptor);
141
142             string issuerName = tokenDescriptor.TokenIssuerName;
143
144             SamlAssertion assertion = CreateAssertion(issuerName, conditions, advice, statements);
145             if (assertion == null)
146             {
147                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4013)));
148             }
149
150             assertion.SigningCredentials = GetSigningCredentials(tokenDescriptor);
151
152             SecurityToken token = new SamlSecurityToken(assertion);
153
154             //
155             // Encrypt the token if encrypting credentials are set
156             //
157
158             EncryptingCredentials encryptingCredentials = GetEncryptingCredentials(tokenDescriptor);
159             if (encryptingCredentials != null)
160             {
161                 token = new EncryptedSecurityToken(token, encryptingCredentials);
162             }
163
164             return token;
165         }
166
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)
176         {
177             if (null == tokenDescriptor)
178             {
179                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
180             }
181
182             EncryptingCredentials encryptingCredentials = null;
183
184             if (null != tokenDescriptor.EncryptingCredentials)
185             {
186                 encryptingCredentials = tokenDescriptor.EncryptingCredentials;
187
188                 if (encryptingCredentials.SecurityKey is AsymmetricSecurityKey)
189                 {
190                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
191                                         new SecurityTokenException(SR.GetString(SR.ID4178)));
192                 }
193             }
194
195             return encryptingCredentials;
196         }
197
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)
206         {
207             if (null == tokenDescriptor)
208             {
209                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
210             }
211
212             return tokenDescriptor.SigningCredentials;
213         }
214
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)
221         {
222             return null;
223         }
224
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)
236         {
237             return new SamlAssertion(System.IdentityModel.UniqueId.CreateRandomId(), issuer, DateTime.UtcNow, conditions, advice, statements);
238         }
239
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)
248         {
249             if (token == null)
250             {
251                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
252             }
253
254             return token.CreateKeyIdentifierClause<SamlAssertionKeyIdentifierClause>();
255         }
256
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)
270         {
271             SamlConditions conditions = new SamlConditions();
272             if (tokenLifetime != null)
273             {
274                 if (tokenLifetime.Created != null)
275                 {
276                     conditions.NotBefore = tokenLifetime.Created.Value;
277                 }
278
279                 if (tokenLifetime.Expires != null)
280                 {
281                     conditions.NotOnOrAfter = tokenLifetime.Expires.Value;
282                 }
283             }
284
285             if (!string.IsNullOrEmpty(relyingPartyAddress))
286             {
287                 conditions.Conditions.Add(new SamlAudienceRestrictionCondition(new Uri[] { new Uri(relyingPartyAddress) }));
288             }
289
290             return conditions;
291         }
292
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)
307         {
308             Collection<SamlStatement> statements = new Collection<SamlStatement>();
309
310             SamlSubject subject = CreateSamlSubject(tokenDescriptor);
311             SamlAttributeStatement attributeStatement = CreateAttributeStatement(subject, tokenDescriptor.Subject, tokenDescriptor);
312             if (attributeStatement != null)
313             {
314                 statements.Add(attributeStatement);
315             }
316
317             SamlAuthenticationStatement authnStatement = CreateAuthenticationStatement(subject, tokenDescriptor.AuthenticationInfo, tokenDescriptor);
318             if (authnStatement != null)
319             {
320                 statements.Add(authnStatement);
321             }
322
323             return statements;
324         }
325
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)
339         {
340             if (samlSubject == null)
341             {
342                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSubject");
343             }
344
345             if (tokenDescriptor == null)
346             {
347                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
348             }
349
350             if (tokenDescriptor.Subject == null)
351             {
352                 return null;
353             }
354             string authenticationMethod = null;
355             string authenticationInstant = null;
356
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)
362             {
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;
367             }
368
369             claimCollection = (from c in tokenDescriptor.Subject.Claims
370                                where c.Type == ClaimTypes.AuthenticationInstant
371                                select c);
372             if (claimCollection.Count<Claim>() > 0)
373             {
374                 authenticationInstant = claimCollection.First<Claim>().Value;
375             }
376
377             if (authenticationMethod == null && authenticationInstant == null)
378             {
379                 return null;
380             }
381             else if (authenticationMethod == null)
382             {
383                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationMethod", "SAML11"));
384             }
385             else if (authenticationInstant == null)
386             {
387                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationInstant", "SAML11"));
388             }
389
390             DateTime authInstantTime = DateTime.ParseExact(authenticationInstant,
391                                                             DateTimeFormats.Accepted,
392                                                             DateTimeFormatInfo.InvariantInfo,
393                                                             DateTimeStyles.None).ToUniversalTime();
394             if (authInfo == null)
395             {
396                 return new SamlAuthenticationStatement(samlSubject, DenormalizeAuthenticationType(authenticationMethod), authInstantTime, null, null, null);
397             }
398             else
399             {
400                 return new SamlAuthenticationStatement(samlSubject, DenormalizeAuthenticationType(authenticationMethod), authInstantTime, authInfo.DnsName, authInfo.Address, null);
401             }
402         }
403
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)
420         {
421             if (subject == null)
422             {
423                 return null;
424             }
425
426             if (samlSubject == null)
427             {
428                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSubject");
429             }
430
431             if (subject.Claims != null)
432             {
433
434                 List<SamlAttribute> attributes = new List<SamlAttribute>();
435                 foreach (Claim claim in subject.Claims)
436                 {
437                     if (claim != null && claim.Type != ClaimTypes.NameIdentifier)
438                     {
439                         //
440                         // NameIdentifier claim is already processed while creating the samlsubject
441                         // AuthenticationInstant and AuthenticationType are not converted to Claims
442                         //
443                         switch (claim.Type)
444                         {
445                             case ClaimTypes.AuthenticationInstant:
446                             case ClaimTypes.AuthenticationMethod:
447                                 break;
448                             default:
449                                 attributes.Add(CreateAttribute(claim, tokenDescriptor));
450                                 break;
451                         }
452                     }
453                 }
454
455                 AddDelegateToAttributes(subject, attributes, tokenDescriptor);
456
457                 ICollection<SamlAttribute> collectedAttributes = CollectAttributeValues(attributes);
458                 if (collectedAttributes.Count > 0)
459                 {
460                     return new SamlAttributeStatement(samlSubject, collectedAttributes);
461                 }
462             }
463
464             return null;
465         }
466
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)
474         {
475             Dictionary<SamlAttributeKeyComparer.AttributeKey, SamlAttribute> distinctAttributes = new Dictionary<SamlAttributeKeyComparer.AttributeKey, SamlAttribute>(attributes.Count, new SamlAttributeKeyComparer());
476
477             foreach (SamlAttribute attribute in attributes)
478             {
479                 SamlAttribute SamlAttribute = attribute as SamlAttribute;
480                 if (SamlAttribute != null)
481                 {
482                     // Use unique attribute if name, value type, or issuer differ
483                     SamlAttributeKeyComparer.AttributeKey attributeKey = new SamlAttributeKeyComparer.AttributeKey(SamlAttribute);
484
485                     if (distinctAttributes.ContainsKey(attributeKey))
486                     {
487                         foreach (string attributeValue in SamlAttribute.AttributeValues)
488                         {
489                             distinctAttributes[attributeKey].AttributeValues.Add(attributeValue);
490                         }
491                     }
492                     else
493                     {
494                         distinctAttributes.Add(attributeKey, SamlAttribute);
495                     }
496                 }
497             }
498
499             return distinctAttributes.Values;
500         }
501
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)
512         {
513             if (subject == null)
514             {
515                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
516             }
517             if (tokenDescriptor == null)
518             {
519                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
520             }
521             if (subject.Actor == null)
522             {
523                 return;
524             }
525
526             List<SamlAttribute> actingAsAttributes = new List<SamlAttribute>();
527
528             foreach (Claim claim in subject.Actor.Claims)
529             {
530                 if (claim != null)
531                 {
532                     actingAsAttributes.Add(CreateAttribute(claim, tokenDescriptor));
533                 }
534             }
535
536             // perform depth first recursion
537             AddDelegateToAttributes(subject.Actor, actingAsAttributes, tokenDescriptor);
538
539             ICollection<SamlAttribute> collectedAttributes = CollectAttributeValues(actingAsAttributes);
540             attributes.Add(CreateAttribute(new Claim(ClaimTypes.Actor, CreateXmlStringFromAttributes(collectedAttributes), ClaimValueTypes.String), tokenDescriptor));
541         }
542
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)
557         {
558             if (tokenDescriptor == null)
559             {
560                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
561             }
562
563             SamlSubject samlSubject = new SamlSubject();
564
565             Claim identityClaim = null;
566             if (tokenDescriptor.Subject != null && tokenDescriptor.Subject.Claims != null)
567             {
568                 foreach (Claim claim in tokenDescriptor.Subject.Claims)
569                 {
570                     if (claim.Type == ClaimTypes.NameIdentifier)
571                     {
572                         // Do not allow multiple name identifier claim.
573                         if (null != identityClaim)
574                         {
575                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
576                                 new InvalidOperationException(SR.GetString(SR.ID4139)));
577                         }
578                         identityClaim = claim;
579                     }
580                 }
581             }
582
583             if (identityClaim != null)
584             {
585                 samlSubject.Name = identityClaim.Value;
586
587                 if (identityClaim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierFormat))
588                 {
589                     samlSubject.NameFormat = identityClaim.Properties[ClaimProperties.SamlNameIdentifierFormat];
590                 }
591
592                 if (identityClaim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierNameQualifier))
593                 {
594                     samlSubject.NameQualifier = identityClaim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier];
595                 }
596             }
597
598             if (tokenDescriptor.Proof != null)
599             {
600                 //
601                 // Add the key and the Holder-Of-Key confirmation method
602                 // for both symmetric and asymmetric key case
603                 //
604                 samlSubject.KeyIdentifier = tokenDescriptor.Proof.KeyIdentifier;
605                 samlSubject.ConfirmationMethods.Add(SamlConstants.HolderOfKey);
606             }
607             else
608             {
609                 //
610                 // This is a bearer token
611                 //
612                 samlSubject.ConfirmationMethods.Add(BearerConfirmationMethod);
613             }
614
615             return samlSubject;
616         }
617
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)
625         {
626             bool actorElementWritten = false;
627
628             using (MemoryStream ms = new MemoryStream())
629             {
630                 using (XmlDictionaryWriter dicWriter = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8, false))
631                 {
632                     foreach (SamlAttribute samlAttribute in attributes)
633                     {
634                         if (samlAttribute != null)
635                         {
636                             if (!actorElementWritten)
637                             {
638                                 dicWriter.WriteStartElement(Actor);
639                                 actorElementWritten = true;
640                             }
641                             WriteAttribute(dicWriter, samlAttribute);
642                         }
643                     }
644
645                     if (actorElementWritten)
646                     {
647                         dicWriter.WriteEndElement();
648                     }
649
650                     dicWriter.Flush();
651                 }
652                 return Encoding.UTF8.GetString(ms.ToArray());
653             }
654         }
655
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)
664         {
665             if (claim == null)
666             {
667                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim");
668             }
669
670             int lastSlashIndex = claim.Type.LastIndexOf('/');
671             string attributeNamespace = null;
672             string attributeName = null;
673
674             if ((lastSlashIndex == 0) || (lastSlashIndex == -1))
675             {
676                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.GetString(SR.ID4216, claim.Type));
677             }
678             else if (lastSlashIndex == claim.Type.Length - 1)
679             {
680                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.GetString(SR.ID4216, claim.Type));
681             }
682             else
683             {
684                 attributeNamespace = claim.Type.Substring(0, lastSlashIndex);
685                 //
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. 
688                 //
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.
692                 //
693                 // Send     Receive     Behavior
694                 // =============================
695                 // WCF      WCF         Works as expected
696                 //
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.
699                 //
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).
702                 //
703                 // IDFX     IDFX        Works as expected
704                 //
705                 if (attributeNamespace.EndsWith("/", StringComparison.Ordinal))
706                 {
707                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claim", SR.GetString(SR.ID4213, claim.Type));
708                 }
709                 attributeName = claim.Type.Substring(lastSlashIndex + 1, claim.Type.Length - (lastSlashIndex + 1));
710             }
711
712             SamlAttribute attribute = new SamlAttribute(attributeNamespace, attributeName, new string[] { claim.Value });
713             if (!StringComparer.Ordinal.Equals(ClaimsIdentity.DefaultIssuer, claim.OriginalIssuer))
714             {
715                 attribute.OriginalIssuer = claim.OriginalIssuer;
716             }
717             attribute.AttributeValueXsiType = claim.ValueType;
718
719             return attribute;
720         }
721
722         #endregion
723
724         #region TokenValidation
725
726         /// <summary>
727         /// Returns value indicates if this handler can validate tokens of type
728         /// SamlSecurityToken.
729         /// </summary>
730         public override bool CanValidateToken
731         {
732             get { return true; }
733         }
734
735         /// <summary>
736         /// Gets or sets the X509CeritificateValidator that is used by the current instance.
737         /// </summary>
738         public X509CertificateValidator CertificateValidator
739         {
740             get
741             {
742                 if (_samlSecurityTokenRequirement.CertificateValidator == null)
743                 {
744                     if (Configuration != null)
745                     {
746                         return Configuration.CertificateValidator;
747                     }
748                     else
749                     {
750                         return null;
751                     }
752                 }
753                 else
754                 {
755                     return _samlSecurityTokenRequirement.CertificateValidator;
756                 }
757             }
758             set
759             {
760                 _samlSecurityTokenRequirement.CertificateValidator = value;
761             }
762         }
763
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)
774         {
775             if (token == null)
776             {
777                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
778             }
779
780             SamlSecurityToken samlToken = token as SamlSecurityToken;
781             if (null == samlToken)
782             {
783                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1067, token.GetType().ToString()));
784             }
785
786             //
787             // by default we only check bearer tokens.
788             //
789
790             if (samlToken.SecurityKeys.Count != 0)
791             {
792                 return;
793             }
794
795             if (Configuration == null)
796             {
797                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
798             }
799
800             if (Configuration.Caches.TokenReplayCache == null)
801             {
802                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4278));
803             }
804
805             if (string.IsNullOrEmpty(samlToken.Assertion.AssertionId))
806             {
807                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID1063)));
808             }
809
810             StringBuilder stringBuilder = new StringBuilder();
811
812             string key;
813
814             using (HashAlgorithm hashAlgorithm = CryptoHelper.NewSha256HashAlgorithm())
815             {
816                 if (string.IsNullOrEmpty(samlToken.Assertion.Issuer))
817                 {
818                     stringBuilder.AppendFormat("{0}{1}", samlToken.Assertion.AssertionId, _tokenTypeIdentifiers[0]);
819                 }
820                 else
821                 {
822                     stringBuilder.AppendFormat("{0}{1}{2}", samlToken.Assertion.AssertionId, samlToken.Assertion.Issuer, _tokenTypeIdentifiers[0]);
823                 }
824
825                 key = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString())));
826             }
827
828             if (Configuration.Caches.TokenReplayCache.Contains(key))
829             {
830                 if (string.IsNullOrEmpty(samlToken.Assertion.Issuer))
831                 {
832                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
833                              new SecurityTokenReplayDetectedException(SR.GetString(SR.ID1062, typeof(SamlSecurityToken).ToString(), samlToken.Assertion.AssertionId, "")));
834                 }
835                 else
836                 {
837                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
838                             new SecurityTokenReplayDetectedException(SR.GetString(SR.ID1062, typeof(SamlSecurityToken).ToString(), samlToken.Assertion.AssertionId, samlToken.Assertion.Issuer)));
839                 }
840             }
841             else
842             {
843                 Configuration.Caches.TokenReplayCache.AddOrUpdate(key, token, DateTimeUtil.Add(GetTokenReplayCacheEntryExpirationTime(samlToken), Configuration.MaxClockSkew));
844             }
845         }
846
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)
855         {
856             if (token == null)
857             {
858                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
859             }
860
861             //
862             //  DateTimeUtil handles overflows
863             //
864             DateTime maximumExpirationTime = DateTimeUtil.Add(DateTime.UtcNow, Configuration.TokenReplayCacheExpirationPeriod);
865
866             // If the token validity period is greater than the TokenReplayCacheExpirationPeriod, throw
867             if (DateTime.Compare(maximumExpirationTime, token.ValidTo) < 0)
868             {
869                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
870                     new SecurityTokenValidationException(SR.GetString(SR.ID1069, token.ValidTo.ToString(), Configuration.TokenReplayCacheExpirationPeriod.ToString())));
871             }
872
873             return token.ValidTo;
874         }
875
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)
888         {
889             if (null != conditions)
890             {
891                 DateTime now = DateTime.UtcNow;
892
893                 if (null != conditions.NotBefore
894                     && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < conditions.NotBefore)
895                 {
896                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
897                         new SecurityTokenNotYetValidException(SR.GetString(SR.ID4222, conditions.NotBefore, now)));
898                 }
899
900                 if (null != conditions.NotOnOrAfter
901                     && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= conditions.NotOnOrAfter)
902                 {
903                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
904                         new SecurityTokenExpiredException(SR.GetString(SR.ID4223, conditions.NotOnOrAfter, now)));
905                 }
906             }
907
908             //
909             // Enforce the audience restriction
910             //
911             if (enforceAudienceRestriction)
912             {
913                 if (this.Configuration == null || this.Configuration.AudienceRestriction.AllowedAudienceUris.Count == 0)
914                 {
915                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID1032)));
916                 }
917
918                 //
919                 // Process each condition, enforcing the AudienceRestrictionConditions
920                 //
921                 bool foundAudienceRestriction = false;
922
923                 if (null != conditions && null != conditions.Conditions)
924                 {
925                     foreach (SamlCondition condition in conditions.Conditions)
926                     {
927                         SamlAudienceRestrictionCondition audienceRestriction = condition as SamlAudienceRestrictionCondition;
928                         if (null == audienceRestriction)
929                         {
930                             // Skip other conditions
931                             continue;
932                         }
933
934                         _samlSecurityTokenRequirement.ValidateAudienceRestriction(this.Configuration.AudienceRestriction.AllowedAudienceUris, audienceRestriction.Audiences);
935                         foundAudienceRestriction = true;
936                     }
937                 }
938
939                 if (!foundAudienceRestriction)
940                 {
941                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException(SR.GetString(SR.ID1035)));
942                 }
943             }
944         }
945
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)
958         {
959             if (token == null)
960             {
961                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
962             }
963
964             SamlSecurityToken samlToken = token as SamlSecurityToken;
965             if (samlToken == null)
966             {
967                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1033, token.GetType().ToString()));
968             }
969
970             if (this.Configuration == null)
971             {
972                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
973             }
974
975             try
976             {
977                 if (samlToken.Assertion == null)
978                 {
979                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1034));
980                 }
981
982                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.Diagnostics, SR.GetString(SR.TraceValidateToken), new SecurityTraceRecordHelper.TokenTraceRecord(token), null, null);
983
984                 // Ensure token was signed and verified at some point
985                 if (samlToken.Assertion.SigningToken == null)
986                 {
987                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4220)));
988                 }
989
990                 this.ValidateConditions(samlToken.Assertion.Conditions, _samlSecurityTokenRequirement.ShouldEnforceAudienceRestriction(this.Configuration.AudienceRestriction.AudienceMode, samlToken));
991
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)
995                 {
996                     this.DetectReplayedToken(samlToken);
997                 }
998
999                 //
1000                 // If the backing token is x509, validate trust
1001                 //
1002                 X509SecurityToken x509IssuerToken = samlToken.Assertion.SigningToken as X509SecurityToken;
1003                 if (x509IssuerToken != null)
1004                 {
1005                     try
1006                     {
1007                         CertificateValidator.Validate(x509IssuerToken.Certificate);
1008                     }
1009                     catch (SecurityTokenValidationException e)
1010                     {
1011                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4257,
1012                                 X509Util.GetCertificateId(x509IssuerToken.Certificate)), e));
1013                     }
1014                 }
1015
1016                 //
1017                 // Create the claims
1018                 //
1019                 ClaimsIdentity claimsIdentity = CreateClaims(samlToken);
1020
1021                 if (_samlSecurityTokenRequirement.MapToWindows)
1022                 {
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));
1026
1027                     // PARTIAL TRUST: will fail when adding claims, AddClaims is SecurityCritical.
1028                     windowsIdentity.AddClaims(claimsIdentity.Claims);
1029                     claimsIdentity = windowsIdentity;
1030                 }
1031
1032                 if (this.Configuration.SaveBootstrapContext)
1033                 {
1034                     claimsIdentity.BootstrapContext = new BootstrapContext(token, this);
1035                 }
1036
1037                 this.TraceTokenValidationSuccess(token);
1038
1039                 List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1);
1040                 identities.Add(claimsIdentity);
1041                 return identities.AsReadOnly();
1042             }
1043             catch (Exception e)
1044             {
1045                 if (Fx.IsFatal(e))
1046                 {
1047                     throw;
1048                 }
1049
1050                 this.TraceTokenValidationFailure(token, e.Message);
1051                 throw e;
1052             }
1053         }
1054
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)
1062         {
1063             if (string.IsNullOrEmpty(upn))
1064             {
1065                 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("upn");
1066             }
1067
1068             WindowsIdentity wi = new WindowsIdentity(upn);
1069
1070             return new WindowsIdentity(wi.Token, AuthenticationTypes.Federation, WindowsAccountType.Normal, true);
1071         }
1072         
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)
1082         {
1083             return ClaimsHelper.FindUpn(claimsIdentity);
1084         }
1085
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)
1102         {
1103             if (samlSecurityToken == null)
1104             {
1105                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSecurityToken");
1106             }
1107
1108             if (samlSecurityToken.Assertion == null)
1109             {
1110                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("samlSecurityToken", SR.GetString(SR.ID1034));
1111             }
1112
1113             //
1114             // Construct the subject and issuer identities.
1115             // Use claim types specified in the security token requirements used for IPrincipal.Role and IIdentity.Name 
1116             //
1117             ClaimsIdentity subject = new ClaimsIdentity(AuthenticationTypes.Federation,
1118                                                          _samlSecurityTokenRequirement.NameClaimType,
1119                                                          _samlSecurityTokenRequirement.RoleClaimType);
1120
1121             string issuer = null;
1122
1123             if (this.Configuration == null)
1124             {
1125                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1126             }
1127
1128             if (this.Configuration.IssuerNameRegistry == null)
1129             {
1130                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4277));
1131             }
1132
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);
1137
1138             if (string.IsNullOrEmpty(issuer))
1139             {
1140                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4175)));
1141             }
1142
1143             ProcessStatement(samlSecurityToken.Assertion.Statements, subject, issuer);
1144             return subject;
1145         }
1146
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)
1153         {
1154             return AuthenticationTypeMaps.Denormalize(normalizedAuthenticationType, AuthenticationTypeMaps.Saml);
1155         }
1156
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)
1163         {
1164             return AuthenticationTypeMaps.Normalize(saml11AuthenticationMethod, AuthenticationTypeMaps.Saml);
1165         }
1166
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)
1174         {
1175             if (statements == null)
1176             {
1177                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statements");
1178             }
1179
1180             Collection<SamlAuthenticationStatement> authStatementCollection = new Collection<SamlAuthenticationStatement>();
1181
1182             //
1183             // Validate that the Saml subjects in all the statements are the same.
1184             //
1185             ValidateStatements(statements);
1186
1187             foreach (SamlStatement samlStatement in statements)
1188             {
1189                 SamlAttributeStatement attrStatement = samlStatement as SamlAttributeStatement;
1190                 if (attrStatement != null)
1191                 {
1192                     ProcessAttributeStatement(attrStatement, subject, issuer);
1193                 }
1194                 else
1195                 {
1196                     SamlAuthenticationStatement authenStatement = samlStatement as SamlAuthenticationStatement;
1197                     if (authenStatement != null)
1198                     {
1199                         authStatementCollection.Add(authenStatement);
1200                     }
1201                     else
1202                     {
1203                         SamlAuthorizationDecisionStatement decisionStatement = samlStatement as SamlAuthorizationDecisionStatement;
1204                         if (decisionStatement != null)
1205                         {
1206                             ProcessAuthorizationDecisionStatement(decisionStatement, subject, issuer);
1207                         }
1208                         else
1209                         {
1210                             // We don't process custom statements. Just fall through.
1211                         }
1212                     }
1213                 }
1214             }
1215
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)
1219             {
1220                 if (authStatement != null)
1221                 {
1222                     ProcessAuthenticationStatement(authStatement, subject, issuer);
1223                 }
1224             }
1225         }
1226
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)
1235         {
1236             if (samlStatement == null)
1237             {
1238                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlStatement");
1239             }
1240
1241             if (subject == null)
1242             {
1243                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1244             }
1245
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);
1250
1251             foreach (SamlAttribute attr in samlStatement.Attributes)
1252             {
1253                 string claimType = null;
1254                 if (string.IsNullOrEmpty(attr.Namespace))
1255                 {
1256                     claimType = attr.Name;
1257                 }
1258                 else if (StringComparer.Ordinal.Equals(attr.Name, SamlConstants.ElementNames.NameIdentifier))
1259                 {
1260                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID4094)));
1261                 }
1262                 else
1263                 {
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)))
1268                     {
1269                         claimType = attr.Namespace + "/" + attr.Name;
1270                     }
1271                     else
1272                     {
1273                         claimType = attr.Namespace + attr.Name;
1274                     }
1275
1276                 }
1277
1278                 if (claimType == ClaimTypes.Actor)
1279                 {
1280                     if (subject.Actor != null)
1281                     {
1282                         throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4034));
1283                     }
1284
1285                     SetDelegateFromAttribute(attr, subject, issuer);
1286                 }
1287                 else
1288                 {
1289                     for (int k = 0; k < attr.AttributeValues.Count; ++k)
1290                     {
1291                         // Check if we already have a nameId claim.
1292                         if (StringComparer.Ordinal.Equals(ClaimTypes.NameIdentifier, claimType) && GetClaim(subject, ClaimTypes.NameIdentifier) != null)
1293                         {
1294                             continue;
1295                         }
1296                         string originalIssuer = issuer;
1297                         SamlAttribute SamlAttribute = attr as SamlAttribute;
1298                         if ((SamlAttribute != null) && (SamlAttribute.OriginalIssuer != null))
1299                         {
1300                             originalIssuer = SamlAttribute.OriginalIssuer;
1301                         }
1302                         string claimValueType = ClaimValueTypes.String;
1303                         if (SamlAttribute != null)
1304                         {
1305                             claimValueType = SamlAttribute.AttributeValueXsiType;
1306                         }
1307                         subject.AddClaim(new Claim(claimType, attr.AttributeValues[k], claimValueType, issuer, originalIssuer));
1308                     }
1309                 }
1310             }
1311
1312         }
1313
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)
1321         {
1322             foreach (Claim claim in subject.Claims)
1323             {
1324                 if (StringComparer.Ordinal.Equals(claimType, claim.Type))
1325                 {
1326                     return claim;
1327                 }
1328             }
1329             return null;
1330         }
1331
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)
1348         {
1349             if (samlSubject == null)
1350             {
1351                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSubject");
1352             }
1353
1354             Claim nameIdentifierClaim = GetClaim(subject, ClaimTypes.NameIdentifier);
1355
1356             if (nameIdentifierClaim == null)
1357             {
1358                 // first saml subject. so we will create claims for this subject.
1359                 // subsequent subjects must have the same content.
1360
1361                 // add name identifier claim if present.
1362                 if (!string.IsNullOrEmpty(samlSubject.Name))
1363                 {
1364                     Claim claim = new Claim(ClaimTypes.NameIdentifier, samlSubject.Name, ClaimValueTypes.String, issuer);
1365
1366                     if (samlSubject.NameFormat != null)
1367                     {
1368                         claim.Properties[ClaimProperties.SamlNameIdentifierFormat] = samlSubject.NameFormat;
1369                     }
1370
1371                     if (samlSubject.NameQualifier != null)
1372                     {
1373                         claim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier] = samlSubject.NameQualifier;
1374                     }
1375
1376                     subject.AddClaim(claim);
1377                 }
1378             }
1379         }
1380
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)
1390         {
1391             if (samlStatement == null)
1392             {
1393                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlStatement");
1394             }
1395
1396             if (subject == null)
1397             {
1398                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1399             }
1400
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);
1405
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));
1408         }
1409
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)
1418         {
1419         }
1420
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)
1431         {
1432             // bail here nothing to add.
1433             if (subject == null || attribute == null || attribute.AttributeValues == null || attribute.AttributeValues.Count < 1)
1434             {
1435                 return;
1436             }
1437
1438             Collection<Claim> claims = new Collection<Claim>();
1439             SamlAttribute actingAsAttribute = null;
1440
1441             foreach (string attributeValue in attribute.AttributeValues)
1442             {
1443                 if (attributeValue != null && attributeValue.Length > 0)
1444                 {
1445
1446                     using (XmlDictionaryReader xmlReader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(attributeValue), XmlDictionaryReaderQuotas.Max))
1447                     {
1448                         xmlReader.MoveToContent();
1449                         xmlReader.ReadStartElement(Actor);
1450
1451                         while (xmlReader.IsStartElement(Attribute))
1452                         {
1453                             SamlAttribute innerAttribute = ReadAttribute(xmlReader);
1454                             if (innerAttribute != null)
1455                             {
1456                                 string claimType = string.IsNullOrEmpty(innerAttribute.Namespace) ? innerAttribute.Name : innerAttribute.Namespace + "/" + innerAttribute.Name;
1457                                 if (claimType == ClaimTypes.Actor)
1458                                 {
1459                                     // In this case we have two delegates acting as an identity, we do not allow this
1460                                     if (actingAsAttribute != null)
1461                                     {
1462                                         throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4034));
1463                                     }
1464
1465                                     actingAsAttribute = innerAttribute;
1466                                 }
1467                                 else
1468                                 {
1469                                     string claimValueType = ClaimValueTypes.String;
1470                                     string originalIssuer = null;
1471                                     SamlAttribute SamlAttribute = innerAttribute as SamlAttribute;
1472                                     if (SamlAttribute != null)
1473                                     {
1474                                         claimValueType = SamlAttribute.AttributeValueXsiType;
1475                                         originalIssuer = SamlAttribute.OriginalIssuer;
1476                                     }
1477                                     for (int k = 0; k < innerAttribute.AttributeValues.Count; ++k)
1478                                     {
1479                                         Claim claim = null;
1480                                         if (string.IsNullOrEmpty(originalIssuer))
1481                                         {
1482                                             claim = new Claim(claimType, innerAttribute.AttributeValues[k], claimValueType, issuer);
1483                                         }
1484                                         else
1485                                         {
1486                                             claim = new Claim(claimType, innerAttribute.AttributeValues[k], claimValueType, issuer, originalIssuer);
1487                                         }
1488                                         claims.Add(claim);
1489                                     }
1490                                 }
1491                             }
1492                         }
1493
1494                         xmlReader.ReadEndElement(); // Actor
1495                     }
1496                 }
1497             }
1498
1499             subject.Actor = new ClaimsIdentity(claims, AuthenticationTypes.Federation);
1500
1501             SetDelegateFromAttribute(actingAsAttribute, subject.Actor, issuer);
1502         }
1503
1504         #endregion
1505
1506         #region TokenSerialization
1507
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)
1516         {
1517             if (reader == null)
1518             {
1519                 return false;
1520             }
1521
1522             return reader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace);
1523         }
1524
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)
1533         {
1534             if (Configuration == null)
1535             {
1536                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1537             }
1538
1539             if (Configuration.IssuerTokenResolver == null)
1540             {
1541                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
1542             }
1543
1544             SamlAssertion assertion = ReadAssertion(reader);
1545             //
1546             // Resolve signing token if one is present. It may be deferred and signed by reference.
1547             //
1548             SecurityToken token;
1549
1550             TryResolveIssuerToken(assertion, Configuration.IssuerTokenResolver, out token);
1551
1552             assertion.SigningToken = token;
1553
1554             return new SamlSecurityToken(assertion);
1555         }
1556
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)
1565         {
1566             if (reader == null)
1567             {
1568                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1569             }
1570
1571             if (reader.IsStartElement(SamlConstants.ElementNames.Action, SamlConstants.Namespace))
1572             {
1573                 // The Namespace attribute is optional.
1574                 string ns = reader.GetAttribute(SamlConstants.AttributeNames.Namespace, null);
1575
1576                 reader.MoveToContent();
1577                 string action = reader.ReadString();
1578                 if (string.IsNullOrEmpty(action))
1579                 {
1580                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4073)));
1581                 }
1582
1583                 reader.MoveToContent();
1584                 reader.ReadEndElement();
1585
1586                 return new SamlAction(action, ns);
1587             }
1588             else
1589             {
1590                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Action, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
1591             }
1592         }
1593
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)
1601         {
1602             if (writer == null)
1603             {
1604                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1605             }
1606
1607             if (action == null)
1608             {
1609                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("action");
1610             }
1611
1612             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Action, SamlConstants.Namespace);
1613             if (!string.IsNullOrEmpty(action.Namespace))
1614             {
1615                 writer.WriteAttributeString(SamlConstants.AttributeNames.Namespace, null, action.Namespace);
1616             }
1617             writer.WriteString(action.Action);
1618             writer.WriteEndElement();
1619         }
1620
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)
1629         {
1630             if (reader == null)
1631             {
1632                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1633             }
1634
1635             if (!reader.IsStartElement(SamlConstants.ElementNames.Advice, SamlConstants.Namespace))
1636             {
1637                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Advice, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
1638             }
1639
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)
1643             {
1644                 // Just issue a read for the empty element.
1645                 reader.MoveToContent();
1646                 reader.Read();
1647                 return new SamlAdvice();
1648             }
1649
1650             reader.MoveToContent();
1651             reader.Read();
1652             Collection<string> assertionIdReferences = new Collection<string>();
1653             Collection<SamlAssertion> assertions = new Collection<SamlAssertion>();
1654             while (reader.IsStartElement())
1655             {
1656
1657                 if (reader.IsStartElement(SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace))
1658                 {
1659                     assertionIdReferences.Add(reader.ReadString());
1660                     reader.ReadEndElement();
1661                 }
1662                 else if (reader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace))
1663                 {
1664                     SamlAssertion assertion = ReadAssertion(reader);
1665                     assertions.Add(assertion);
1666                 }
1667                 else
1668                 {
1669                     TraceUtility.TraceString(TraceEventType.Warning, SR.GetString(SR.ID8005, reader.LocalName, reader.NamespaceURI));
1670                     reader.Skip();
1671                 }
1672
1673             }
1674
1675             reader.MoveToContent();
1676             reader.ReadEndElement();
1677
1678             return new SamlAdvice(assertionIdReferences, assertions);
1679         }
1680
1681
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)
1689         {
1690             if (writer == null)
1691             {
1692                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1693             }
1694
1695             if (advice == null)
1696             {
1697                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("advice");
1698             }
1699
1700             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Advice, SamlConstants.Namespace);
1701             if (advice.AssertionIdReferences.Count > 0)
1702             {
1703                 foreach (string assertionIdReference in advice.AssertionIdReferences)
1704                 {
1705                     if (string.IsNullOrEmpty(assertionIdReference))
1706                     {
1707                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4079)));
1708                     }
1709                     writer.WriteElementString(SamlConstants.Prefix, SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace, assertionIdReference);
1710                 }
1711             }
1712
1713             if (advice.Assertions.Count > 0)
1714             {
1715                 foreach (SamlAssertion assertion in advice.Assertions)
1716                 {
1717                     WriteAssertion(writer, assertion);
1718                 }
1719             }
1720
1721             writer.WriteEndElement();
1722         }
1723
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)
1733         {
1734             if (reader == null)
1735             {
1736                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1737             }
1738
1739             if (this.Configuration == null)
1740             {
1741                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1742             }
1743
1744             if (this.Configuration.IssuerTokenResolver == null)
1745             {
1746                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
1747             }
1748
1749             SamlAssertion assertion = new SamlAssertion();
1750
1751             EnvelopedSignatureReader wrappedReader = new EnvelopedSignatureReader(reader, new WrappedSerializer(this, assertion), this.Configuration.IssuerTokenResolver, false, true, false);
1752
1753
1754             if (!wrappedReader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace))
1755             {
1756                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Assertion, SamlConstants.Namespace, wrappedReader.LocalName, wrappedReader.NamespaceURI)));
1757             }
1758
1759             string attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.MajorVersion, null);
1760             if (string.IsNullOrEmpty(attributeValue))
1761             {
1762                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.MajorVersion)));
1763             }
1764
1765             int majorVersion = XmlConvert.ToInt32(attributeValue);
1766
1767             attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.MinorVersion, null);
1768             if (string.IsNullOrEmpty(attributeValue))
1769             {
1770                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.MinorVersion)));
1771             }
1772
1773             int minorVersion = XmlConvert.ToInt32(attributeValue);
1774
1775             if ((majorVersion != SamlConstants.MajorVersionValue) || (minorVersion != SamlConstants.MinorVersionValue))
1776             {
1777                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4076, majorVersion, minorVersion, SamlConstants.MajorVersionValue, SamlConstants.MinorVersionValue)));
1778             }
1779
1780             attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.AssertionId, null);
1781             if (string.IsNullOrEmpty(attributeValue))
1782             {
1783                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.AssertionId)));
1784             }
1785
1786             if (!XmlUtil.IsValidXmlIDValue(attributeValue))
1787             {
1788                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4077, attributeValue)));
1789             }
1790
1791             assertion.AssertionId = attributeValue;
1792
1793             attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.Issuer, null);
1794             if (string.IsNullOrEmpty(attributeValue))
1795             {
1796                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.Issuer)));
1797             }
1798
1799             assertion.Issuer = attributeValue;
1800
1801             attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.IssueInstant, null);
1802             if (!string.IsNullOrEmpty(attributeValue))
1803             {
1804                 assertion.IssueInstant = DateTime.ParseExact(
1805                     attributeValue, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1806             }
1807
1808             wrappedReader.MoveToContent();
1809             wrappedReader.Read();
1810
1811             if (wrappedReader.IsStartElement(SamlConstants.ElementNames.Conditions, SamlConstants.Namespace))
1812             {
1813                 assertion.Conditions = ReadConditions(wrappedReader);
1814             }
1815
1816             if (wrappedReader.IsStartElement(SamlConstants.ElementNames.Advice, SamlConstants.Namespace))
1817             {
1818                 assertion.Advice = ReadAdvice(wrappedReader);
1819             }
1820
1821             while (wrappedReader.IsStartElement())
1822             {
1823                 assertion.Statements.Add(ReadStatement(wrappedReader));
1824             }
1825
1826             if (assertion.Statements.Count == 0)
1827             {
1828                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4078)));
1829             }
1830
1831             wrappedReader.MoveToContent();
1832             wrappedReader.ReadEndElement();
1833
1834             // Reading the end element will complete the signature; 
1835             // capture the signing creds
1836             assertion.SigningCredentials = wrappedReader.SigningCredentials;
1837
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);
1841
1842             return assertion;
1843         }
1844
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)
1852         {
1853             if (writer == null)
1854             {
1855                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1856             }
1857
1858             if (assertion == null)
1859             {
1860                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
1861             }
1862
1863             SamlAssertion SamlAssertion = assertion as SamlAssertion;
1864             if (SamlAssertion != null)
1865             {
1866                 if (SamlAssertion.CanWriteSourceData)
1867                 {
1868                     SamlAssertion.WriteSourceData(writer);
1869                     return;
1870                 }
1871             }
1872
1873             if (assertion.SigningCredentials != null)
1874             {
1875                 writer = new EnvelopedSignatureWriter(writer, assertion.SigningCredentials, assertion.AssertionId, new WrappedSerializer(this, assertion));
1876             }
1877             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Assertion, SamlConstants.Namespace);
1878
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));
1884
1885             // Write out conditions
1886             if (assertion.Conditions != null)
1887             {
1888                 WriteConditions(writer, assertion.Conditions);
1889             }
1890
1891             // Write out advice if there is one
1892             if (assertion.Advice != null)
1893             {
1894                 WriteAdvice(writer, assertion.Advice);
1895             }
1896
1897             // Write statements.
1898             for (int i = 0; i < assertion.Statements.Count; i++)
1899             {
1900                 WriteStatement(writer, assertion.Statements[i]);
1901             }
1902
1903             writer.WriteEndElement();
1904         }
1905
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)
1915         {
1916             if (reader == null)
1917             {
1918                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
1919             }
1920
1921             SamlConditions conditions = new SamlConditions();
1922             string time = reader.GetAttribute(SamlConstants.AttributeNames.NotBefore, null);
1923             if (!string.IsNullOrEmpty(time))
1924             {
1925                 conditions.NotBefore = DateTime.ParseExact(
1926                     time, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1927             }
1928
1929             time = reader.GetAttribute(SamlConstants.AttributeNames.NotOnOrAfter, null);
1930             if (!string.IsNullOrEmpty(time))
1931             {
1932                 conditions.NotOnOrAfter = DateTime.ParseExact(
1933                     time, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1934             }
1935
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)
1940             {
1941                 // Just issue a read to read the Empty element.
1942                 reader.MoveToContent();
1943                 reader.Read();
1944                 return conditions;
1945             }
1946
1947             reader.ReadStartElement();
1948
1949             while (reader.IsStartElement())
1950             {
1951                 conditions.Conditions.Add(ReadCondition(reader));
1952             }
1953
1954             reader.ReadEndElement();
1955
1956             return conditions;
1957         }
1958
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)
1966         {
1967             if (writer == null)
1968             {
1969                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
1970             }
1971
1972             if (conditions == null)
1973             {
1974                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("conditions");
1975             }
1976
1977             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Conditions, SamlConstants.Namespace);
1978
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)
1985             {
1986                 writer.WriteAttributeString(
1987                     SamlConstants.AttributeNames.NotBefore,
1988                     null,
1989                     conditions.NotBefore.ToUniversalTime().ToString(DateTimeFormats.Generated, DateTimeFormatInfo.InvariantInfo));
1990             }
1991
1992             if (conditions.NotOnOrAfter != DateTimeUtil.GetMaxValue(DateTimeKind.Utc) &&
1993                 conditions.NotOnOrAfter != WCFMaxValue)
1994             {
1995                 writer.WriteAttributeString(
1996                     SamlConstants.AttributeNames.NotOnOrAfter,
1997                     null,
1998                     conditions.NotOnOrAfter.ToUniversalTime().ToString(DateTimeFormats.Generated, DateTimeFormatInfo.InvariantInfo));
1999             }
2000
2001             for (int i = 0; i < conditions.Conditions.Count; i++)
2002             {
2003                 WriteCondition(writer, conditions.Conditions[i]);
2004             }
2005
2006             writer.WriteEndElement();
2007         }
2008
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)
2017         {
2018             if (reader == null)
2019             {
2020                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2021             }
2022
2023             if (reader.IsStartElement(SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace))
2024             {
2025                 return ReadAudienceRestrictionCondition(reader);
2026             }
2027             else if (reader.IsStartElement(SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace))
2028             {
2029                 return ReadDoNotCacheCondition(reader);
2030             }
2031             else
2032             {
2033                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4080, reader.LocalName, reader.NamespaceURI)));
2034             }
2035         }
2036
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)
2046         {
2047             if (condition == null)
2048             {
2049                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("condition");
2050             }
2051
2052             SamlAudienceRestrictionCondition audienceRestrictionCondition = condition as SamlAudienceRestrictionCondition;
2053             if (audienceRestrictionCondition != null)
2054             {
2055                 WriteAudienceRestrictionCondition(writer, audienceRestrictionCondition);
2056                 return;
2057             }
2058
2059             SamlDoNotCacheCondition doNotCacheCondition = condition as SamlDoNotCacheCondition;
2060             if (doNotCacheCondition != null)
2061             {
2062                 WriteDoNotCacheCondition(writer, doNotCacheCondition);
2063                 return;
2064             }
2065
2066             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4081, condition.GetType())));
2067         }
2068
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)
2077         {
2078             if (reader == null)
2079             {
2080                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2081             }
2082
2083             if (!reader.IsStartElement(SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace))
2084             {
2085                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2086             }
2087
2088             reader.ReadStartElement();
2089
2090             SamlAudienceRestrictionCondition audienceRestrictionCondition = new SamlAudienceRestrictionCondition();
2091             while (reader.IsStartElement())
2092             {
2093                 if (reader.IsStartElement(SamlConstants.ElementNames.Audience, SamlConstants.Namespace))
2094                 {
2095                     string audience = reader.ReadString();
2096                     if (string.IsNullOrEmpty(audience))
2097                     {
2098                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4083)));
2099                     }
2100
2101                     audienceRestrictionCondition.Audiences.Add(new Uri(audience, UriKind.RelativeOrAbsolute));
2102                     reader.MoveToContent();
2103                     reader.ReadEndElement();
2104                 }
2105                 else
2106                 {
2107                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.Audience, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2108                 }
2109             }
2110
2111             if (audienceRestrictionCondition.Audiences.Count == 0)
2112             {
2113                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4084)));
2114             }
2115
2116             reader.MoveToContent();
2117             reader.ReadEndElement();
2118
2119             return audienceRestrictionCondition;
2120         }
2121
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)
2129         {
2130             if (writer == null)
2131             {
2132                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2133             }
2134
2135             if (condition == null)
2136             {
2137                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("condition");
2138             }
2139
2140             // Schema requires at least one audience.
2141             if (condition.Audiences == null || condition.Audiences.Count == 0)
2142             {
2143                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2144                     new InvalidOperationException(SR.GetString(SR.ID4269)));
2145             }
2146
2147             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AudienceRestrictionCondition, SamlConstants.Namespace);
2148
2149             for (int i = 0; i < condition.Audiences.Count; i++)
2150             {
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);
2153             }
2154
2155             writer.WriteEndElement();
2156         }
2157
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)
2166         {
2167             if (reader == null)
2168             {
2169                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2170             }
2171
2172             if (!reader.IsStartElement(SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace))
2173             {
2174                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2175             }
2176
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)
2181             {
2182                 reader.MoveToContent();
2183                 reader.Read();
2184                 return doNotCacheCondition;
2185             }
2186
2187             reader.MoveToContent();
2188             reader.ReadStartElement();
2189             reader.ReadEndElement();
2190
2191             return doNotCacheCondition;
2192         }
2193
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)
2201         {
2202             if (writer == null)
2203             {
2204                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2205             }
2206
2207             if (condition == null)
2208             {
2209                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("condition");
2210             }
2211
2212             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.DoNotCacheCondition, SamlConstants.Namespace);
2213             writer.WriteEndElement();
2214         }
2215
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)
2225         {
2226             if (reader == null)
2227             {
2228                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2229             }
2230
2231             if (reader.IsStartElement(SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace))
2232             {
2233                 return ReadAuthenticationStatement(reader);
2234             }
2235             else if (reader.IsStartElement(SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace))
2236             {
2237                 return ReadAttributeStatement(reader);
2238             }
2239             else if (reader.IsStartElement(SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace))
2240             {
2241                 return ReadAuthorizationDecisionStatement(reader);
2242             }
2243             else
2244             {
2245                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4085, reader.LocalName, reader.NamespaceURI)));
2246             }
2247
2248         }
2249
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)
2259         {
2260             if (writer == null)
2261             {
2262                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2263             }
2264
2265             if (statement == null)
2266             {
2267                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
2268             }
2269
2270             SamlAuthenticationStatement authnStatement = statement as SamlAuthenticationStatement;
2271             if (authnStatement != null)
2272             {
2273                 WriteAuthenticationStatement(writer, authnStatement);
2274                 return;
2275             }
2276
2277             SamlAuthorizationDecisionStatement authzStatement = statement as SamlAuthorizationDecisionStatement;
2278             if (authzStatement != null)
2279             {
2280                 WriteAuthorizationDecisionStatement(writer, authzStatement);
2281                 return;
2282             }
2283
2284             SamlAttributeStatement attributeStatement = statement as SamlAttributeStatement;
2285             if (attributeStatement != null)
2286             {
2287                 WriteAttributeStatement(writer, attributeStatement);
2288                 return;
2289             }
2290
2291             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4086, statement.GetType())));
2292         }
2293
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)
2302         {
2303             if (reader == null)
2304             {
2305                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2306             }
2307
2308             if (!reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
2309             {
2310                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.Subject, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2311             }
2312
2313             SamlSubject subject = new SamlSubject();
2314
2315             reader.ReadStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace);
2316             if (reader.IsStartElement(SamlConstants.ElementNames.NameIdentifier, SamlConstants.Namespace))
2317             {
2318                 subject.NameFormat = reader.GetAttribute(SamlConstants.AttributeNames.NameIdentifierFormat, null);
2319                 subject.NameQualifier = reader.GetAttribute(SamlConstants.AttributeNames.NameIdentifierNameQualifier, null);
2320
2321                 reader.MoveToContent();
2322                 subject.Name = reader.ReadElementString();
2323
2324                 if (string.IsNullOrEmpty(subject.Name))
2325                 {
2326                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4087)));
2327                 }
2328             }
2329
2330             if (reader.IsStartElement(SamlConstants.ElementNames.SubjectConfirmation, SamlConstants.Namespace))
2331             {
2332                 reader.ReadStartElement();
2333
2334                 while (reader.IsStartElement(SamlConstants.ElementNames.SubjectConfirmationMethod, SamlConstants.Namespace))
2335                 {
2336                     string method = reader.ReadElementString();
2337                     if (string.IsNullOrEmpty(method))
2338                     {
2339                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4088)));
2340                     }
2341
2342                     subject.ConfirmationMethods.Add(method);
2343                 }
2344
2345                 if (subject.ConfirmationMethods.Count == 0)
2346                 {
2347                     // A SubjectConfirmaton clause should specify at least one 
2348                     // ConfirmationMethod.
2349                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4088)));
2350                 }
2351
2352                 if (reader.IsStartElement(SamlConstants.ElementNames.SubjectConfirmationData, SamlConstants.Namespace))
2353                 {
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();
2357                 }
2358
2359                 if (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace))
2360                 {
2361                     subject.KeyIdentifier = ReadSubjectKeyInfo(reader);
2362                     SecurityKey key = ResolveSubjectKeyIdentifier(subject.KeyIdentifier);
2363                     if (key != null)
2364                     {
2365                         subject.Crypto = key;
2366                     }
2367                     else
2368                     {
2369                         subject.Crypto = new SecurityKeyElement(subject.KeyIdentifier, this.Configuration.ServiceTokenResolver);
2370                     }
2371                 }
2372
2373
2374                 if ((subject.ConfirmationMethods.Count == 0) && (string.IsNullOrEmpty(subject.Name)))
2375                 {
2376                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4089)));
2377                 }
2378
2379                 reader.MoveToContent();
2380                 reader.ReadEndElement();
2381             }
2382
2383             reader.MoveToContent();
2384             reader.ReadEndElement();
2385
2386             return subject;
2387         }
2388
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)
2396         {
2397             if (writer == null)
2398             {
2399                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2400             }
2401
2402             if (subject == null)
2403             {
2404                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
2405             }
2406
2407             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Subject, SamlConstants.Namespace);
2408             if (!string.IsNullOrEmpty(subject.Name))
2409             {
2410                 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.NameIdentifier, SamlConstants.Namespace);
2411                 if (!string.IsNullOrEmpty(subject.NameFormat))
2412                 {
2413                     writer.WriteAttributeString(SamlConstants.AttributeNames.NameIdentifierFormat, null, subject.NameFormat);
2414                 }
2415                 if (subject.NameQualifier != null)
2416                 {
2417                     writer.WriteAttributeString(SamlConstants.AttributeNames.NameIdentifierNameQualifier, null, subject.NameQualifier);
2418                 }
2419                 writer.WriteString(subject.Name);
2420                 writer.WriteEndElement();
2421             }
2422
2423             if (subject.ConfirmationMethods.Count > 0)
2424             {
2425                 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.SubjectConfirmation, SamlConstants.Namespace);
2426
2427                 foreach (string method in subject.ConfirmationMethods)
2428                 {
2429                     writer.WriteElementString(SamlConstants.ElementNames.SubjectConfirmationMethod, SamlConstants.Namespace, method);
2430                 }
2431
2432                 if (!string.IsNullOrEmpty(subject.SubjectConfirmationData))
2433                 {
2434                     writer.WriteElementString(SamlConstants.ElementNames.SubjectConfirmationData, SamlConstants.Namespace, subject.SubjectConfirmationData);
2435                 }
2436
2437                 if (subject.KeyIdentifier != null)
2438                 {
2439                     WriteSubjectKeyInfo(writer, subject.KeyIdentifier);
2440                 }
2441
2442                 writer.WriteEndElement();
2443             }
2444
2445             writer.WriteEndElement();
2446         }
2447
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)
2456         {
2457             if (reader == null)
2458             {
2459                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2460             }
2461
2462             if (KeyInfoSerializer.CanReadKeyIdentifier(reader))
2463             {
2464                 return KeyInfoSerializer.ReadKeyIdentifier(reader);
2465             }
2466
2467             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4090)));
2468         }
2469
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)
2477         {
2478             if (writer == null)
2479             {
2480                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2481             }
2482
2483             if (subjectSki == null)
2484             {
2485                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subjectSki");
2486             }
2487
2488             if (KeyInfoSerializer.CanWriteKeyIdentifier(subjectSki))
2489             {
2490                 KeyInfoSerializer.WriteKeyIdentifier(writer, subjectSki);
2491                 return;
2492             }
2493
2494             throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("subjectSki", SR.GetString(SR.ID4091, subjectSki.GetType()));
2495         }
2496
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)
2506         {
2507             if (reader == null)
2508             {
2509                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2510             }
2511
2512             if (!reader.IsStartElement(SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace))
2513             {
2514                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2515             }
2516
2517             reader.ReadStartElement();
2518
2519             SamlAttributeStatement attributeStatement = new SamlAttributeStatement();
2520             if (reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
2521             {
2522                 attributeStatement.SamlSubject = ReadSubject(reader);
2523             }
2524             else
2525             {
2526                 // SAML Subject is a required Attribute Statement clause.
2527                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4092)));
2528             }
2529
2530             while (reader.IsStartElement())
2531             {
2532                 if (reader.IsStartElement(SamlConstants.ElementNames.Attribute, SamlConstants.Namespace))
2533                 {
2534                     // SAML Attribute is a extensibility point. So ask the SAML serializer 
2535                     // to load this part.
2536                     attributeStatement.Attributes.Add(ReadAttribute(reader));
2537                 }
2538                 else
2539                 {
2540                     break;
2541                 }
2542             }
2543
2544             if (attributeStatement.Attributes.Count == 0)
2545             {
2546                 // Each Attribute statement should have at least one attribute.
2547                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4093)));
2548             }
2549
2550             reader.MoveToContent();
2551             reader.ReadEndElement();
2552
2553             return attributeStatement;
2554         }
2555
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)
2563         {
2564             if (writer == null)
2565             {
2566                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2567             }
2568
2569             if (statement == null)
2570             {
2571                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
2572             }
2573
2574             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AttributeStatement, SamlConstants.Namespace);
2575
2576             WriteSubject(writer, statement.SamlSubject);
2577
2578             for (int i = 0; i < statement.Attributes.Count; i++)
2579             {
2580                 WriteAttribute(writer, statement.Attributes[i]);
2581             }
2582
2583             writer.WriteEndElement();
2584         }
2585
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)
2594         {
2595             if (reader == null)
2596             {
2597                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2598             }
2599
2600             SamlAttribute attribute = new SamlAttribute();
2601
2602             attribute.Name = reader.GetAttribute(SamlConstants.AttributeNames.AttributeName, null);
2603             if (string.IsNullOrEmpty(attribute.Name))
2604             {
2605                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4094)));
2606             }
2607
2608             attribute.Namespace = reader.GetAttribute(SamlConstants.AttributeNames.AttributeNamespace, null);
2609             if (string.IsNullOrEmpty(attribute.Namespace))
2610             {
2611                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4095)));
2612             }
2613
2614             //
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
2619             //
2620             string originalIssuer = reader.GetAttribute(SamlConstants.AttributeNames.OriginalIssuer, ClaimType2009Namespace);
2621
2622             if (originalIssuer == null)
2623             {
2624                 originalIssuer = reader.GetAttribute(SamlConstants.AttributeNames.OriginalIssuer, ProductConstants.NamespaceUri);
2625             }
2626
2627             if (originalIssuer == String.Empty)
2628             {
2629                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4252)));
2630             }
2631             attribute.OriginalIssuer = originalIssuer;
2632
2633             reader.MoveToContent();
2634             reader.Read();
2635             while (reader.IsStartElement(SamlConstants.ElementNames.AttributeValue, SamlConstants.Namespace))
2636             {
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))
2645                 {
2646                     if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) == -1) // "some-non-empty-string" case
2647                     {
2648                         attributeValueXsiTypePrefix = reader.LookupNamespace(String.Empty);
2649                         attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix;
2650                     }
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
2653                     {
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);
2658                     }
2659                 }
2660                 if (attributeValueXsiTypePrefix != null && attributeValueXsiTypeSuffix != null)
2661                 {
2662                     attribute.AttributeValueXsiType = String.Concat(attributeValueXsiTypePrefix, "#", attributeValueXsiTypeSuffix);
2663                 }
2664
2665                 if (reader.IsEmptyElement)
2666                 {
2667                     reader.Read();
2668                     attribute.AttributeValues.Add(string.Empty);
2669                 }
2670                 else
2671                 {
2672                     attribute.AttributeValues.Add(ReadAttributeValue(reader, attribute));
2673                 }
2674             }
2675
2676             if (attribute.AttributeValues.Count == 0)
2677             {
2678                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4212)));
2679             }
2680
2681             reader.MoveToContent();
2682             reader.ReadEndElement();
2683
2684             return attribute;
2685         }
2686
2687
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)
2696         {
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.
2700             // 
2701             // Couple of cases to help understand the design choices.
2702             //
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;'
2706             //
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;'
2711             //
2712             // 3. 
2713             // "/r/n/t   "
2714             // Could result in the our reader reporting the NodeType as whitespace.
2715             //
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.
2720             // 
2721
2722             if (reader == null)
2723             {
2724                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2725             }
2726
2727             string result = String.Empty;
2728             string whiteSpace = String.Empty;
2729
2730             reader.ReadStartElement(Saml2Constants.Elements.AttributeValue, SamlConstants.Namespace);
2731
2732             while (reader.NodeType == XmlNodeType.Whitespace)
2733             {
2734                 whiteSpace += reader.Value;
2735                 reader.Read();
2736             }
2737
2738             reader.MoveToContent();
2739             if (reader.NodeType == XmlNodeType.Element)
2740             {
2741                 while (reader.NodeType == XmlNodeType.Element)
2742                 {
2743                     result += reader.ReadOuterXml();
2744                     reader.MoveToContent();
2745                 }
2746             }
2747             else
2748             {
2749                 result = whiteSpace;
2750                 result += reader.ReadContentAsString();
2751             }
2752
2753             reader.ReadEndElement();
2754             return result;
2755         }
2756
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)
2764         {
2765             if (writer == null)
2766             {
2767                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2768             }
2769
2770             if (attribute == null)
2771             {
2772                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("attribute");
2773             }
2774
2775             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Attribute, SamlConstants.Namespace);
2776
2777             writer.WriteAttributeString(SamlConstants.AttributeNames.AttributeName, null, attribute.Name);
2778             writer.WriteAttributeString(SamlConstants.AttributeNames.AttributeNamespace, null, attribute.Namespace);
2779
2780             SamlAttribute SamlAttribute = attribute as SamlAttribute;
2781             if ((SamlAttribute != null) && (SamlAttribute.OriginalIssuer != null))
2782             {
2783                 writer.WriteAttributeString(SamlConstants.AttributeNames.OriginalIssuer, ClaimType2009Namespace, SamlAttribute.OriginalIssuer);
2784             }
2785
2786             string xsiTypePrefix = null;
2787             string xsiTypeSuffix = null;
2788             if (SamlAttribute != null && !StringComparer.Ordinal.Equals(SamlAttribute.AttributeValueXsiType, ClaimValueTypes.String))
2789             {
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);
2795             }
2796
2797             for (int i = 0; i < attribute.AttributeValues.Count; i++)
2798             {
2799                 if (attribute.AttributeValues[i] == null)
2800                 {
2801                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4096)));
2802                 }
2803
2804                 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AttributeValue, SamlConstants.Namespace);
2805                 if ((xsiTypePrefix != null) && (xsiTypeSuffix != null))
2806                 {
2807                     writer.WriteAttributeString("xmlns", ProductConstants.ClaimValueTypeSerializationPrefix, null, xsiTypePrefix);
2808                     writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, String.Concat(ProductConstants.ClaimValueTypeSerializationPrefixWithColon, xsiTypeSuffix));
2809                 }
2810                 WriteAttributeValue(writer, attribute.AttributeValues[i], attribute);
2811                 writer.WriteEndElement();
2812             }
2813
2814             writer.WriteEndElement();
2815         }
2816
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)
2826         {
2827             if (writer == null)
2828             {
2829                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2830             }
2831
2832             writer.WriteString(value);
2833         }
2834
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)
2844         {
2845             if (reader == null)
2846             {
2847                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2848             }
2849
2850             if (!reader.IsStartElement(SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace))
2851             {
2852                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
2853             }
2854
2855             SamlAuthenticationStatement authnStatement = new SamlAuthenticationStatement();
2856             string authInstance = reader.GetAttribute(SamlConstants.AttributeNames.AuthenticationInstant, null);
2857             if (string.IsNullOrEmpty(authInstance))
2858             {
2859                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4097)));
2860             }
2861             authnStatement.AuthenticationInstant = DateTime.ParseExact(
2862                 authInstance, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
2863
2864             authnStatement.AuthenticationMethod = reader.GetAttribute(SamlConstants.AttributeNames.AuthenticationMethod, null);
2865             if (string.IsNullOrEmpty(authnStatement.AuthenticationMethod))
2866             {
2867                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4098)));
2868             }
2869
2870             reader.MoveToContent();
2871             reader.Read();
2872
2873             if (reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
2874             {
2875                 authnStatement.SamlSubject = ReadSubject(reader);
2876             }
2877             else
2878             {
2879                 // Subject is a required element for a Authentication Statement clause.
2880                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4099)));
2881             }
2882
2883             if (reader.IsStartElement(SamlConstants.ElementNames.SubjectLocality, SamlConstants.Namespace))
2884             {
2885                 authnStatement.DnsAddress = reader.GetAttribute(SamlConstants.AttributeNames.SubjectLocalityDNSAddress, null);
2886                 authnStatement.IPAddress = reader.GetAttribute(SamlConstants.AttributeNames.SubjectLocalityIPAddress, null);
2887
2888                 if (reader.IsEmptyElement)
2889                 {
2890                     reader.MoveToContent();
2891                     reader.Read();
2892                 }
2893                 else
2894                 {
2895                     reader.MoveToContent();
2896                     reader.Read();
2897                     reader.ReadEndElement();
2898                 }
2899             }
2900
2901             while (reader.IsStartElement())
2902             {
2903                 if (reader.IsStartElement(SamlConstants.ElementNames.AuthorityBinding, SamlConstants.Namespace))
2904                 {
2905                     authnStatement.AuthorityBindings.Add(ReadAuthorityBinding(reader));
2906                 }
2907                 else
2908                 {
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)));
2911                 }
2912             }
2913
2914             reader.MoveToContent();
2915             reader.ReadEndElement();
2916
2917             return authnStatement;
2918         }
2919
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)
2927         {
2928             if (writer == null)
2929             {
2930                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2931             }
2932
2933             if (statement == null)
2934             {
2935                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
2936             }
2937
2938             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AuthenticationStatement, SamlConstants.Namespace);
2939
2940             writer.WriteAttributeString(SamlConstants.AttributeNames.AuthenticationMethod, null, statement.AuthenticationMethod);
2941
2942             writer.WriteAttributeString(SamlConstants.AttributeNames.AuthenticationInstant, null,
2943                              XmlConvert.ToString(statement.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated));
2944
2945
2946             WriteSubject(writer, statement.SamlSubject);
2947
2948             if ((statement.IPAddress != null) || (statement.DnsAddress != null))
2949             {
2950                 writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.SubjectLocality, SamlConstants.Namespace);
2951
2952                 if (statement.IPAddress != null)
2953                 {
2954                     writer.WriteAttributeString(SamlConstants.AttributeNames.SubjectLocalityIPAddress, null, statement.IPAddress);
2955                 }
2956
2957                 if (statement.DnsAddress != null)
2958                 {
2959                     writer.WriteAttributeString(SamlConstants.AttributeNames.SubjectLocalityDNSAddress, null, statement.DnsAddress);
2960                 }
2961
2962                 writer.WriteEndElement();
2963             }
2964
2965             for (int i = 0; i < statement.AuthorityBindings.Count; i++)
2966             {
2967                 WriteAuthorityBinding(writer, statement.AuthorityBindings[i]);
2968             }
2969
2970             writer.WriteEndElement();
2971         }
2972
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)
2982         {
2983             if (reader == null)
2984             {
2985                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2986             }
2987
2988             SamlAuthorityBinding authorityBinding = new SamlAuthorityBinding();
2989             string authKind = reader.GetAttribute(SamlConstants.AttributeNames.AuthorityKind, null);
2990             if (string.IsNullOrEmpty(authKind))
2991             {
2992                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4200)));
2993             }
2994
2995             string[] authKindParts = authKind.Split(':');
2996             if (authKindParts.Length > 2)
2997             {
2998                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4201, authKind)));
2999             }
3000
3001             string localName;
3002             string prefix;
3003             string nameSpace;
3004             if (authKindParts.Length == 2)
3005             {
3006                 prefix = authKindParts[0];
3007                 localName = authKindParts[1];
3008             }
3009             else
3010             {
3011                 prefix = String.Empty;
3012                 localName = authKindParts[0];
3013             }
3014
3015             nameSpace = reader.LookupNamespace(prefix);
3016
3017             authorityBinding.AuthorityKind = new XmlQualifiedName(localName, nameSpace);
3018
3019             authorityBinding.Binding = reader.GetAttribute(SamlConstants.AttributeNames.Binding, null);
3020             if (string.IsNullOrEmpty(authorityBinding.Binding))
3021             {
3022                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4202)));
3023             }
3024
3025             authorityBinding.Location = reader.GetAttribute(SamlConstants.AttributeNames.Location, null);
3026             if (string.IsNullOrEmpty(authorityBinding.Location))
3027             {
3028                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4203)));
3029             }
3030
3031             if (reader.IsEmptyElement)
3032             {
3033                 reader.MoveToContent();
3034                 reader.Read();
3035             }
3036             else
3037             {
3038                 reader.MoveToContent();
3039                 reader.Read();
3040                 reader.ReadEndElement();
3041             }
3042
3043             return authorityBinding;
3044         }
3045
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)
3052         {
3053             if (writer == null)
3054             {
3055                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3056             }
3057
3058             if (authorityBinding == null)
3059             {
3060                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
3061             }
3062
3063             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AuthorityBinding, SamlConstants.Namespace);
3064
3065             string prefix = null;
3066             if (!string.IsNullOrEmpty(authorityBinding.AuthorityKind.Namespace))
3067             {
3068                 writer.WriteAttributeString(String.Empty, SamlConstants.AttributeNames.NamespaceAttributePrefix, null, authorityBinding.AuthorityKind.Namespace);
3069                 prefix = writer.LookupPrefix(authorityBinding.AuthorityKind.Namespace);
3070             }
3071
3072             writer.WriteStartAttribute(SamlConstants.AttributeNames.AuthorityKind, null);
3073             if (string.IsNullOrEmpty(prefix))
3074             {
3075                 writer.WriteString(authorityBinding.AuthorityKind.Name);
3076             }
3077             else
3078             {
3079                 writer.WriteString(prefix + ":" + authorityBinding.AuthorityKind.Name);
3080             }
3081             writer.WriteEndAttribute();
3082
3083             writer.WriteAttributeString(SamlConstants.AttributeNames.Location, null, authorityBinding.Location);
3084
3085             writer.WriteAttributeString(SamlConstants.AttributeNames.Binding, null, authorityBinding.Binding);
3086
3087             writer.WriteEndElement();
3088         }
3089
3090         /// <summary>
3091         /// Gets a boolean indicating if the SecurityTokenHandler can Serialize Tokens. Return true by default.
3092         /// </summary>
3093         public override bool CanWriteToken
3094         {
3095             get
3096             {
3097                 return true;
3098             }
3099         }
3100
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)
3109         {
3110             if (writer == null)
3111             {
3112                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3113             }
3114
3115             if (token == null)
3116             {
3117                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
3118             }
3119
3120             SamlSecurityToken samlSecurityToken = token as SamlSecurityToken;
3121             if (samlSecurityToken == null)
3122             {
3123                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4217, token.GetType(), typeof(SamlSecurityToken))));
3124             }
3125
3126             WriteAssertion(writer, samlSecurityToken.Assertion);
3127         }
3128
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)
3139         {
3140             if (reader == null)
3141             {
3142                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3143             }
3144
3145             if (!reader.IsStartElement(SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace))
3146             {
3147                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
3148             }
3149
3150             SamlAuthorizationDecisionStatement authzStatement = new SamlAuthorizationDecisionStatement();
3151             authzStatement.Resource = reader.GetAttribute(SamlConstants.AttributeNames.Resource, null);
3152             if (string.IsNullOrEmpty(authzStatement.Resource))
3153             {
3154                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4205)));
3155             }
3156
3157             string decisionString = reader.GetAttribute(SamlConstants.AttributeNames.Decision, null);
3158             if (string.IsNullOrEmpty(decisionString))
3159             {
3160                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4204)));
3161             }
3162
3163             if (decisionString.Equals(SamlAccessDecision.Deny.ToString(), StringComparison.OrdinalIgnoreCase))
3164             {
3165                 authzStatement.AccessDecision = SamlAccessDecision.Deny;
3166             }
3167             else if (decisionString.Equals(SamlAccessDecision.Permit.ToString(), StringComparison.OrdinalIgnoreCase))
3168             {
3169                 authzStatement.AccessDecision = SamlAccessDecision.Permit;
3170             }
3171             else
3172             {
3173                 authzStatement.AccessDecision = SamlAccessDecision.Indeterminate;
3174             }
3175
3176             reader.MoveToContent();
3177             reader.Read();
3178
3179             if (reader.IsStartElement(SamlConstants.ElementNames.Subject, SamlConstants.Namespace))
3180             {
3181                 authzStatement.SamlSubject = ReadSubject(reader);
3182             }
3183             else
3184             {
3185                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4206)));
3186             }
3187
3188             while (reader.IsStartElement())
3189             {
3190                 if (reader.IsStartElement(SamlConstants.ElementNames.Action, SamlConstants.Namespace))
3191                 {
3192                     authzStatement.SamlActions.Add(ReadAction(reader));
3193                 }
3194                 else if (reader.IsStartElement(SamlConstants.ElementNames.Evidence, SamlConstants.Namespace))
3195                 {
3196                     if (authzStatement.Evidence != null)
3197                     {
3198                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4207)));
3199                     }
3200
3201                     authzStatement.Evidence = ReadEvidence(reader);
3202                 }
3203                 else
3204                 {
3205                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4208, reader.LocalName, reader.NamespaceURI)));
3206                 }
3207             }
3208
3209             if (authzStatement.SamlActions.Count == 0)
3210             {
3211                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4209)));
3212             }
3213
3214             reader.MoveToContent();
3215             reader.ReadEndElement();
3216
3217             return authzStatement;
3218         }
3219
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)
3227         {
3228             if (writer == null)
3229             {
3230                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3231             }
3232
3233             if (statement == null)
3234             {
3235                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
3236             }
3237
3238             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AuthorizationDecisionStatement, SamlConstants.Namespace);
3239
3240             writer.WriteAttributeString(SamlConstants.AttributeNames.Decision, null, statement.AccessDecision.ToString());
3241
3242             writer.WriteAttributeString(SamlConstants.AttributeNames.Resource, null, statement.Resource);
3243
3244             WriteSubject(writer, statement.SamlSubject);
3245
3246             foreach (SamlAction action in statement.SamlActions)
3247             {
3248                 WriteAction(writer, action);
3249             }
3250
3251             if (statement.Evidence != null)
3252             {
3253                 WriteEvidence(writer, statement.Evidence);
3254             }
3255
3256             writer.WriteEndElement();
3257         }
3258
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)
3268         {
3269             if (reader == null)
3270             {
3271                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3272             }
3273
3274             if (!reader.IsStartElement(SamlConstants.ElementNames.Evidence, SamlConstants.Namespace))
3275             {
3276                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4082, SamlConstants.ElementNames.Evidence, SamlConstants.Namespace, reader.LocalName, reader.NamespaceURI)));
3277             }
3278
3279             SamlEvidence evidence = new SamlEvidence();
3280             reader.ReadStartElement();
3281
3282             while (reader.IsStartElement())
3283             {
3284                 if (reader.IsStartElement(SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace))
3285                 {
3286                     evidence.AssertionIdReferences.Add(reader.ReadElementString());
3287                 }
3288                 else if (reader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace))
3289                 {
3290                     evidence.Assertions.Add(ReadAssertion(reader));
3291                 }
3292                 else
3293                 {
3294                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4210, reader.LocalName, reader.NamespaceURI)));
3295                 }
3296             }
3297
3298             if ((evidence.AssertionIdReferences.Count == 0) && (evidence.Assertions.Count == 0))
3299             {
3300                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4211)));
3301             }
3302
3303             reader.MoveToContent();
3304             reader.ReadEndElement();
3305
3306             return evidence;
3307         }
3308
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)
3316         {
3317             if (writer == null)
3318             {
3319                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3320             }
3321
3322             if (evidence == null)
3323             {
3324                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("evidence");
3325             }
3326
3327             writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Evidence, SamlConstants.Namespace);
3328
3329             for (int i = 0; i < evidence.AssertionIdReferences.Count; i++)
3330             {
3331                 writer.WriteElementString(SamlConstants.Prefix, SamlConstants.ElementNames.AssertionIdReference, SamlConstants.Namespace, evidence.AssertionIdReferences[i]);
3332             }
3333
3334             for (int i = 0; i < evidence.Assertions.Count; i++)
3335             {
3336                 WriteAssertion(writer, evidence.Assertions[i]);
3337             }
3338
3339             writer.WriteEndElement();
3340         }
3341
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)
3349         {
3350             if (subjectKeyIdentifier == null)
3351             {
3352                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subjectKeyIdentifier");
3353             }
3354
3355             if (this.Configuration == null)
3356             {
3357                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
3358             }
3359
3360             if (this.Configuration.ServiceTokenResolver == null)
3361             {
3362                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
3363             }
3364
3365             SecurityKey key = null;
3366             foreach (SecurityKeyIdentifierClause clause in subjectKeyIdentifier)
3367             {
3368                 if (this.Configuration.ServiceTokenResolver.TryResolveSecurityKey(clause, out key))
3369                 {
3370                     return key;
3371                 }
3372             }
3373
3374             if (subjectKeyIdentifier.CanCreateKey)
3375             {
3376                 return subjectKeyIdentifier.CreateKey();
3377             }
3378
3379             return null;
3380         }
3381
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)
3392         {
3393             if (null == assertion)
3394             {
3395                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
3396             }
3397
3398             if (null == issuerResolver)
3399             {
3400                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuerResolver");
3401             }
3402
3403             SecurityToken token;
3404             if (TryResolveIssuerToken(assertion, issuerResolver, out token))
3405             {
3406                 return token;
3407             }
3408             else
3409             {
3410                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4220)));
3411             }
3412         }
3413
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)
3423         {
3424             if (null == assertion)
3425             {
3426                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
3427             }
3428
3429             if (assertion.SigningCredentials != null
3430                 && assertion.SigningCredentials.SigningKeyIdentifier != null
3431                 && issuerResolver != null)
3432             {
3433                 SecurityKeyIdentifier keyIdentifier = assertion.SigningCredentials.SigningKeyIdentifier;
3434                 return issuerResolver.TryResolveToken(keyIdentifier, out token);
3435             }
3436             else
3437             {
3438                 token = null;
3439                 return false;
3440             }
3441         }
3442
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)
3454         {
3455             if (reader == null)
3456             {
3457                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3458             }
3459
3460             SecurityKeyIdentifier ski;
3461
3462             if (KeyInfoSerializer.CanReadKeyIdentifier(reader))
3463             {
3464                 ski = KeyInfoSerializer.ReadKeyIdentifier(reader);
3465             }
3466             else
3467             {
3468                 KeyInfo keyInfo = new KeyInfo(KeyInfoSerializer);
3469                 keyInfo.ReadXml(XmlDictionaryReader.CreateDictionaryReader(reader));
3470                 ski = keyInfo.KeyIdentifier;
3471             }
3472
3473             // no key info
3474             if (ski.Count == 0)
3475             {
3476                 return new SecurityKeyIdentifier(new SamlSecurityKeyIdentifierClause(assertion));
3477             }
3478
3479             return ski;
3480         }
3481
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)
3489         {
3490             if (writer == null)
3491             {
3492                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3493             }
3494
3495             if (signingKeyIdentifier == null)
3496             {
3497                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("signingKeyIdentifier");
3498             }
3499
3500             if (KeyInfoSerializer.CanWriteKeyIdentifier(signingKeyIdentifier))
3501             {
3502                 KeyInfoSerializer.WriteKeyIdentifier(writer, signingKeyIdentifier);
3503                 return;
3504             }
3505
3506             throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4221, signingKeyIdentifier));
3507         }
3508
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)
3514         {
3515             if (statements == null)
3516             {
3517                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statements");
3518             }
3519             List<SamlSubject> subjects = new List<SamlSubject>();
3520             foreach (SamlStatement statement in statements)
3521             {
3522                 if (statement is SamlAttributeStatement)
3523                 {
3524                     subjects.Add((statement as SamlAttributeStatement).SamlSubject);
3525                 }
3526
3527                 if (statement is SamlAuthenticationStatement)
3528                 {
3529                     subjects.Add((statement as SamlAuthenticationStatement).SamlSubject);
3530                 }
3531
3532                 if (statement is SamlAuthorizationDecisionStatement)
3533                 {
3534                     subjects.Add((statement as SamlAuthorizationDecisionStatement).SamlSubject);
3535                 }
3536                 //
3537                 // skip all custom statements
3538                 //
3539
3540             }
3541
3542             if (subjects.Count == 0)
3543             {
3544                 //
3545                 // All statements are custom and we cannot validate
3546                 //
3547                 return;
3548             }
3549             string requiredSubjectName = subjects[0].Name;
3550             string requiredSubjectFormat = subjects[0].NameFormat;
3551             string requiredSubjectQualifier = subjects[0].NameQualifier;
3552
3553             foreach (SamlSubject subject in subjects)
3554             {
3555                 if (!StringComparer.Ordinal.Equals(subject.Name, requiredSubjectName) ||
3556                      !StringComparer.Ordinal.Equals(subject.NameFormat, requiredSubjectFormat) ||
3557                      !StringComparer.Ordinal.Equals(subject.NameQualifier, requiredSubjectQualifier))
3558                 {
3559                     //
3560                     // The SamlSubjects in the statements do not match
3561                     //
3562                     throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4225, subject));
3563                 }
3564             }
3565         }
3566
3567         #endregion
3568
3569         /// <summary>
3570         /// Returns the saml token's token type that is supported by this handler.
3571         /// </summary>
3572         public override string[] GetTokenTypeIdentifiers()
3573         {
3574             return _tokenTypeIdentifiers;
3575         }
3576
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
3583         {
3584             get
3585             {
3586                 if (_keyInfoSerializer == null)
3587                 {
3588                     lock (_syncObject)
3589                     {
3590                         if (_keyInfoSerializer == null)
3591                         {
3592                             SecurityTokenHandlerCollection sthc = (ContainingCollection != null) ?
3593                                 ContainingCollection : SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
3594                             _keyInfoSerializer = new SecurityTokenSerializerAdapter(sthc);
3595                         }
3596                     }
3597                 }
3598
3599                 return _keyInfoSerializer;
3600             }
3601             set
3602             {
3603                 if (value == null)
3604                 {
3605                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
3606                 }
3607
3608                 _keyInfoSerializer = value;
3609             }
3610         }
3611
3612         /// <summary>
3613         /// Gets the System.Type of the SecurityToken is supported by ththis handler.
3614         /// </summary>
3615         public override Type TokenType
3616         {
3617             get { return typeof(SamlSecurityToken); }
3618         }
3619
3620         /// <summary>
3621         /// Gets or sets the <see cref="SamlSecurityTokenRequirement"/>
3622         /// </summary>
3623         public SamlSecurityTokenRequirement SamlSecurityTokenRequirement
3624         {
3625             get
3626             {
3627                 return _samlSecurityTokenRequirement;
3628             }
3629             set
3630             {
3631                 if (value == null)
3632                 {
3633                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
3634                 }
3635                 _samlSecurityTokenRequirement = value;
3636             }
3637         }
3638
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
3643         {
3644             SamlSecurityTokenHandler _parent;
3645             SamlAssertion _assertion;
3646
3647             public WrappedSerializer(SamlSecurityTokenHandler parent, SamlAssertion assertion)
3648             {
3649                 if (parent == null)
3650                 {
3651                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent");
3652                 }
3653
3654                 _parent = parent;
3655                 _assertion = assertion;
3656             }
3657
3658             protected override bool CanReadKeyIdentifierClauseCore(XmlReader reader)
3659             {
3660                 return false;
3661             }
3662
3663             protected override bool CanReadKeyIdentifierCore(XmlReader reader)
3664             {
3665                 return true;
3666             }
3667
3668             protected override bool CanReadTokenCore(XmlReader reader)
3669             {
3670                 return false;
3671             }
3672
3673             protected override bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause)
3674             {
3675                 return false;
3676             }
3677
3678             protected override bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier)
3679             {
3680                 return false;
3681             }
3682
3683             protected override bool CanWriteTokenCore(SecurityToken token)
3684             {
3685                 return false;
3686             }
3687
3688             protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
3689             {
3690                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3691             }
3692
3693             protected override SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader)
3694             {
3695                 return _parent.ReadSigningKeyInfo(reader, _assertion);
3696             }
3697
3698             protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
3699             {
3700                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3701             }
3702
3703             protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
3704             {
3705                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3706             }
3707
3708             protected override void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier)
3709             {
3710                 _parent.WriteSigningKeyInfo(writer, keyIdentifier);
3711             }
3712
3713             protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
3714             {
3715                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
3716             }
3717         }
3718     }
3719 }