63ea232ef93b316866436f827f63cfe4cd363132
[mono.git] / mcs / class / referencesource / System.IdentityModel / System / IdentityModel / Tokens / Saml2SecurityTokenHandler.cs
1 //-----------------------------------------------------------------------
2 // <copyright file="Saml2SecurityTokenHandler.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
6
7 namespace System.IdentityModel.Tokens
8 {
9     using System;
10     using System.Collections.Generic;
11     using System.Collections.ObjectModel;
12     using System.Diagnostics;
13     using System.Globalization;
14     using System.IdentityModel.Configuration;
15     using System.IdentityModel.Diagnostics;
16     using System.IdentityModel.Protocols.WSTrust;
17     using System.IdentityModel.Selectors;
18     using System.IO;
19     using System.Linq;
20     using System.Runtime;
21     using System.Security.Claims;
22     using System.Security.Cryptography;
23     using System.Security.Principal;
24     using System.Text;
25     using System.Xml;
26     using System.Xml.Schema;
27     using Claim = System.Security.Claims.Claim;
28     using SAML2 = System.IdentityModel.Tokens.Saml2Constants;
29     using WSC = System.IdentityModel.WSSecureConversationFeb2005Constants;
30     using WSC13 = System.IdentityModel.WSSecureConversation13Constants;
31     using WSSE = System.IdentityModel.WSSecurity10Constants;
32     using WSSE11 = System.IdentityModel.WSSecurity11Constants;
33
34     /// <summary>
35     /// Creates SAML2 assertion-based security tokens
36     /// </summary>
37     public class Saml2SecurityTokenHandler : SecurityTokenHandler
38     {
39         /// <summary>
40         /// The key identifier value type for SAML 2.0 assertion IDs, as defined
41         /// by the OASIS Web Services Security SAML Token Profile 1.1. 
42         /// </summary>
43         public const string TokenProfile11ValueType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID";
44         private const string Actor = "Actor";
45         private const string Attribute = "Attribute";
46         private static string[] tokenTypeIdentifiers = new string[] { SecurityTokenTypes.Saml2TokenProfile11, SecurityTokenTypes.OasisWssSaml2TokenProfile11 };
47         private SamlSecurityTokenRequirement samlSecurityTokenRequirement;
48         private SecurityTokenSerializer keyInfoSerializer;
49
50         const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
51         object syncObject = new object();
52
53         /// <summary>
54         /// Creates an instance of <see cref="Saml2SecurityTokenHandler"/>
55         /// </summary>
56         public Saml2SecurityTokenHandler()
57             : this(new SamlSecurityTokenRequirement())
58         {
59         }
60
61         /// <summary>
62         /// Creates an instance of <see cref="Saml2SecurityTokenHandler"/>
63         /// </summary>
64         /// <param name="samlSecurityTokenRequirement">The SamlSecurityTokenRequirement to be used by the Saml2SecurityTokenHandler instance when validating tokens.</param>
65         public Saml2SecurityTokenHandler(SamlSecurityTokenRequirement samlSecurityTokenRequirement)
66         {
67             if (samlSecurityTokenRequirement == null)
68             {
69                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSecurityTokenRequirement");
70             }
71
72             this.samlSecurityTokenRequirement = samlSecurityTokenRequirement;
73         }
74
75         /// <summary>
76         /// Load custom configuration from Xml
77         /// </summary>
78         /// <param name="customConfigElements">SAML token authentication requirements.</param>
79         /// <exception cref="ArgumentNullException">Input parameter 'customConfigElements' is null.</exception>
80         /// <exception cref="InvalidOperationException">Custom configuration specified was invalid.</exception>
81         public override void LoadCustomConfiguration(XmlNodeList customConfigElements)
82         {
83             if (customConfigElements == null)
84             {
85                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfigElements");
86             }
87
88             List<XmlElement> configNodes = XmlUtil.GetXmlElements(customConfigElements);
89
90             bool foundValidConfig = false;
91
92             foreach (XmlElement configElement in configNodes)
93             {
94                 if (configElement.LocalName != ConfigurationStrings.SamlSecurityTokenRequirement)
95                 {
96                     continue;
97                 }
98
99                 if (foundValidConfig)
100                 {
101                     throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7026, ConfigurationStrings.SamlSecurityTokenRequirement));
102                 }
103
104                 this.samlSecurityTokenRequirement = new SamlSecurityTokenRequirement(configElement);
105
106                 foundValidConfig = true;
107             }
108
109             if (!foundValidConfig)
110             {
111                 this.samlSecurityTokenRequirement = new SamlSecurityTokenRequirement();
112             }
113         }
114
115         /// <summary>
116         /// Returns a value that indicates if this handler can validate <see cref="SecurityToken"/>.
117         /// </summary>
118         /// <returns>'True', indicating this instance can validate <see cref="SecurityToken"/>.</returns>
119         public override bool CanValidateToken
120         {
121             get { return true; }
122         }
123
124         /// <summary>
125         /// Gets the token type supported by this handler.
126         /// </summary>
127         public override Type TokenType
128         {
129             get { return typeof(Saml2SecurityToken); }
130         }
131
132         /// <summary>
133         /// Gets or sets the <see cref="X509CertificateValidator"/> that is used by the current instance to validate 
134         /// certificates that have signed the <see cref="Saml2SecurityToken"/>.
135         /// </summary>
136         public X509CertificateValidator CertificateValidator
137         {
138             get
139             {
140                 if (this.samlSecurityTokenRequirement.CertificateValidator == null)
141                 {
142                     if (Configuration != null)
143                     {
144                         return Configuration.CertificateValidator;
145                     }
146                     else
147                     {
148                         return null;
149                     }
150                 }
151                 else
152                 {
153                     return this.samlSecurityTokenRequirement.CertificateValidator;
154                 }
155             }
156
157             set
158             {
159                 this.samlSecurityTokenRequirement.CertificateValidator = value;
160             }
161         }
162
163         /// <summary>
164         /// Gets or sets a <see cref="SecurityTokenSerializer"/> that will be used to serialize and deserialize
165         /// a <see cref="SecurityKeyIdentifier"/>. For example, SamlSubject SecurityKeyIdentifier or Signature 
166         /// SecurityKeyIdentifier.
167         /// </summary>
168         public SecurityTokenSerializer KeyInfoSerializer
169         {
170             get
171             {
172                  if ( this.keyInfoSerializer == null )
173                  {
174                     lock ( this.syncObject )
175                     {
176                         if ( this.keyInfoSerializer == null )
177                         {
178                             SecurityTokenHandlerCollection sthc = ( ContainingCollection != null ) ?
179                                 ContainingCollection : SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
180                             this.keyInfoSerializer = new SecurityTokenSerializerAdapter(sthc);
181                         }
182                     }
183                  }
184
185                 return this.keyInfoSerializer;
186             }
187
188             set
189             {
190                 if (value == null)
191                 {
192                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
193                 }
194
195                 this.keyInfoSerializer = value;
196             }
197         }
198
199         /// <summary>
200         /// Gets the value if this instance can write a token.
201         /// </summary>
202         public override bool CanWriteToken
203         {
204             get { return true; }
205         }
206
207         /// <summary>
208         /// Gets or sets the <see cref="SamlSecurityTokenRequirement"/>.
209         /// </summary>
210         public SamlSecurityTokenRequirement SamlSecurityTokenRequirement
211         {
212             get
213             {
214                 return this.samlSecurityTokenRequirement;
215             }
216
217             set
218             {
219                 if (value == null)
220                 {
221                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
222                 }
223
224                 this.samlSecurityTokenRequirement = value;
225             }
226         }
227
228         /// <summary>
229         /// Creates a <see cref="SecurityKeyIdentifierClause"/> to be used as the security token reference when the token is not attached to the message.
230         /// </summary>
231         /// <param name="token">The saml token.</param>
232         /// <param name="attached">Boolean that indicates if a attached or unattached
233         /// reference needs to be created.</param>
234         /// <returns>A <see cref="SamlAssertionKeyIdentifierClause"/> instance.</returns>
235         public override SecurityKeyIdentifierClause CreateSecurityTokenReference(SecurityToken token, bool attached)
236         {
237             if (null == token)
238             {
239                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
240             }
241
242             return token.CreateKeyIdentifierClause<Saml2AssertionKeyIdentifierClause>();
243         }
244
245         /// <summary>
246         /// Creates a <see cref="SecurityToken"/> based on a information contained in the <see cref="SecurityTokenDescriptor"/>.
247         /// </summary>
248         /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that has creation information.</param>
249         /// <returns>A <see cref="SecurityToken"/> instance.</returns>
250         /// <exception cref="ArgumentNullException">Thrown if 'tokenDescriptor' is null.</exception>
251         public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor)
252         {
253             if (null == tokenDescriptor)
254             {
255                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
256             }
257
258             // Assertion/issuer
259             Saml2Assertion assertion = new Saml2Assertion(this.CreateIssuerNameIdentifier(tokenDescriptor));
260
261             // Subject
262             assertion.Subject = this.CreateSamlSubject(tokenDescriptor);
263
264             // Signature
265             assertion.SigningCredentials = this.GetSigningCredentials(tokenDescriptor);
266
267             // Conditions
268             assertion.Conditions = this.CreateConditions(tokenDescriptor.Lifetime, tokenDescriptor.AppliesToAddress, tokenDescriptor);
269
270             // Advice
271             assertion.Advice = this.CreateAdvice(tokenDescriptor);
272
273             // Statements
274             IEnumerable<Saml2Statement> statements = this.CreateStatements(tokenDescriptor);
275             if (null != statements)
276             {
277                 foreach (Saml2Statement statement in statements)
278                 {
279                     assertion.Statements.Add(statement);
280                 }
281             }
282
283             // encrypting credentials
284             assertion.EncryptingCredentials = this.GetEncryptingCredentials(tokenDescriptor);
285
286             SecurityToken token = new Saml2SecurityToken(assertion);
287
288             return token;
289         }
290
291         /// <summary>
292         /// Gets the token type identifier(s) supported by this handler.
293         /// </summary>
294         /// <returns>A collection of strings that identify the tokens this instance can handle.</returns>
295         public override string[] GetTokenTypeIdentifiers()
296         {
297             return tokenTypeIdentifiers;
298         }
299
300         /// <summary>
301         /// Validates a <see cref="Saml2SecurityToken"/>.
302         /// </summary>
303         /// <param name="token">The <see cref="Saml2SecurityToken"/> to validate.</param>
304         /// <returns>A <see cref="ReadOnlyCollection{T}"/> of <see cref="ClaimsIdentity"/> representing the identities contained in the token.</returns>
305         /// <exception cref="ArgumentNullException">The parameter 'token' is null.</exception>
306         /// <exception cref="ArgumentException">The token is not of assignable from <see cref="Saml2SecurityToken"/>.</exception>
307         /// <exception cref="InvalidOperationException">Configuration <see cref="SecurityTokenHandlerConfiguration"/>is null.</exception>
308         /// <exception cref="SecurityTokenValidationException">Thrown if Saml2SecurityToken.Assertion.IssuerToken is null.</exception>
309         /// <exception cref="SecurityTokenValidationException">Thrown if Saml2SecurityToken.Assertion.SigningToken is null.</exception>
310         /// <exception cref="InvalidOperationException">Saml2SecurityToken.Assertion is null.</exception>
311         public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
312         {
313             if (token == null)
314             {
315                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
316             }
317
318             Saml2SecurityToken samlToken = token as Saml2SecurityToken;
319             if (samlToken == null)
320             {
321                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID4151));
322             }
323
324             if (this.Configuration == null)
325             {
326                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
327             }
328
329             try
330             {
331                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.Diagnostics, SR.GetString(SR.TraceValidateToken), new SecurityTraceRecordHelper.TokenTraceRecord(token), null, null);
332
333                 if (samlToken.IssuerToken == null)
334                 {
335                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4152)));
336                 }
337
338                 if (samlToken.Assertion == null)
339                 {
340                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1034));
341                 }
342
343                 this.ValidateConditions(
344                     samlToken.Assertion.Conditions,
345                     SamlSecurityTokenRequirement.ShouldEnforceAudienceRestriction(this.Configuration.AudienceRestriction.AudienceMode, samlToken));
346
347                 //
348                 // We need something like AudienceUriMode and have a setting on Configuration to allow extensibility and custom settings
349                 // By default we only check bearer tokens
350                 //
351                 if (this.Configuration.DetectReplayedTokens)
352                 {
353                     this.DetectReplayedToken(samlToken);
354                 }
355
356                 Saml2SubjectConfirmation subjectConfirmation = samlToken.Assertion.Subject.SubjectConfirmations[0];
357                 if (subjectConfirmation.SubjectConfirmationData != null)
358                 {
359                     this.ValidateConfirmationData(subjectConfirmation.SubjectConfirmationData);
360                 }
361
362                 // If the backing token is x509, validate trust
363                 X509SecurityToken issuerToken = samlToken.IssuerToken as X509SecurityToken;
364                 if (issuerToken != null)
365                 {
366                     this.CertificateValidator.Validate(issuerToken.Certificate);
367                 }
368
369                 ClaimsIdentity claimsIdentity = null;
370
371                 if (this.samlSecurityTokenRequirement.MapToWindows)
372                 {
373                     // TFS: 153865, Microsoft WindowsIdentity does not set Authtype. I don't think that authtype should be set here anyway.
374                     // The authtype will be S4U (kerberos) it doesn't really matter that the upn arrived in a SAML token.
375                     claimsIdentity = this.CreateWindowsIdentity(this.FindUpn(claimsIdentity));
376
377                     // PARTIAL TRUST: will fail when adding claims, AddClaims is SecurityCritical.
378                     claimsIdentity.AddClaims(this.CreateClaims(samlToken).Claims);
379                 }
380                 else
381                 {
382                     claimsIdentity = this.CreateClaims(samlToken);
383                 }
384
385                 if (this.Configuration.SaveBootstrapContext)
386                 {
387                     claimsIdentity.BootstrapContext = new BootstrapContext(token, this);
388                 }
389
390                 this.TraceTokenValidationSuccess(token);
391
392                 List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1);
393                 identities.Add(claimsIdentity);
394                 return identities.AsReadOnly();
395             }
396             catch (Exception e)
397             {
398                 if (Fx.IsFatal(e))
399                 {
400                     throw;
401                 }
402
403                 this.TraceTokenValidationFailure(token, e.Message);
404                 throw e;
405             }
406         }
407
408         /// <summary>
409         /// Creates a <see cref="WindowsIdentity"/> object using the <paramref name="upn"/> value.
410         /// </summary>
411         /// <param name="upn">The upn name.</param>
412         /// <returns>A <see cref="WindowsIdentity"/> object.</returns>
413         /// <exception cref="ArgumentException">If <paramref name="upn"/> is null or empty.</exception>
414         protected virtual WindowsIdentity CreateWindowsIdentity(string upn)
415         {
416             if (string.IsNullOrEmpty(upn))
417             {
418                 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("upn");
419             }
420
421             WindowsIdentity wi = new WindowsIdentity(upn);
422
423             return new WindowsIdentity(wi.Token, AuthenticationTypes.Federation, WindowsAccountType.Normal, true);
424         }
425         
426         /// <summary>
427         /// Writes a Saml2 Token using the XmlWriter.
428         /// </summary>
429         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityToken"/>.</param>
430         /// <param name="token">The <see cref="SecurityToken"/> to serialize.</param>
431         /// <exception cref="ArgumentNullException">The input argument 'writer' or 'token' is null.</exception>
432         /// <exception cref="ArgumentException">The input argument 'token' is not a <see cref="Saml2SecurityToken"/>.</exception>
433         public override void WriteToken(XmlWriter writer, SecurityToken token)
434         {
435             if (writer == null)
436             {
437                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
438             }
439
440             if (token == null)
441             {
442                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
443             }
444
445             Saml2SecurityToken samlToken = token as Saml2SecurityToken;
446
447             if (null != samlToken)
448             {
449                 this.WriteAssertion(writer, samlToken.Assertion);
450             }
451             else
452             {
453                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID4160));
454             }
455         }
456
457         /// <summary>
458         /// Indicates whether the current XML element can be read as a token of the type handled by this instance.
459         /// </summary>
460         /// <param name="reader">An <see cref="XmlReader"/> reader positioned at a start element. The reader should not be advanced.</param>
461         /// <returns>'True' if the ReadToken method can read the element.</returns>
462         public override bool CanReadToken(XmlReader reader)
463         {
464             if (reader == null)
465             {
466                 return false;
467             }
468
469             return reader.IsStartElement(SAML2.Elements.Assertion, SAML2.Namespace)
470                || reader.IsStartElement(SAML2.Elements.EncryptedAssertion, SAML2.Namespace);
471         }
472
473         /// <summary>
474         /// Indicates if the current XML element is pointing to a KeyIdentifierClause that
475         /// can be serialized by this instance.
476         /// </summary>
477         /// <param name="reader">An <see cref="XmlReader"/> reader positioned at a start element. The reader should not be advanced.</param>
478         /// <returns>'True' if the ReadKeyIdentifierClause can read the element. 'False' otherwise.</returns>
479         public override bool CanReadKeyIdentifierClause(XmlReader reader)
480         {
481             return IsSaml2KeyIdentifierClause(reader);
482         }
483
484         /// <summary>
485         /// Indicates if the given SecurityKeyIdentifierClause can be serialized by this
486         /// instance.
487         /// </summary>
488         /// <param name="securityKeyIdentifierClause">SecurityKeyIdentifierClause to be serialized.</param>
489         /// <returns>"True' if the given SecurityKeyIdentifierClause can be serialized. 'False' otherwise.</returns>
490         public override bool CanWriteKeyIdentifierClause(SecurityKeyIdentifierClause securityKeyIdentifierClause)
491         {
492             return (securityKeyIdentifierClause is Saml2AssertionKeyIdentifierClause) ||
493                 (securityKeyIdentifierClause is WrappedSaml2AssertionKeyIdentifierClause);
494         }
495
496         /// <summary>
497         /// Reads a SecurityKeyIdentifierClause.
498         /// </summary>
499         /// <param name="reader">A <see cref="XmlReader"/> reader positioned at a <see cref="SecurityKeyIdentifierClause"/> element.</param>
500         /// <returns>A <see cref="SecurityKeyIdentifierClause"/> instance.</returns>
501         /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
502         public override SecurityKeyIdentifierClause ReadKeyIdentifierClause(XmlReader reader)
503         {
504             if (reader == null)
505             {
506                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
507             }
508
509             if (!IsSaml2KeyIdentifierClause(reader))
510             {
511                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4161));
512             }
513
514             // disallow empty
515             if (reader.IsEmptyElement)
516             {
517                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, WSSE.Elements.SecurityTokenReference, WSSE.Namespace));
518             }
519
520             try
521             {
522                 // @attributes
523                 string value;
524                 string id;
525                 byte[] nonce = null;
526                 int length = 0;
527
528                 // @wsse11:TokenType is checked by IsSaml2KeyIdentifierClause
529
530                 // @wsc:Nonce and @wsc:Length, first try WSCFeb2005
531                 value = reader.GetAttribute(WSC.Attributes.Nonce, WSC.Namespace);
532                 if (!string.IsNullOrEmpty(value))
533                 {
534                     nonce = Convert.FromBase64String(value);
535
536                     value = reader.GetAttribute(WSC.Attributes.Length, WSC.Namespace);
537                     if (!string.IsNullOrEmpty(value))
538                     {
539                         length = XmlConvert.ToInt32(value);
540                     }
541                     else
542                     {
543                         length = WSC.DefaultDerivedKeyLength;
544                     }
545                 }
546
547                 // @wsc:Nonce and @wsc:Length, now try WSC13
548                 if (null == nonce)
549                 {
550                     value = reader.GetAttribute(WSC13.Attributes.Nonce, WSC13.Namespace);
551                     if (!string.IsNullOrEmpty(value))
552                     {
553                         nonce = Convert.FromBase64String(value);
554
555                         value = reader.GetAttribute(WSC13.Attributes.Length, WSC13.Namespace);
556                         if (!string.IsNullOrEmpty(value))
557                         {
558                             length = XmlConvert.ToInt32(value);
559                         }
560                         else
561                         {
562                             length = WSC13.DefaultDerivedKeyLength;
563                         }
564                     }
565                 }
566
567                 // <wsse:SecurityTokenReference> content begins
568                 reader.Read();
569
570                 // <wsse:Reference> - throw exception
571                 if (reader.IsStartElement(WSSE.Elements.Reference, WSSE.Namespace))
572                 {
573                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4126));
574                 }
575
576                 // <wsse:KeyIdentifier>
577                 if (!reader.IsStartElement(WSSE.Elements.KeyIdentifier, WSSE.Namespace))
578                 {
579                     reader.ReadStartElement(WSSE.Elements.KeyIdentifier, WSSE.Namespace);
580                 }
581
582                 // @ValueType - required
583                 value = reader.GetAttribute(WSSE.Attributes.ValueType);
584                 if (string.IsNullOrEmpty(value))
585                 {
586                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, WSSE.Attributes.ValueType, WSSE.Elements.KeyIdentifier));
587                 }
588
589                 if (!StringComparer.Ordinal.Equals(TokenProfile11ValueType, value))
590                 {
591                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4127, value));
592                 }
593
594                 // <wsse:KeyIdentifier> Content is string </wsse:KeyIdentifier>
595                 id = reader.ReadElementString();
596
597                 // </wsse:SecurityTokenReference>
598                 reader.ReadEndElement();
599
600                 return new Saml2AssertionKeyIdentifierClause(id, nonce, length);
601             }
602             catch (Exception inner)
603             {
604                 // Wrap common data-validation exceptions that may have bubbled up
605                 if (inner is FormatException
606                     || inner is ArgumentException
607                     || inner is InvalidOperationException
608                     || inner is OverflowException)
609                 {
610                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4125), inner);
611                 }
612                 else
613                 {
614                     throw;
615                 }
616             }
617         }
618
619         /// <summary>
620         /// Reads a SAML 2.0 token from the XmlReader.
621         /// </summary>
622         /// <param name="reader">A <see cref="XmlReader"/> reader positioned at a <see cref="Saml2SecurityToken"/> element.</param>
623         /// <returns>An instance of <see cref="Saml2SecurityToken"/>.</returns>
624         /// <exception cref="InvalidOperationException">Is thrown if 'Configuration', 'Configruation.IssuerTokenResolver' or 'Configuration.ServiceTokenResolver is null.</exception>
625         public override SecurityToken ReadToken(XmlReader reader)
626         {
627             if (Configuration == null)
628             {
629                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
630             }
631
632             if (Configuration.IssuerTokenResolver == null)
633             {
634                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
635             }
636
637             if (Configuration.ServiceTokenResolver == null)
638             {
639                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
640             }
641
642             Saml2Assertion assertion = this.ReadAssertion(reader);
643
644             ReadOnlyCollection<SecurityKey> keys = this.ResolveSecurityKeys(assertion, Configuration.ServiceTokenResolver);
645
646             // Resolve signing token if one is present. It may be deferred and signed by reference.
647             SecurityToken issuerToken;
648             this.TryResolveIssuerToken(assertion, Configuration.IssuerTokenResolver, out issuerToken);
649
650             return new Saml2SecurityToken(assertion, keys, issuerToken);
651         }
652
653         /// <summary>
654         /// Serializes a Saml2AssertionKeyIdentifierClause to the XmlWriter.
655         /// </summary>
656         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifierClause"/>.</param>
657         /// <param name="securityKeyIdentifierClause">The <see cref="SecurityKeyIdentifierClause"/> to be serialized.</param>
658         /// <exception cref="ArgumentNullException">Input parameter 'writer' or 'securityKeyIdentifierClause' is null.</exception>
659         public override void WriteKeyIdentifierClause(XmlWriter writer, SecurityKeyIdentifierClause securityKeyIdentifierClause)
660         {
661             if (writer == null)
662             {
663                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
664             }
665
666             if (securityKeyIdentifierClause == null)
667             {
668                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyIdentifierClause");
669             }
670
671             Saml2AssertionKeyIdentifierClause samlClause = null;
672             WrappedSaml2AssertionKeyIdentifierClause wrappedClause = securityKeyIdentifierClause as WrappedSaml2AssertionKeyIdentifierClause;
673
674             if (wrappedClause != null)
675             {
676                 samlClause = wrappedClause.WrappedClause;
677             }
678             else
679             {
680                 samlClause = securityKeyIdentifierClause as Saml2AssertionKeyIdentifierClause;
681             }
682
683             if (null == samlClause)
684             {
685                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("keyIdentifierClause", SR.GetString(SR.ID4162));
686             }
687
688             // <wsse:SecurityTokenReference>
689             writer.WriteStartElement(WSSE.Elements.SecurityTokenReference, WSSE.Namespace);
690
691             // @wsc:Nonce
692             byte[] nonce = samlClause.GetDerivationNonce();
693             if (null != nonce)
694             {
695                 writer.WriteAttributeString(WSC.Attributes.Nonce, WSC.Namespace, Convert.ToBase64String(nonce));
696
697                 int length = samlClause.DerivationLength;
698
699                 // Don't emit @wsc:Length since it's not actually in the spec/schema
700                 if (length != 0 && length != WSC.DefaultDerivedKeyLength)
701                 {
702                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
703                         new InvalidOperationException(SR.GetString(SR.ID4129)));
704                 }
705             }
706
707             // @wsse11:TokenType
708             writer.WriteAttributeString(WSSE11.Attributes.TokenType, WSSE11.Namespace, SecurityTokenTypes.OasisWssSaml2TokenProfile11);
709
710             // <wsse:KeyIdentifier>
711             writer.WriteStartElement(WSSE.Elements.KeyIdentifier, WSSE.Namespace);
712
713             // @ValueType
714             writer.WriteAttributeString(WSSE.Attributes.ValueType, TokenProfile11ValueType);
715
716             // ID is the string content
717             writer.WriteString(samlClause.Id);
718
719             // </wsse:KeyIdentifier>
720             writer.WriteEndElement();
721
722             // </wsse:SecurityTokenReference>
723             writer.WriteEndElement();
724         }
725
726         internal static XmlDictionaryReader CreatePlaintextReaderFromEncryptedData(
727                         XmlDictionaryReader reader,
728                         SecurityTokenResolver serviceTokenResolver,
729                         SecurityTokenSerializer keyInfoSerializer,
730                         Collection<EncryptedKeyIdentifierClause> clauses,
731                         out EncryptingCredentials encryptingCredentials)
732         {
733             if (reader == null)
734             {
735                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
736             }
737
738             reader.MoveToContent();
739             if (reader.IsEmptyElement)
740             {
741 #pragma warning suppress 56504 // bogus - thinks reader.LocalName, reader.NamespaceURI need validation
742                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
743             }
744
745             encryptingCredentials = null;
746
747             XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.EncryptedElementType, Saml2Constants.Namespace);
748
749             reader.ReadStartElement();
750             EncryptedDataElement encryptedData = new EncryptedDataElement(keyInfoSerializer);
751
752             // <xenc:EncryptedData> 1
753             encryptedData.ReadXml(reader);
754
755             // <xenc:EncryptedKey> 0-oo
756             reader.MoveToContent();
757             while (reader.IsStartElement(XmlEncryptionConstants.Elements.EncryptedKey, XmlEncryptionConstants.Namespace))
758             {
759                 SecurityKeyIdentifierClause skic;
760                 if (keyInfoSerializer.CanReadKeyIdentifierClause(reader))
761                 {
762                     skic = keyInfoSerializer.ReadKeyIdentifierClause(reader);
763                 }
764                 else
765                 {
766                     EncryptedKeyElement encryptedKey = new EncryptedKeyElement(keyInfoSerializer);
767                     encryptedKey.ReadXml(reader);
768                     skic = encryptedKey.GetClause();
769                 }
770
771                 EncryptedKeyIdentifierClause encryptedKeyClause = skic as EncryptedKeyIdentifierClause;
772                 if (null == encryptedKeyClause)
773                 {
774                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4172));
775                 }
776
777                 clauses.Add(encryptedKeyClause);
778             }
779
780             reader.ReadEndElement();
781
782             // Try to resolve the decryption key from both the embedded 
783             // KeyInfo and any external clauses
784             SecurityKey decryptionKey = null;
785             SecurityKeyIdentifierClause matchingClause = null;
786
787             foreach (SecurityKeyIdentifierClause clause in encryptedData.KeyIdentifier)
788             {
789                 if (serviceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey))
790                 {
791                     matchingClause = clause;
792                     break;
793                 }
794             }
795
796             if (null == decryptionKey)
797             {
798                 foreach (SecurityKeyIdentifierClause clause in clauses)
799                 {
800                     if (serviceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey))
801                     {
802                         matchingClause = clause;
803                         break;
804                     }
805                 }
806             }
807
808             if (null == decryptionKey)
809             {
810                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
811                     new EncryptedTokenDecryptionFailedException());
812             }
813
814             // Need a symmetric key
815             SymmetricSecurityKey symmetricKey = decryptionKey as SymmetricSecurityKey;
816             if (null == symmetricKey)
817             {
818                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
819                     new SecurityTokenException(SR.GetString(SR.ID4023)));
820             }
821
822             // Do the actual decryption
823             SymmetricAlgorithm decryptor = symmetricKey.GetSymmetricAlgorithm(encryptedData.Algorithm);
824             byte[] plainText = encryptedData.Decrypt(decryptor);
825
826             // Save off the encrypting credentials for roundtrip
827             encryptingCredentials = new ReceivedEncryptingCredentials(decryptionKey, new SecurityKeyIdentifier(matchingClause), encryptedData.Algorithm);
828
829             return XmlDictionaryReader.CreateTextReader(plainText, reader.Quotas);
830         }
831
832         // Wraps common data validation exceptions with an XmlException 
833         // associated with the failing reader
834         internal static Exception TryWrapReadException(XmlReader reader, Exception inner)
835         {
836             if (inner is FormatException
837                 || inner is ArgumentException
838                 || inner is InvalidOperationException
839                 || inner is OverflowException)
840             {
841                 return DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4125), inner);
842             }
843
844             return null;
845         }
846
847         /// <summary>
848         /// Indicates if the current XML element is pointing to a Saml2SecurityKeyIdentifierClause.
849         /// </summary>
850         /// <param name="reader">An <see cref="XmlReader"/> reader.</param>
851         /// <returns>'True' if reader contains a <see cref="Saml2SecurityKeyIdentifierClause"/>. 'False' otherwise.</returns>
852         internal static bool IsSaml2KeyIdentifierClause(XmlReader reader)
853         {
854             if (!reader.IsStartElement(WSSE.Elements.SecurityTokenReference, WSSE.Namespace))
855             {
856                 return false;
857             }
858
859             string tokenType = reader.GetAttribute(WSSE11.Attributes.TokenType, WSSE11.Namespace);
860             return tokenTypeIdentifiers.Contains(tokenType);
861         }
862
863         /// <summary>
864         /// Indicates if the current XML element is pointing to a Saml2Assertion.
865         /// </summary>
866         /// <param name="reader">A reader that may contain a <see cref="Saml2Assertion"/>.</param>
867         /// <returns>'True' if reader contains a <see cref="Saml2Assertion"/>. 'False' otherwise.</returns>
868         internal static bool IsSaml2Assertion(XmlReader reader)
869         {
870             return reader.IsStartElement(SAML2.Elements.Assertion, SAML2.Namespace)
871                || reader.IsStartElement(SAML2.Elements.EncryptedAssertion, SAML2.Namespace);
872         }
873
874         // Read an element that must not contain content.
875         internal static void ReadEmptyContentElement(XmlReader reader)
876         {
877             bool isEmpty = reader.IsEmptyElement;
878             reader.Read();
879             if (!isEmpty)
880             {
881                 reader.ReadEndElement();
882             }
883         }
884
885         internal static Saml2Id ReadSimpleNCNameElement(XmlReader reader)
886         {
887             Fx.Assert(reader.IsStartElement(), "reader is not on start element");
888
889             try
890             {
891                 if (reader.IsEmptyElement)
892                 {
893                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
894                 }
895
896                 XmlUtil.ValidateXsiType(reader, "NCName", XmlSchema.Namespace);
897
898                 reader.MoveToElement();
899                 string value = reader.ReadElementContentAsString();
900
901                 return new Saml2Id(value);
902             }
903             catch (Exception e)
904             {
905                 if (System.Runtime.Fx.IsFatal(e))
906                     throw;
907                 
908                 Exception wrapped = TryWrapReadException(reader, e);
909                 if (null == wrapped)
910                 {
911                     throw;
912                 }
913                 else
914                 {
915                     throw wrapped;
916                 }
917             }
918         }
919
920         // Reads an element with simple content anyURI. Since this is SAML, 
921         // restricts the URI to absolute.
922         internal static Uri ReadSimpleUriElement(XmlReader reader)
923         {
924             return ReadSimpleUriElement(reader, UriKind.Absolute);
925         }
926
927         // Reads an element with simple content anyURI where a UriKind can be specified
928         internal static Uri ReadSimpleUriElement(XmlReader reader, UriKind kind)
929         {
930             return ReadSimpleUriElement(reader, kind, false);
931         }
932
933         // allow lax reading of relative URIs in some instances for interop
934         internal static Uri ReadSimpleUriElement(XmlReader reader, UriKind kind, bool allowLaxReading)
935         {
936             Fx.Assert(reader.IsStartElement(), "reader is not on start element");
937
938             try
939             {
940                 if (reader.IsEmptyElement)
941                 {
942                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
943                 }
944
945                 XmlUtil.ValidateXsiType(reader, "anyURI", XmlSchema.Namespace);
946
947                 reader.MoveToElement();
948                 string value = reader.ReadElementContentAsString();
949
950                 if (string.IsNullOrEmpty(value))
951                 {
952                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0022));
953                 }
954
955                 if (!allowLaxReading && !UriUtil.CanCreateValidUri(value, kind))
956                 {
957                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(kind == UriKind.RelativeOrAbsolute ? SR.ID0019 : SR.ID0013));
958                 }
959
960                 return new Uri(value, kind);
961             }
962             catch (Exception e)
963             {
964                 if (System.Runtime.Fx.IsFatal(e))
965                     throw;
966                 
967                 Exception wrapped = TryWrapReadException(reader, e);
968                 if (null == wrapped)
969                 {
970                     throw;
971                 }
972                 else
973                 {
974                     throw wrapped;
975                 }
976             }
977         }
978
979         /// <summary>
980         /// Creates the conditions for the assertion.
981         /// </summary>
982         /// <remarks>
983         /// <para>
984         /// Generally, conditions should be included in assertions to limit the 
985         /// impact of misuse of the assertion. Specifying the NotBefore and 
986         /// NotOnOrAfter conditions can limit the period of vulnerability in 
987         /// the case of a compromised assertion. The AudienceRestrictionCondition
988         /// can be used to explicitly state the intended relying party or parties
989         /// of the assertion, which coupled with appropriate audience restriction
990         /// enforcement at relying parties can help to mitigate spoofing attacks
991         /// between relying parties.
992         /// </para>
993         /// <para>
994         /// The default implementation creates NotBefore and NotOnOrAfter conditions
995         /// based on the tokenDescriptor.Lifetime. It will also generate an 
996         /// AudienceRestrictionCondition limiting consumption of the assertion to 
997         /// tokenDescriptor.Scope.Address.
998         /// </para>
999         /// </remarks>
1000         /// <param name="tokenLifetime">Lifetime of the Token.</param>
1001         /// <param name="relyingPartyAddress">The endpoint address to who the token is created. The address
1002         /// is modeled as an AudienceRestriction condition.</param>
1003         /// <param name="tokenDescriptor">The token descriptor.</param>
1004         /// <returns>A Saml2Conditions object.</returns>
1005         protected virtual Saml2Conditions CreateConditions(Lifetime tokenLifetime, string relyingPartyAddress, SecurityTokenDescriptor tokenDescriptor)
1006         {
1007             bool hasLifetime = null != tokenLifetime;
1008             bool hasScope = !string.IsNullOrEmpty(relyingPartyAddress);
1009             if (!hasLifetime && !hasScope)
1010             {
1011                 return null;
1012             }
1013
1014             Saml2Conditions conditions = new Saml2Conditions();
1015             if (hasLifetime)
1016             {
1017                 conditions.NotBefore = tokenLifetime.Created;
1018                 conditions.NotOnOrAfter = tokenLifetime.Expires;
1019             }
1020
1021             if (hasScope)
1022             {
1023                 conditions.AudienceRestrictions.Add(new Saml2AudienceRestriction(new Uri(relyingPartyAddress)));
1024             }
1025
1026             return conditions;
1027         }
1028
1029         /// <summary>
1030         /// Creates the advice for the assertion.
1031         /// </summary>
1032         /// <remarks>
1033         /// By default, this method returns null.
1034         /// </remarks>
1035         /// <param name="tokenDescriptor">The token descriptor.</param>
1036         /// <returns>A Saml2Advice object, default is null.</returns>
1037         protected virtual Saml2Advice CreateAdvice(SecurityTokenDescriptor tokenDescriptor)
1038         {
1039             return null;
1040         }
1041
1042         /// <summary>
1043         /// Creates a name identifier that identifies the assertion issuer.
1044         /// </summary>
1045         /// <remarks>
1046         /// <para>
1047         /// SAML2 assertions must contain a name identifier for the issuer. 
1048         /// This method may not return null.
1049         /// </para>
1050         /// <para>
1051         /// The default implementation creates a simple name identifier 
1052         /// from the tokenDescriptor.Issuer. 
1053         /// </para>
1054         /// </remarks>
1055         /// <param name="tokenDescriptor">The token descriptor.</param>
1056         /// <returns>A <see cref="Saml2NameIdentifier"/> from the tokenDescriptor</returns>
1057         protected virtual Saml2NameIdentifier CreateIssuerNameIdentifier(SecurityTokenDescriptor tokenDescriptor)
1058         {
1059             if (null == tokenDescriptor)
1060             {
1061                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1062             }
1063
1064             string issuerName = tokenDescriptor.TokenIssuerName;
1065
1066             // Must have an issuer
1067             if (string.IsNullOrEmpty(issuerName))
1068             {
1069                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4138)));
1070             }
1071
1072             return new Saml2NameIdentifier(issuerName);
1073         }
1074
1075         /// <summary>
1076         /// Generates a Saml2Attribute from a claim.
1077         /// </summary>
1078         /// <param name="claim">The <see cref="Claim"/> from which to generate a <see cref="Saml2Attribute"/>.</param>
1079         /// <param name="tokenDescriptor">Contains all the information that is used in token issuance.</param>
1080         /// <returns>A <see cref="Saml2Attribute"/> based on the claim.</returns>
1081         /// <exception cref="ArgumentNullException">The parameter 'claim' is null.</exception>
1082         protected virtual Saml2Attribute CreateAttribute(Claim claim, SecurityTokenDescriptor tokenDescriptor)
1083         {
1084             if (claim == null)
1085             {
1086                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim");
1087             }
1088
1089             Saml2Attribute attribute = new Saml2Attribute(claim.Type, claim.Value);
1090             if (!StringComparer.Ordinal.Equals(ClaimsIdentity.DefaultIssuer, claim.OriginalIssuer))
1091             {
1092                 attribute.OriginalIssuer = claim.OriginalIssuer;
1093             }
1094
1095             attribute.AttributeValueXsiType = claim.ValueType;
1096
1097             if (claim.Properties.ContainsKey(ClaimProperties.SamlAttributeNameFormat))
1098             {
1099                 string nameFormat = claim.Properties[ClaimProperties.SamlAttributeNameFormat];
1100                 if (!UriUtil.CanCreateValidUri(nameFormat, UriKind.Absolute))
1101                 {
1102                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("nameFormat", SR.GetString(SR.ID0013));
1103                 }
1104
1105                 attribute.NameFormat = new Uri(nameFormat);
1106             }
1107
1108             if (claim.Properties.ContainsKey(ClaimProperties.SamlAttributeDisplayName))
1109             {
1110                 attribute.FriendlyName = claim.Properties[ClaimProperties.SamlAttributeDisplayName];
1111             }
1112
1113             return attribute;
1114         }
1115
1116         /// <summary>
1117         /// Creates <see cref="Saml2AttributeStatement"/> from a <see cref="SecurityTokenDescriptor"/> and a <see cref="ClaimsIdentity"/>
1118         /// </summary>
1119         /// <remarks>This method may return null if the token descriptor does not contain any subject or the subject does not have any claims.
1120         /// </remarks>
1121         /// <param name="subject">The <see cref="ClaimsIdentity"/> that contains claims which will be converted to SAML Attributes.</param>
1122         /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that contains information on building the <see cref="Saml2AttributeStatement"/>.</param>
1123         /// <returns>A Saml2AttributeStatement.</returns>
1124         protected virtual Saml2AttributeStatement CreateAttributeStatement(ClaimsIdentity subject, SecurityTokenDescriptor tokenDescriptor)
1125         {
1126             if (subject == null)
1127             {
1128                 return null;
1129             }
1130
1131             // We treat everything else as an Attribute except the nameId claim, which is already processed
1132             // for saml2subject
1133             // AuthenticationInstant and AuthenticationType are not converted to Claims
1134             if (subject.Claims != null)
1135             {
1136                 List<Saml2Attribute> attributes = new List<Saml2Attribute>();
1137                 foreach (Claim claim in subject.Claims)
1138                 {
1139                     if (claim != null && claim.Type != ClaimTypes.NameIdentifier)
1140                     {
1141                         switch (claim.Type)
1142                         {
1143                             case ClaimTypes.AuthenticationInstant:
1144                             case ClaimTypes.AuthenticationMethod:
1145                                 break;
1146                             default:
1147                                 attributes.Add(this.CreateAttribute(claim, tokenDescriptor));
1148                                 break;
1149                         }
1150                     }
1151                 }
1152
1153                 this.AddDelegateToAttributes(subject, attributes, tokenDescriptor);
1154
1155                 ICollection<Saml2Attribute> collectedAttributes = this.CollectAttributeValues(attributes);
1156                 if (collectedAttributes.Count > 0)
1157                 {
1158                     return new Saml2AttributeStatement(collectedAttributes);
1159                 }
1160             }
1161
1162             return null;
1163         }
1164
1165         /// <summary>
1166         /// Collects attributes with a common claim type, claim value type, and original issuer into a
1167         /// single attribute with multiple values.
1168         /// </summary>
1169         /// <param name="attributes">List of attributes generated from claims.</param>
1170         /// <returns>A <see cref="ICollection{T}"/> of <see cref="Saml2Attribute"/> with common attributes collected into value lists.</returns>
1171         protected virtual ICollection<Saml2Attribute> CollectAttributeValues(ICollection<Saml2Attribute> attributes)
1172         {
1173             Dictionary<SamlAttributeKeyComparer.AttributeKey, Saml2Attribute> distinctAttributes = new Dictionary<SamlAttributeKeyComparer.AttributeKey, Saml2Attribute>(attributes.Count, new SamlAttributeKeyComparer());
1174
1175             // Use unique attribute if name, value type, or issuer differ
1176             foreach (Saml2Attribute saml2Attribute in attributes)
1177             {
1178                 if (saml2Attribute != null)
1179                 {
1180                     SamlAttributeKeyComparer.AttributeKey attributeKey = new SamlAttributeKeyComparer.AttributeKey(saml2Attribute);
1181
1182                     if (distinctAttributes.ContainsKey(attributeKey))
1183                     {
1184                         foreach (string attributeValue in saml2Attribute.Values)
1185                         {
1186                             distinctAttributes[attributeKey].Values.Add(attributeValue);
1187                         }
1188                     }
1189                     else
1190                     {
1191                         distinctAttributes.Add(attributeKey, saml2Attribute);
1192                     }
1193                 }
1194             }
1195
1196             return distinctAttributes.Values;
1197         }
1198
1199         /// <summary>
1200         /// Adds all the delegates associated with the subject into the attribute collection.
1201         /// </summary>
1202         /// <param name="subject">The delegate of this <see cref="ClaimsIdentity"/> will be serialized into a <see cref="Saml2Attribute"/>.</param>
1203         /// <param name="attributes">A <see cref="ICollection{T}"/> of <see cref="Saml2Attribute"/>.</param>
1204         /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that contains information on building the delegate.</param>
1205         protected virtual void AddDelegateToAttributes(ClaimsIdentity subject, ICollection<Saml2Attribute> attributes, SecurityTokenDescriptor tokenDescriptor)
1206         {
1207             if (subject == null)
1208             {
1209                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1210             }
1211
1212             if (tokenDescriptor == null)
1213             {
1214                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1215             }
1216
1217             if (subject.Actor == null)
1218             {
1219                 return;
1220             }
1221
1222             List<Saml2Attribute> actingAsAttributes = new List<Saml2Attribute>();
1223
1224             foreach (Claim claim in subject.Actor.Claims)
1225             {
1226                 if (claim != null)
1227                 {
1228                     actingAsAttributes.Add(this.CreateAttribute(claim, tokenDescriptor));
1229                 }
1230             }
1231
1232             this.AddDelegateToAttributes(subject.Actor, actingAsAttributes, tokenDescriptor);
1233
1234             ICollection<Saml2Attribute> collectedAttributes = this.CollectAttributeValues(actingAsAttributes);
1235             attributes.Add(this.CreateAttribute(new Claim(ClaimTypes.Actor, this.CreateXmlStringFromAttributes(collectedAttributes), ClaimValueTypes.String), tokenDescriptor));
1236         }
1237
1238         /// <summary>
1239         /// Builds an XML formatted string from a collection of SAML attributes that represent the Actor. 
1240         /// </summary>
1241         /// <param name="attributes">An enumeration of Saml2Attributes.</param>
1242         /// <returns>A well-formed XML string.</returns>
1243         /// <remarks>The string is of the form ""&lt;Actor&gt;&lt;Attribute name, ns&gt;&lt;AttributeValue&gt;...&lt;/AttributeValue&gt;, ...&lt;/Attribute&gt;...&lt;/Actor&gt;"</remarks>
1244         protected virtual string CreateXmlStringFromAttributes(IEnumerable<Saml2Attribute> attributes)
1245         {
1246             bool actorElementWritten = false;
1247
1248             using (MemoryStream ms = new MemoryStream())
1249             {
1250                 using (XmlDictionaryWriter dicWriter = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8, false))
1251                 {
1252                     foreach (Saml2Attribute samlAttribute in attributes)
1253                     {
1254                         if (samlAttribute != null)
1255                         {
1256                             if (!actorElementWritten)
1257                             {
1258                                 dicWriter.WriteStartElement(Actor);
1259                                 actorElementWritten = true;
1260                             }
1261
1262                             this.WriteAttribute(dicWriter, samlAttribute);
1263                         }
1264                     }
1265
1266                     if (actorElementWritten)
1267                     {
1268                         dicWriter.WriteEndElement();
1269                     }
1270
1271                     dicWriter.Flush();
1272                 }
1273
1274                 return Encoding.UTF8.GetString(ms.ToArray());
1275             }
1276         }
1277
1278         /// <summary>
1279         /// Creates an <see cref="IEnumerable{T}"/> of <see cref="Saml2Statement"/> to be included in the assertion.
1280         /// </summary>
1281         /// <remarks>
1282         /// <para>
1283         /// Statements are not required in a SAML2 assertion. This method may
1284         /// return an empty collection.
1285         /// </para>
1286         /// </remarks>
1287         /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that contains information on creating the <see cref="Saml2Statement"/>.</param>
1288         /// <returns>An enumeration of Saml2Statements.</returns>
1289         protected virtual IEnumerable<Saml2Statement> CreateStatements(SecurityTokenDescriptor tokenDescriptor)
1290         {
1291             if (tokenDescriptor == null)
1292             {
1293                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1294             }
1295
1296             Collection<Saml2Statement> statements = new Collection<Saml2Statement>();
1297
1298             Saml2AttributeStatement attributeStatement = this.CreateAttributeStatement(tokenDescriptor.Subject, tokenDescriptor);
1299             if (attributeStatement != null)
1300             {
1301                 statements.Add(attributeStatement);
1302             }
1303
1304             Saml2AuthenticationStatement authenticationStatement = this.CreateAuthenticationStatement(tokenDescriptor.AuthenticationInfo, tokenDescriptor);
1305             if (authenticationStatement != null)
1306             {
1307                 statements.Add(authenticationStatement);
1308             }
1309
1310             return statements;
1311         }
1312
1313         /// <summary>
1314         /// Given an AuthenticationInformation object, this routine creates a Saml2AuthenticationStatement
1315         /// to be added to the Saml2Assertion that is produced by the factory.
1316         /// </summary>
1317         /// <param name="authInfo">
1318         /// An AuthenticationInformation object containing the state to be wrapped as a Saml2AuthenticationStatement
1319         /// object.
1320         /// </param>
1321         /// <param name="tokenDescriptor">The token descriptor.</param>
1322         /// <returns>
1323         /// The Saml2AuthenticationStatement to add to the assertion being created or null to ignore the AuthenticationInformation
1324         /// being wrapped as a statement.
1325         /// </returns>
1326         protected virtual Saml2AuthenticationStatement CreateAuthenticationStatement(AuthenticationInformation authInfo, SecurityTokenDescriptor tokenDescriptor)
1327         {
1328             if (tokenDescriptor == null)
1329             {
1330                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1331             }
1332
1333             if (tokenDescriptor.Subject == null)
1334             {
1335                 return null;
1336             }
1337
1338             string authenticationMethod = null;
1339             string authenticationInstant = null;
1340
1341             // Search for an Authentication Claim.
1342             IEnumerable<Claim> claimCollection = from c in tokenDescriptor.Subject.Claims where c.Type == ClaimTypes.AuthenticationMethod select c;
1343             if (claimCollection.Count<Claim>() > 0)
1344             {
1345                 // We support only one authentication statement and hence we just pick the first authentication type
1346                 // claim found in the claim collection. Since the spec allows multiple Auth Statements, 
1347                 // we do not throw an error.
1348                 authenticationMethod = claimCollection.First<Claim>().Value;
1349             }
1350
1351             claimCollection = from c in tokenDescriptor.Subject.Claims where c.Type == ClaimTypes.AuthenticationInstant select c;
1352
1353             if (claimCollection.Count<Claim>() > 0)
1354             {
1355                 authenticationInstant = claimCollection.First<Claim>().Value;
1356             }
1357
1358             if (authenticationMethod == null && authenticationInstant == null)
1359             {
1360                 return null;
1361             }
1362             else if (authenticationMethod == null)
1363             {
1364                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationMethod", "SAML2"));
1365             }
1366             else if (authenticationInstant == null)
1367             {
1368                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationInstant", "SAML2"));
1369             }
1370
1371             Uri saml2AuthenticationClass;
1372             if (!UriUtil.TryCreateValidUri(this.DenormalizeAuthenticationType(authenticationMethod), UriKind.Absolute, out saml2AuthenticationClass))
1373             {
1374                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4185, authenticationMethod));
1375             }
1376
1377             Saml2AuthenticationContext authCtx = new Saml2AuthenticationContext(saml2AuthenticationClass);
1378             DateTime authInstantTime = DateTime.ParseExact(authenticationInstant, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1379             Saml2AuthenticationStatement authnStatement = new Saml2AuthenticationStatement(authCtx, authInstantTime);
1380
1381             if (authInfo != null)
1382             {
1383                 if (!string.IsNullOrEmpty(authInfo.DnsName)
1384                     || !string.IsNullOrEmpty(authInfo.Address))
1385                 {
1386                     authnStatement.SubjectLocality
1387                         = new Saml2SubjectLocality(authInfo.Address, authInfo.DnsName);
1388                 }
1389
1390                 if (!string.IsNullOrEmpty(authInfo.Session))
1391                 {
1392                     authnStatement.SessionIndex = authInfo.Session;
1393                 }
1394
1395                 authnStatement.SessionNotOnOrAfter = authInfo.NotOnOrAfter;
1396             }
1397
1398             return authnStatement;
1399         }
1400
1401         /// <summary>
1402         /// Creates a SAML2 subject of the assertion.
1403         /// </summary>
1404         /// <param name="tokenDescriptor">The security token descriptor to create the subject.</param>
1405         /// <exception cref="ArgumentNullException">Thrown when 'tokenDescriptor' is null.</exception>
1406         /// <returns>A Saml2Subject.</returns>
1407         protected virtual Saml2Subject CreateSamlSubject(SecurityTokenDescriptor tokenDescriptor)
1408         {
1409             if (null == tokenDescriptor)
1410             {
1411                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1412             }
1413
1414             Saml2Subject saml2Subject = new Saml2Subject();
1415
1416             // Look for name identifier claims
1417             string nameIdentifierClaim = null;
1418             string nameIdentifierFormat = null;
1419             string nameIdentifierNameQualifier = null;
1420             string nameIdentifierSpProviderId = null;
1421             string nameIdentifierSpNameQualifier = null;
1422
1423             if (tokenDescriptor.Subject != null && tokenDescriptor.Subject.Claims != null)
1424             {
1425                 foreach (Claim claim in tokenDescriptor.Subject.Claims)
1426                 {
1427                     if (claim.Type == ClaimTypes.NameIdentifier)
1428                     {
1429                         // Do not allow multiple name identifier claim.
1430                         if (null != nameIdentifierClaim)
1431                         {
1432                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4139)));
1433                         }
1434
1435                         nameIdentifierClaim = claim.Value;
1436
1437                         if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierFormat))
1438                         {
1439                             nameIdentifierFormat = claim.Properties[ClaimProperties.SamlNameIdentifierFormat];
1440                         }
1441
1442                         if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierNameQualifier))
1443                         {
1444                             nameIdentifierNameQualifier = claim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier];
1445                         }
1446
1447                         if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierSPNameQualifier))
1448                         {
1449                             nameIdentifierSpNameQualifier = claim.Properties[ClaimProperties.SamlNameIdentifierSPNameQualifier];
1450                         }
1451
1452                         if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierSPProvidedId))
1453                         {
1454                             nameIdentifierSpProviderId = claim.Properties[ClaimProperties.SamlNameIdentifierSPProvidedId];
1455                         }
1456                     }
1457                 }
1458             }
1459
1460             if (nameIdentifierClaim != null)
1461             {
1462                 Saml2NameIdentifier nameIdentifier = new Saml2NameIdentifier(nameIdentifierClaim);
1463
1464                 if (nameIdentifierFormat != null && UriUtil.CanCreateValidUri(nameIdentifierFormat, UriKind.Absolute))
1465                 {
1466                     nameIdentifier.Format = new Uri(nameIdentifierFormat);
1467                 }
1468
1469                 nameIdentifier.NameQualifier = nameIdentifierNameQualifier;
1470                 nameIdentifier.SPNameQualifier = nameIdentifierSpNameQualifier;
1471                 nameIdentifier.SPProvidedId = nameIdentifierSpProviderId;
1472
1473                 saml2Subject.NameId = nameIdentifier;
1474             }
1475
1476             // Add subject confirmation data
1477             Saml2SubjectConfirmation subjectConfirmation;
1478             if (null == tokenDescriptor.Proof)
1479             {
1480                 subjectConfirmation = new Saml2SubjectConfirmation(Saml2Constants.ConfirmationMethods.Bearer);
1481             }
1482             else
1483             {
1484                 subjectConfirmation = new Saml2SubjectConfirmation(Saml2Constants.ConfirmationMethods.HolderOfKey, new Saml2SubjectConfirmationData());
1485                 subjectConfirmation.SubjectConfirmationData.KeyIdentifiers.Add(tokenDescriptor.Proof.KeyIdentifier);
1486             }
1487
1488             saml2Subject.SubjectConfirmations.Add(subjectConfirmation);
1489
1490             return saml2Subject;
1491         }
1492
1493         /// <summary>
1494         /// Override this method to change the token encrypting credentials. 
1495         /// </summary>
1496         /// <param name="tokenDescriptor">Retrieve some scope encrypting credentials from the Scope object</param>
1497         /// <returns>the token encrypting credentials</returns>
1498         /// <exception cref="ArgumentNullException">When the given tokenDescriptor is null</exception>
1499         protected virtual EncryptingCredentials GetEncryptingCredentials(SecurityTokenDescriptor tokenDescriptor)
1500         {
1501             if (null == tokenDescriptor)
1502             {
1503                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1504             }
1505
1506             EncryptingCredentials encryptingCredentials = null;
1507
1508             if (null != tokenDescriptor.EncryptingCredentials)
1509             {
1510                 encryptingCredentials = tokenDescriptor.EncryptingCredentials;
1511                 if (encryptingCredentials.SecurityKey is AsymmetricSecurityKey)
1512                 {
1513                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4178)));
1514                 }
1515             }
1516
1517             return encryptingCredentials;
1518         }
1519
1520         /// <summary>
1521         /// Gets the credentials for the signing the assertion.
1522         /// </summary>
1523         /// <remarks>
1524         /// <para>
1525         /// SAML2 assertions used as security tokens should be signed.
1526         /// </para>
1527         /// <para>
1528         /// The default implementation uses the 
1529         /// tokenDescriptor.Scope.SigningCredentials.
1530         /// </para>
1531         /// </remarks>
1532         /// <param name="tokenDescriptor">The token descriptor.</param>
1533         /// <returns>The signing credential.</returns>
1534         protected virtual SigningCredentials GetSigningCredentials(SecurityTokenDescriptor tokenDescriptor)
1535         {
1536             if (null == tokenDescriptor)
1537             {
1538                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1539             }
1540
1541             return tokenDescriptor.SigningCredentials;
1542         }
1543
1544         /// <summary>
1545         /// Rejects tokens that are not valid. 
1546         /// </summary>
1547         /// <remarks>
1548         /// The token may not be valid for a number of reasons. For example, the 
1549         /// current time may not be within the token's validity period, the 
1550         /// token may contain data that is contradictory or not valid, or the token 
1551         /// may contain unsupported SAML2 elements.
1552         /// </remarks>
1553         /// <param name="conditions">SAML 2.0 condition to be validated.</param>
1554         /// <param name="enforceAudienceRestriction">True to check for Audience Restriction condition.</param>
1555         protected virtual void ValidateConditions(Saml2Conditions conditions, bool enforceAudienceRestriction)
1556         {
1557             if (conditions != null)
1558             {
1559                 DateTime now = DateTime.UtcNow;
1560
1561                 if (conditions.NotBefore != null
1562                     && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < conditions.NotBefore.Value)
1563                 {
1564                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenNotYetValidException(SR.GetString(SR.ID4147, conditions.NotBefore.Value, now)));
1565                 }
1566
1567                 if (conditions.NotOnOrAfter != null
1568                     && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= conditions.NotOnOrAfter.Value)
1569                 {
1570                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenExpiredException(SR.GetString(SR.ID4148, conditions.NotOnOrAfter.Value, now)));
1571                 }
1572
1573                 if (conditions.OneTimeUse)
1574                 {
1575                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4149)));
1576                 }
1577
1578                 if (conditions.ProxyRestriction != null)
1579                 {
1580                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4150)));
1581                 }
1582             }
1583
1584             if (enforceAudienceRestriction)
1585             {
1586                 if (this.Configuration == null || this.Configuration.AudienceRestriction.AllowedAudienceUris.Count == 0)
1587                 {
1588                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID1032)));
1589                 }
1590
1591                 if (conditions == null || conditions.AudienceRestrictions.Count == 0)
1592                 {
1593                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException(SR.GetString(SR.ID1035)));
1594                 }
1595                 else
1596                 {
1597                     foreach (Saml2AudienceRestriction audienceRestriction in conditions.AudienceRestrictions)
1598                     {
1599                         SamlSecurityTokenRequirement.ValidateAudienceRestriction(this.Configuration.AudienceRestriction.AllowedAudienceUris, audienceRestriction.Audiences);
1600                     }
1601                 }
1602             }
1603         }
1604
1605         /// <summary>
1606         /// Finds the UPN claim value in the provided <see cref="ClaimsIdentity" /> object for the purpose
1607         /// of mapping the identity to a <see cref="WindowsIdentity" /> object.
1608         /// </summary>
1609         /// <param name="claimsIdentity">The claims identity object containing the desired UPN claim.</param>
1610         /// <returns>The UPN claim value found.</returns>
1611         protected virtual string FindUpn(ClaimsIdentity claimsIdentity)
1612         {
1613             return ClaimsHelper.FindUpn(claimsIdentity);
1614         }
1615
1616         /// <summary>
1617         /// Returns the Saml2 AuthenticationContext matching a normalized value.
1618         /// </summary>
1619         /// <param name="normalizedAuthenticationType">Normalized value.</param>
1620         /// <returns>A string that represents the denormalized authentication type used to obtain the token.</returns>
1621         protected virtual string DenormalizeAuthenticationType(string normalizedAuthenticationType)
1622         {
1623             return AuthenticationTypeMaps.Denormalize(normalizedAuthenticationType, AuthenticationTypeMaps.Saml2);
1624         }
1625
1626         /// <summary>
1627         /// Throws if a token is detected as being replayed. If the token is not found, it is added to the 
1628         /// <see cref="TokenReplayCache" />.
1629         /// </summary>
1630         /// <param name="token">The token to detect for replay.</param>
1631         /// <exception cref="ArgumentNullException">The input argument 'token' is null.</exception>
1632         /// <exception cref="InvalidOperationException">Configuration or Configuration.TokenReplayCache property is null.</exception>
1633         /// <exception cref="ArgumentException">The input argument 'token' can not be cast as a 'Saml2SecurityToken'.</exception>
1634         /// <exception cref="SecurityTokenValidationException">The Saml2SecurityToken.Assertion.Id.Value is null or empty.</exception>
1635         /// <exception cref="SecurityTokenReplayDetectedException">The token is found in the <see cref="TokenReplayCache" />.</exception>
1636         /// <remarks>The default behavior is to only check tokens bearer tokens (tokens that do not have keys).</remarks>
1637         protected override void DetectReplayedToken(SecurityToken token)
1638         {
1639             if (token == null)
1640             {
1641                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
1642             }
1643
1644             Saml2SecurityToken samlToken = token as Saml2SecurityToken;
1645             if (null == samlToken)
1646             {
1647                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1064, token.GetType().ToString()));
1648             }
1649
1650             // by default we only check bearer tokens.
1651             if (samlToken.SecurityKeys.Count != 0)
1652             {
1653                 return;
1654             }
1655
1656             if (Configuration == null)
1657             {
1658                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1659             }
1660
1661             if (Configuration.Caches.TokenReplayCache == null)
1662             {
1663                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4278));
1664             }
1665
1666             if (string.IsNullOrEmpty(samlToken.Assertion.Id.Value))
1667             {
1668                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID1065)));
1669             }
1670
1671             StringBuilder stringBuilder = new StringBuilder();
1672             string key;
1673
1674             using (HashAlgorithm hashAlgorithm = CryptoHelper.NewSha256HashAlgorithm())
1675             {
1676                 if (string.IsNullOrEmpty(samlToken.Assertion.Issuer.Value))
1677                 {
1678                     stringBuilder.AppendFormat("{0}{1}", samlToken.Assertion.Id.Value, tokenTypeIdentifiers[0]);
1679                 }
1680                 else
1681                 {
1682                     stringBuilder.AppendFormat("{0}{1}{2}", samlToken.Assertion.Id.Value, samlToken.Assertion.Issuer.Value, tokenTypeIdentifiers[0]);
1683                 }
1684
1685                 key = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString())));
1686             }
1687
1688             if (Configuration.Caches.TokenReplayCache.Contains(key))
1689             {
1690                 string issuer = (samlToken.Assertion.Issuer.Value != null) ? samlToken.Assertion.Issuer.Value : String.Empty;
1691
1692                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenReplayDetectedException(SR.GetString(SR.ID1066, typeof(Saml2SecurityToken).ToString(), samlToken.Assertion.Id.Value, issuer)));
1693             }
1694             else
1695             {
1696                 Configuration.Caches.TokenReplayCache.AddOrUpdate(key, token, DateTimeUtil.Add(this.GetTokenReplayCacheEntryExpirationTime(samlToken), Configuration.MaxClockSkew));
1697             }
1698         }
1699
1700         /// <summary>
1701         /// Returns the time until which the token should be held in the token replay cache.
1702         /// </summary>
1703         /// <param name="token">The token to return an expiration time for.</param>
1704         /// <exception cref="ArgumentNullException">The input argument 'token' is null.</exception>
1705         /// <exception cref="SecurityTokenValidationException">The Saml2SecurityToken's validity period is greater than the expiration period set to TokenReplayCache.</exception>
1706         /// <returns>A DateTime representing the expiration time.</returns>
1707         /// <remarks>By default, this function returns the NotOnOrAfter of the SAML Condition if present.
1708         /// If that value does not exist, it returns the NotOnOrAfter of the first SubjectConfirmationData.
1709         /// This function will never return a value further from now than Configuration.TokenReplayCacheExpirationPeriod.</remarks>
1710         protected virtual DateTime GetTokenReplayCacheEntryExpirationTime(Saml2SecurityToken token)
1711         {
1712             if (token == null)
1713             {
1714                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
1715             }
1716
1717             DateTime? tokenExpiration = null;
1718             Saml2Assertion assertion = token.Assertion;
1719             if (assertion != null)
1720             {
1721                 if (assertion.Conditions != null && assertion.Conditions.NotOnOrAfter.HasValue)
1722                 {
1723                     // The Condition has a NotOnOrAfter set, use that.
1724                     tokenExpiration = assertion.Conditions.NotOnOrAfter.Value;
1725                 }
1726                 else if (assertion.Subject != null && assertion.Subject.SubjectConfirmations != null &&
1727                           assertion.Subject.SubjectConfirmations.Count != 0 &&
1728                           assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData != null &&
1729                           assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData.NotOnOrAfter.HasValue)
1730                 {
1731                     // The condition did not have NotOnOrAfter set, but SCD[0] has a NotOnOrAfter set, use that.
1732                     tokenExpiration = assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData.NotOnOrAfter.Value;
1733                 }
1734             }
1735
1736             // DateTimeUtil handles overflows
1737             DateTime maximumExpirationTime = DateTimeUtil.Add(DateTime.UtcNow, Configuration.TokenReplayCacheExpirationPeriod);
1738
1739             // Use DateTime.MaxValue as expiration value for tokens without expiration
1740             tokenExpiration = tokenExpiration ?? DateTime.MaxValue;
1741
1742             // If the refined token validity period is greater than the TokenReplayCacheExpirationPeriod, throw
1743             if (DateTime.Compare(maximumExpirationTime, tokenExpiration.Value) < 0)
1744             {
1745                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1746                     new SecurityTokenValidationException(SR.GetString(SR.ID1069, tokenExpiration.Value.ToString(), Configuration.TokenReplayCacheExpirationPeriod.ToString())));
1747             }
1748
1749             return tokenExpiration.Value;
1750         }
1751
1752         /// <summary>
1753         /// Returns the normalized value matching a SAML2 AuthenticationContext class reference.
1754         /// </summary>
1755         /// <param name="saml2AuthenticationContextClassReference">A string representing the <see cref="Saml2Constants.AuthenticationContextClasses"/></param>
1756         /// <returns>Normalized value.</returns>
1757         protected virtual string NormalizeAuthenticationContextClassReference(string saml2AuthenticationContextClassReference)
1758         {
1759             return AuthenticationTypeMaps.Normalize(saml2AuthenticationContextClassReference, AuthenticationTypeMaps.Saml2);
1760         }
1761
1762         /// <summary>
1763         /// Creates claims from the Saml2Subject.
1764         /// </summary>
1765         /// <param name="assertionSubject">The Saml2Subject.</param>
1766         /// <param name="subject">The ClaimsIdentity subject.</param>
1767         /// <param name="issuer">The issuer.</param>
1768         protected virtual void ProcessSamlSubject(Saml2Subject assertionSubject, ClaimsIdentity subject, string issuer)
1769         {
1770             if (assertionSubject == null)
1771             {
1772                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertionSubject");
1773             }
1774
1775             if (subject == null)
1776             {
1777                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1778             }
1779
1780             Saml2NameIdentifier nameId = assertionSubject.NameId;
1781
1782             if (nameId != null)
1783             {
1784                 Claim claim = new Claim(ClaimTypes.NameIdentifier, nameId.Value, ClaimValueTypes.String, issuer);
1785
1786                 if (nameId.Format != null)
1787                 {
1788                     claim.Properties[ClaimProperties.SamlNameIdentifierFormat] = nameId.Format.AbsoluteUri;
1789                 }
1790
1791                 if (nameId.NameQualifier != null)
1792                 {
1793                     claim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier] = nameId.NameQualifier;
1794                 }
1795
1796                 if (nameId.SPNameQualifier != null)
1797                 {
1798                     claim.Properties[ClaimProperties.SamlNameIdentifierSPNameQualifier] = nameId.SPNameQualifier;
1799                 }
1800
1801                 if (nameId.SPProvidedId != null)
1802                 {
1803                     claim.Properties[ClaimProperties.SamlNameIdentifierSPProvidedId] = nameId.SPProvidedId;
1804                 }
1805
1806                 subject.AddClaim(claim);
1807             }
1808         }
1809
1810         /// <summary>
1811         /// Creates claims from a Saml2AttributeStatement.
1812         /// </summary>
1813         /// <param name="statement">The Saml2AttributeStatement.</param>
1814         /// <param name="subject">The subject.</param>
1815         /// <param name="issuer">The issuer.</param>
1816         protected virtual void ProcessAttributeStatement(Saml2AttributeStatement statement, ClaimsIdentity subject, string issuer)
1817         {
1818             if (statement == null)
1819             {
1820                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
1821             }
1822
1823             if (subject == null)
1824             {
1825                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1826             }
1827
1828             foreach (Saml2Attribute attribute in statement.Attributes)
1829             {
1830                 if (StringComparer.Ordinal.Equals(attribute.Name, ClaimTypes.Actor))
1831                 {
1832                     if (subject.Actor != null)
1833                     {
1834                         throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4218));
1835                     }
1836
1837                     this.SetDelegateFromAttribute(attribute, subject, issuer);
1838                 }
1839                 else
1840                 {
1841                     foreach (string value in attribute.Values)
1842                     {
1843                         if (value != null)
1844                         {
1845                             string originalIssuer = issuer;
1846                             if (attribute.OriginalIssuer != null)
1847                             {
1848                                 originalIssuer = attribute.OriginalIssuer;
1849                             }
1850
1851                             Claim claim = new Claim(attribute.Name, value, attribute.AttributeValueXsiType, issuer, originalIssuer);
1852
1853                             if (attribute.NameFormat != null)
1854                             {
1855                                 claim.Properties[ClaimProperties.SamlAttributeNameFormat] = attribute.NameFormat.AbsoluteUri;
1856                             }
1857
1858                             if (attribute.FriendlyName != null)
1859                             {
1860                                 claim.Properties[ClaimProperties.SamlAttributeDisplayName] = attribute.FriendlyName;
1861                             }
1862
1863                             subject.AddClaim(claim);
1864                         }
1865                     }
1866                 }
1867             }
1868         }
1869
1870         /// <summary>
1871         /// This method gets called when a special type of Saml2Attribute is detected. The Saml2Attribute passed in 
1872         /// wraps a Saml2Attribute that contains a collection of AttributeValues, each of which will get mapped to a 
1873         /// claim.  All of the claims will be returned in an ClaimsIdentity with the specified issuer.
1874         /// </summary>
1875         /// <param name="attribute">The <see cref="Saml2Attribute"/> to use.</param>
1876         /// <param name="subject">The <see cref="ClaimsIdentity"/> that is the subject of this token.</param>
1877         /// <param name="issuer">The issuer of the claim.</param>
1878         /// <exception cref="InvalidOperationException">Will be thrown if the Saml2Attribute does not contain any 
1879         /// valid Saml2AttributeValues.
1880         /// </exception>
1881         protected virtual void SetDelegateFromAttribute(Saml2Attribute attribute, ClaimsIdentity subject, string issuer)
1882         {
1883             // bail here; nothing to add.
1884             if (subject == null || attribute == null || attribute.Values == null || attribute.Values.Count < 1)
1885             {
1886                 return;
1887             }
1888
1889             Saml2Attribute actingAsAttribute = null;
1890             Collection<Claim> claims = new Collection<Claim>();
1891
1892             foreach (string attributeValue in attribute.Values)
1893             {
1894                 if (attributeValue != null)
1895                 {
1896                     using (XmlDictionaryReader dicReader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(attributeValue), XmlDictionaryReaderQuotas.Max))
1897                     {
1898                         dicReader.MoveToContent();
1899                         dicReader.ReadStartElement(Actor);
1900
1901                         while (dicReader.IsStartElement(Attribute))
1902                         {
1903                             Saml2Attribute innerAttribute = this.ReadAttribute(dicReader);
1904                             if (innerAttribute != null)
1905                             {
1906                                 if (innerAttribute.Name == ClaimTypes.Actor)
1907                                 {
1908                                     // In this case, we have two delegates acting as an identity: we do not allow this.
1909                                     if (actingAsAttribute != null)
1910                                     {
1911                                         throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4218));
1912                                     }
1913
1914                                     actingAsAttribute = innerAttribute;
1915                                 }
1916                                 else
1917                                 {
1918                                     string originalIssuer = innerAttribute.OriginalIssuer;
1919                                     for (int k = 0; k < innerAttribute.Values.Count; ++k)
1920                                     {
1921                                         Claim claim = null;
1922                                         if (string.IsNullOrEmpty(originalIssuer))
1923                                         {
1924                                             claim = new Claim(innerAttribute.Name, innerAttribute.Values[k], innerAttribute.AttributeValueXsiType, issuer);
1925                                         }
1926                                         else
1927                                         {
1928                                             claim = new Claim(innerAttribute.Name, innerAttribute.Values[k], innerAttribute.AttributeValueXsiType, issuer, originalIssuer);
1929                                         }
1930
1931                                         if (innerAttribute.NameFormat != null)
1932                                         {
1933                                             claim.Properties[ClaimProperties.SamlAttributeNameFormat] = innerAttribute.NameFormat.AbsoluteUri;
1934                                         }
1935
1936                                         if (innerAttribute.FriendlyName != null)
1937                                         {
1938                                             claim.Properties[ClaimProperties.SamlAttributeDisplayName] = innerAttribute.FriendlyName;
1939                                         }
1940
1941                                         claims.Add(claim);
1942                                     }
1943                                 }
1944                             }
1945                         }
1946
1947                         dicReader.ReadEndElement(); // Actor
1948                     }
1949                 }
1950             }
1951
1952             subject.Actor = new ClaimsIdentity(claims, AuthenticationTypes.Federation);
1953
1954             this.SetDelegateFromAttribute(actingAsAttribute, subject.Actor, issuer);
1955         }
1956
1957         /// <summary>
1958         /// Creates claims from a Saml2AuthenticationStatement.
1959         /// </summary>
1960         /// <param name="statement">The Saml2AuthenticationStatement.</param>
1961         /// <param name="subject">The subject.</param>
1962         /// <param name="issuer">The issuer.</param>
1963         protected virtual void ProcessAuthenticationStatement(Saml2AuthenticationStatement statement, ClaimsIdentity subject, string issuer)
1964         {
1965             if (subject == null)
1966             {
1967                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1968             }
1969
1970             if (statement.AuthenticationContext.DeclarationReference != null)
1971             {
1972                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4180));
1973             }
1974
1975             if (statement.AuthenticationContext.ClassReference != null)
1976             {
1977                 subject.AddClaim(
1978                         new Claim(
1979                             ClaimTypes.AuthenticationMethod,
1980                             this.NormalizeAuthenticationContextClassReference(statement.AuthenticationContext.ClassReference.AbsoluteUri),
1981                             ClaimValueTypes.String,
1982                             issuer));
1983             }
1984
1985             subject.AddClaim(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(statement.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated), ClaimValueTypes.DateTime, issuer));
1986         }
1987
1988         /// <summary>
1989         /// Creates claims from a Saml2AuthorizationDecisionStatement.
1990         /// </summary>
1991         /// <param name="statement">The Saml2AuthorizationDecisionStatement.</param>
1992         /// <param name="subject">The subject.</param>
1993         /// <param name="issuer">The issuer.</param>
1994         protected virtual void ProcessAuthorizationDecisionStatement(Saml2AuthorizationDecisionStatement statement, ClaimsIdentity subject, string issuer)
1995         {
1996         }
1997
1998         /// <summary>
1999         /// Processes all statements to generate claims.
2000         /// </summary>
2001         /// <param name="statements">A collection of Saml2Statement.</param>
2002         /// <param name="subject">The subject.</param>
2003         /// <param name="issuer">The issuer.</param>
2004         protected virtual void ProcessStatement(Collection<Saml2Statement> statements, ClaimsIdentity subject, string issuer)
2005         {
2006             Collection<Saml2AuthenticationStatement> authnStatements = new Collection<Saml2AuthenticationStatement>();
2007
2008             foreach (Saml2Statement statement in statements)
2009             {
2010                 Saml2AttributeStatement attrStatement = statement as Saml2AttributeStatement;
2011                 if (attrStatement != null)
2012                 {
2013                     this.ProcessAttributeStatement(attrStatement, subject, issuer);
2014                     continue;
2015                 }
2016
2017                 Saml2AuthenticationStatement authnStatement = statement as Saml2AuthenticationStatement;
2018                 if (authnStatement != null)
2019                 {
2020                     authnStatements.Add(authnStatement);
2021                     continue;
2022                 }
2023
2024                 Saml2AuthorizationDecisionStatement authzStatement = statement as Saml2AuthorizationDecisionStatement;
2025                 if (authzStatement != null)
2026                 {
2027                     this.ProcessAuthorizationDecisionStatement(authzStatement, subject, issuer);
2028                     continue;
2029                 }
2030
2031                 // We don't process custom statements. Just fall through.
2032             }
2033
2034             foreach (Saml2AuthenticationStatement authStatement in authnStatements)
2035             {
2036                 if (authStatement != null)
2037                 {
2038                     this.ProcessAuthenticationStatement(authStatement, subject, issuer);
2039                 }
2040             }
2041         }
2042
2043         /// <summary>
2044         /// Creates claims from a Saml2 token.
2045         /// </summary>
2046         /// <param name="samlToken">The Saml2SecurityToken.</param>
2047         /// <returns>An IClaimIdentity.</returns>
2048         protected virtual ClaimsIdentity CreateClaims(Saml2SecurityToken samlToken)
2049         {
2050             if (samlToken == null)
2051             {
2052                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlToken");
2053             }
2054
2055             ClaimsIdentity subject = new ClaimsIdentity(AuthenticationTypes.Federation, SamlSecurityTokenRequirement.NameClaimType, SamlSecurityTokenRequirement.RoleClaimType);
2056
2057             Saml2Assertion assertion = samlToken.Assertion;
2058
2059             if (assertion == null)
2060             {
2061                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("samlToken", SR.GetString(SR.ID1034));
2062             }
2063
2064             if (this.Configuration == null)
2065             {
2066                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
2067             }
2068
2069             if (this.Configuration.IssuerNameRegistry == null)
2070             {
2071                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4277));
2072             }
2073
2074             string issuer = this.Configuration.IssuerNameRegistry.GetIssuerName(samlToken.IssuerToken, assertion.Issuer.Value);
2075
2076             if (string.IsNullOrEmpty(issuer))
2077             {
2078                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4175)));
2079             }
2080
2081             this.ProcessSamlSubject(assertion.Subject, subject, issuer);
2082             this.ProcessStatement(assertion.Statements, subject, issuer);
2083
2084             return subject;
2085         }
2086
2087         /// <summary>
2088         /// Validates the Saml2SubjectConfirmation data.
2089         /// </summary>
2090         /// <param name="confirmationData">The Saml2 subject confirmation data.</param>
2091         protected virtual void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData)
2092         {
2093             if (null == confirmationData)
2094             {
2095                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("confirmationData");
2096             }
2097
2098             if (null != confirmationData.Address)
2099             {
2100                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4153)));
2101             }
2102
2103             if (null != confirmationData.InResponseTo)
2104             {
2105                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4154)));
2106             }
2107
2108             if (null != confirmationData.Recipient)
2109             {
2110                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4157)));
2111             }
2112
2113             DateTime now = DateTime.UtcNow;
2114
2115             if (null != confirmationData.NotBefore
2116                     && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < confirmationData.NotBefore.Value)
2117             {
2118                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4176, confirmationData.NotBefore.Value, now)));
2119             }
2120
2121             if (null != confirmationData.NotOnOrAfter
2122                 && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= confirmationData.NotOnOrAfter.Value)
2123             {
2124                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4177, confirmationData.NotOnOrAfter.Value, now)));
2125             }
2126         }
2127
2128         /// <summary>
2129         /// Resolves the collection of <see cref="SecurityKey"/> referenced in a <see cref="Saml2Assertion"/>.
2130         /// </summary>
2131         /// <param name="assertion"><see cref="Saml2Assertion"/> to process.</param>
2132         /// <param name="resolver"><see cref="SecurityTokenResolver"/> to use in resolving the <see cref="SecurityKey"/>.</param>
2133         /// <returns>A read only collection of <see cref="SecurityKey"/> contained in the assertion.</returns>
2134         protected virtual ReadOnlyCollection<SecurityKey> ResolveSecurityKeys(Saml2Assertion assertion, SecurityTokenResolver resolver)
2135         {
2136             if (null == assertion)
2137             {
2138                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
2139             }
2140
2141             // Must have Subject
2142             Saml2Subject subject = assertion.Subject;
2143             if (null == subject)
2144             {
2145                 // No Subject
2146                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4130)));
2147             }
2148
2149             // Must have one SubjectConfirmation
2150             if (0 == subject.SubjectConfirmations.Count)
2151             {
2152                 // No SubjectConfirmation
2153                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4131)));
2154             }
2155
2156             if (subject.SubjectConfirmations.Count > 1)
2157             {
2158                 // More than one SubjectConfirmation
2159                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4132)));
2160             }
2161
2162             // Extract the keys for the given method
2163             ReadOnlyCollection<SecurityKey> securityKeys;
2164
2165             Saml2SubjectConfirmation subjectConfirmation = subject.SubjectConfirmations[0];
2166
2167             // For bearer, ensure there are no keys, set the collection to empty
2168             // For HolderOfKey, ensure there is at least one key, resolve and create collection
2169             if (Saml2Constants.ConfirmationMethods.Bearer == subjectConfirmation.Method)
2170             {
2171                 if (null != subjectConfirmation.SubjectConfirmationData
2172                     && 0 != subjectConfirmation.SubjectConfirmationData.KeyIdentifiers.Count)
2173                 {
2174                     // Bearer but has keys
2175                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4133)));
2176                 }
2177
2178                 securityKeys = EmptyReadOnlyCollection<SecurityKey>.Instance;
2179             }
2180             else if (Saml2Constants.ConfirmationMethods.HolderOfKey == subjectConfirmation.Method)
2181             {
2182                 if (null == subjectConfirmation.SubjectConfirmationData
2183                     || 0 == subjectConfirmation.SubjectConfirmationData.KeyIdentifiers.Count)
2184                 {
2185                     // Holder-of-key but no keys
2186                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4134)));
2187                 }
2188
2189                 List<SecurityKey> holderKeys = new List<SecurityKey>();
2190                 SecurityKey key;
2191
2192                 foreach (SecurityKeyIdentifier keyIdentifier in subjectConfirmation.SubjectConfirmationData.KeyIdentifiers)
2193                 {
2194                     key = null;
2195
2196                     // Try the resolver first
2197                     foreach (SecurityKeyIdentifierClause clause in keyIdentifier)
2198                     {
2199                         if (null != resolver
2200                             && resolver.TryResolveSecurityKey(clause, out key))
2201                         {
2202                             holderKeys.Add(key);
2203                             break;
2204                         }
2205                     }
2206
2207                     // If that doesn't work, try to create the key (e.g. bare RSA or X509 raw)
2208                     if (null == key)
2209                     {
2210                         if (keyIdentifier.CanCreateKey)
2211                         {
2212                             key = keyIdentifier.CreateKey();
2213                             holderKeys.Add(key);
2214                         }
2215                         else
2216                         {
2217                             holderKeys.Add(new SecurityKeyElement(keyIdentifier, resolver));
2218                         }
2219                     }
2220                 }
2221
2222                 securityKeys = holderKeys.AsReadOnly();
2223             }
2224             else
2225             {
2226                 // SenderVouches, as well as other random things, aren't accepted
2227                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4136, subjectConfirmation.Method)));
2228             }
2229
2230             return securityKeys;
2231         }
2232
2233         /// <summary>
2234         /// Resolves the Signing Key Identifier to a SecurityToken.
2235         /// </summary>
2236         /// <param name="assertion">The Assertion for which the Issuer token is to be resolved.</param>
2237         /// <param name="issuerResolver">The current SecurityTokenResolver associated with this handler.</param>
2238         /// <returns>Instance of SecurityToken</returns>
2239         /// <exception cref="ArgumentNullException">Input parameter 'assertion' is null.</exception>
2240         /// <exception cref="SecurityTokenException">Unable to resolve token.</exception>
2241         protected virtual SecurityToken ResolveIssuerToken(Saml2Assertion assertion, SecurityTokenResolver issuerResolver)
2242         {
2243             if (null == assertion)
2244             {
2245                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
2246             }
2247
2248             SecurityToken token;
2249             if (this.TryResolveIssuerToken(assertion, issuerResolver, out token))
2250             {
2251                 return token;
2252             }
2253             else
2254             {
2255                 string exceptionMessage = SR.GetString(assertion.SigningCredentials == null ? SR.ID4141 : SR.ID4142);
2256                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(exceptionMessage));
2257             }
2258         }
2259
2260         /// <summary>
2261         /// Resolves the Signing Key Identifier to a SecurityToken.
2262         /// </summary>
2263         /// <param name="assertion">The Assertion for which the Issuer token is to be resolved.</param>
2264         /// <param name="issuerResolver">The current SecurityTokenResolver associated with this handler.</param>
2265         /// <param name="token">Resolved token.</param>
2266         /// <returns>True if token is resolved.</returns>
2267         protected virtual bool TryResolveIssuerToken(Saml2Assertion assertion, SecurityTokenResolver issuerResolver, out SecurityToken token)
2268         {
2269             if (null == assertion)
2270             {
2271                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
2272             }
2273
2274             if (assertion.SigningCredentials != null
2275                 && assertion.SigningCredentials.SigningKeyIdentifier != null
2276                 && issuerResolver != null)
2277             {
2278                 SecurityKeyIdentifier keyIdentifier = assertion.SigningCredentials.SigningKeyIdentifier;
2279                 return issuerResolver.TryResolveToken(keyIdentifier, out token);
2280             }
2281             else
2282             {
2283                 token = null;
2284                 return false;
2285             }
2286         }
2287
2288         /// <summary>
2289         /// This handles the construct used in &lt;Subject> and &lt;SubjectConfirmation> for ID:
2290         /// <choice>
2291         ///     <element ref="saml:BaseID" />
2292         ///     <element ref="saml:NameID" />
2293         ///     <element ref="saml:EncryptedID" />
2294         /// </choice>
2295         /// </summary>
2296         /// <param name="reader">A <see cref="XmlReader"/> reader positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
2297         /// <param name="parentElement">The parent element this SubjectID is part of.</param>
2298         /// <returns>A <see cref="Saml2NameIdentifier"/> constructed from the XML.</returns>
2299         protected virtual Saml2NameIdentifier ReadSubjectId(XmlReader reader, string parentElement)
2300         {
2301             // <NameID>, <EncryptedID>, <BaseID>
2302             if (reader.IsStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace))
2303             {
2304                 return this.ReadNameId(reader);
2305             }
2306             else if (reader.IsStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace))
2307             {
2308                 return this.ReadEncryptedId(reader);
2309             }
2310             else if (reader.IsStartElement(Saml2Constants.Elements.BaseID, Saml2Constants.Namespace))
2311             {
2312                 // Since BaseID is an abstract type, we have to switch off the xsi:type declaration
2313                 XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader);
2314
2315                 // No declaration, or declaring that this is just a "BaseID", is invalid since 
2316                 // statement is abstract
2317                 if (null == declaredType
2318                     || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.BaseIDAbstractType, Saml2Constants.Namespace))
2319                 {
2320                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI));
2321                 }
2322
2323                 // If it's NameID we can handle it
2324                 if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.NameIDType, Saml2Constants.Namespace))
2325                 {
2326                     return this.ReadNameIdType(reader);
2327                 }
2328                 else
2329                 {
2330                     // Instruct the user to override to handle custom <BaseID>
2331                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4110, parentElement, declaredType.Name, declaredType.Namespace));
2332                 }
2333             }
2334
2335             return null;
2336         }
2337
2338         /// <summary>
2339         /// Reads the &lt;saml:Action> element.
2340         /// </summary>
2341         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Action"/> element.</param>
2342         /// <returns>A <see cref="Saml2Action"/> instance.</returns>
2343         protected virtual Saml2Action ReadAction(XmlReader reader)
2344         {
2345             if (null == reader)
2346             {
2347                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2348             }
2349
2350             // throw if wrong element
2351             if (!reader.IsStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace))
2352             {
2353                 reader.ReadStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace);
2354             }
2355
2356             // disallow empty
2357             if (reader.IsEmptyElement)
2358             {
2359                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.Action, Saml2Constants.Namespace));
2360             }
2361
2362             try
2363             {
2364                 // Need the content to instantiate, so use locals
2365                 Uri actionNamespace;
2366
2367                 // @attributes
2368                 string attributeValue;
2369
2370                 // @xsi:type
2371                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ActionType, Saml2Constants.Namespace);
2372
2373                 // @Namespace - required
2374                 attributeValue = reader.GetAttribute(Saml2Constants.Attributes.Namespace);
2375                 if (string.IsNullOrEmpty(attributeValue))
2376                 {
2377                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
2378                 }
2379
2380                 if (!UriUtil.CanCreateValidUri(attributeValue, UriKind.Absolute))
2381                 {
2382                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
2383                 }
2384
2385                 actionNamespace = new Uri(attributeValue);
2386
2387                 // Content is string
2388                 return new Saml2Action(reader.ReadElementString(), actionNamespace);
2389             }
2390             catch (Exception e)
2391             {
2392                 if (System.Runtime.Fx.IsFatal(e))
2393                     throw;
2394                 
2395                 Exception wrapped = TryWrapReadException(reader, e);
2396                 if (null == wrapped)
2397                 {
2398                     throw;
2399                 }
2400                 else
2401                 {
2402                     throw wrapped;
2403                 }
2404             }
2405         }
2406
2407         /// <summary>
2408         /// Writes the &lt;saml:Action> element.
2409         /// </summary>
2410         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Action"/>.</param>
2411         /// <param name="data">The <see cref="Saml2Action"/> to serialize.</param>
2412         protected virtual void WriteAction(XmlWriter writer, Saml2Action data)
2413         {
2414             if (null == writer)
2415             {
2416                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2417             }
2418
2419             if (null == data)
2420             {
2421                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
2422             }
2423
2424             if (null == data.Namespace)
2425             {
2426                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data.Namespace");
2427             }
2428
2429             if (string.IsNullOrEmpty(data.Namespace.ToString()))
2430             {
2431                 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("data.Namespace");
2432             }
2433
2434             // <Action>
2435             writer.WriteStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace);
2436
2437             // @Namespace - required
2438             writer.WriteAttributeString(Saml2Constants.Attributes.Namespace, data.Namespace.AbsoluteUri);
2439
2440             // String content
2441             writer.WriteString(data.Value);
2442
2443             // </Action>
2444             writer.WriteEndElement();
2445         }
2446
2447         /// <summary>
2448         /// Reads the &lt;saml:Advice> element.
2449         /// </summary>
2450         /// <remarks>
2451         /// <para>
2452         /// The Advice element has an extensibility point to allow XML elements
2453         /// from non-SAML2 namespaces to be included. By default, because the 
2454         /// Advice may be ignored without affecting the semantics of the 
2455         /// assertion, any such elements are ignored. To handle the processing
2456         /// of those elements, override this method.
2457         /// </para>
2458         /// </remarks>
2459         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Advice"/> element.</param>
2460         /// <returns>A <see cref="Saml2Advice"/> instance.</returns>
2461         protected virtual Saml2Advice ReadAdvice(XmlReader reader)
2462         {
2463             if (null == reader)
2464             {
2465                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2466             }
2467
2468             // throw if wrong element
2469             if (!reader.IsStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace))
2470             {
2471                 reader.ReadStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace);
2472             }
2473
2474             try
2475             {
2476                 Saml2Advice advice = new Saml2Advice();
2477                 bool isEmpty = reader.IsEmptyElement;
2478
2479                 // @attributes
2480
2481                 // @xsi:type
2482                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AdviceType, Saml2Constants.Namespace);
2483
2484                 reader.Read();
2485                 if (!isEmpty)
2486                 {
2487                     // <AssertionIDRef|AssertionURIRef|Assertion|EncryptedAssertion|other:any> 0-OO
2488                     while (reader.IsStartElement())
2489                     {
2490                         // <AssertionIDRef>, <AssertionURIRef>, <Assertion>, <EncryptedAssertion>
2491                         if (reader.IsStartElement(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace))
2492                         {
2493                             advice.AssertionIdReferences.Add(ReadSimpleNCNameElement(reader));
2494                         }
2495                         else if (reader.IsStartElement(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace))
2496                         {
2497                             advice.AssertionUriReferences.Add(ReadSimpleUriElement(reader));
2498                         }
2499                         else if (reader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace))
2500                         {
2501                             advice.Assertions.Add(this.ReadAssertion(reader));
2502                         }
2503                         else if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace))
2504                         {
2505                             advice.Assertions.Add(this.ReadAssertion(reader));
2506                         }
2507                         else
2508                         {
2509                             TraceUtility.TraceString(TraceEventType.Warning, SR.GetString(SR.ID8006, reader.LocalName, reader.NamespaceURI));
2510                             reader.Skip();
2511                         }
2512                     }
2513
2514                     reader.ReadEndElement();
2515                 }
2516
2517                 return advice;
2518             }
2519             catch (Exception e)
2520             {
2521                 if (System.Runtime.Fx.IsFatal(e))
2522                     throw;
2523                 
2524                 Exception wrapped = TryWrapReadException(reader, e);
2525                 if (null == wrapped)
2526                 {
2527                     throw;
2528                 }
2529                 else
2530                 {
2531                     throw wrapped;
2532                 }
2533             }
2534         }
2535
2536         /// <summary>
2537         /// Writes the &lt;saml:Advice> element.
2538         /// </summary>
2539         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Advice"/>.</param>
2540         /// <param name="data">The <see cref="Saml2Advice"/> to serialize.</param>
2541         protected virtual void WriteAdvice(XmlWriter writer, Saml2Advice data)
2542         {
2543             if (null == writer)
2544             {
2545                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2546             }
2547
2548             if (null == data)
2549             {
2550                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
2551             }
2552
2553             // <Advice>
2554             writer.WriteStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace);
2555
2556             // <AssertionIDRef> 0-OO
2557             foreach (Saml2Id id in data.AssertionIdReferences)
2558             {
2559                 writer.WriteElementString(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace, id.Value);
2560             }
2561
2562             // <AssertionURIRef> 0-OO
2563             foreach (Uri uri in data.AssertionUriReferences)
2564             {
2565                 writer.WriteElementString(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace, uri.AbsoluteUri);
2566             }
2567
2568             // <Assertion> 0-OO
2569             foreach (Saml2Assertion assertion in data.Assertions)
2570             {
2571                 this.WriteAssertion(writer, assertion);
2572             }
2573
2574             // </Advice>
2575             writer.WriteEndElement();
2576         }
2577
2578         /// <summary>
2579         /// Reads the &lt;saml:Assertion> element.
2580         /// </summary>
2581         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Assertion"/> element.</param>
2582         /// <returns>A <see cref="Saml2Assertion"/> instance.</returns>
2583         protected virtual Saml2Assertion ReadAssertion(XmlReader reader)
2584         {
2585             if (null == reader)
2586             {
2587                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2588             }
2589
2590             if (this.Configuration == null)
2591             {
2592                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
2593             }
2594
2595             if (this.Configuration.IssuerTokenResolver == null)
2596             {
2597                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
2598             }
2599
2600             if (this.Configuration.ServiceTokenResolver == null)
2601             {
2602                 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
2603             }
2604
2605             XmlDictionaryReader plaintextReader = XmlDictionaryReader.CreateDictionaryReader(reader);
2606
2607             Saml2Assertion assertion = new Saml2Assertion(new Saml2NameIdentifier("__TemporaryIssuer__"));
2608
2609             // If it's an EncryptedAssertion, we need to retrieve the plaintext 
2610             // and repoint our reader
2611             if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace))
2612             {
2613                 EncryptingCredentials encryptingCredentials = null;
2614                 plaintextReader = CreatePlaintextReaderFromEncryptedData(
2615                                     plaintextReader,
2616                                     Configuration.ServiceTokenResolver,
2617                                     this.KeyInfoSerializer,
2618                                     assertion.ExternalEncryptedKeys,
2619                                     out encryptingCredentials);
2620
2621                 assertion.EncryptingCredentials = encryptingCredentials;
2622             }
2623
2624             // Throw if wrong element
2625             if (!plaintextReader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace))
2626             {
2627                 plaintextReader.ReadStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace);
2628             }
2629
2630             // disallow empty
2631             if (plaintextReader.IsEmptyElement)
2632             {
2633 #pragma warning suppress 56504 // bogus - thinks plaintextReader.LocalName, plaintextReader.NamespaceURI need validation
2634                 throw DiagnosticUtility.ThrowHelperXml(plaintextReader, SR.GetString(SR.ID3061, plaintextReader.LocalName, plaintextReader.NamespaceURI));
2635             }
2636
2637             // Construct a wrapped serializer so that the EnvelopedSignatureReader's 
2638             // attempt to read the <ds:KeyInfo> will hit our ReadKeyInfo virtual.
2639             WrappedSerializer wrappedSerializer = new WrappedSerializer(this, assertion);
2640
2641             // SAML supports enveloped signature, so we need to wrap our reader.
2642             // We do not dispose this reader, since as a delegating reader it would
2643             // dispose the inner reader, which we don't properly own.
2644             EnvelopedSignatureReader realReader = new EnvelopedSignatureReader(plaintextReader, wrappedSerializer, this.Configuration.IssuerTokenResolver, false, false, false);
2645             try
2646             {
2647                 // Process @attributes
2648                 string value;
2649
2650                 // @xsi:type
2651                 XmlUtil.ValidateXsiType(realReader, Saml2Constants.Types.AssertionType, Saml2Constants.Namespace);
2652
2653                 // @Version - required - must be "2.0"
2654                 string version = realReader.GetAttribute(Saml2Constants.Attributes.Version);
2655                 if (string.IsNullOrEmpty(version))
2656                 {
2657                     throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Version, Saml2Constants.Elements.Assertion));
2658                 }
2659
2660                 if (!StringComparer.Ordinal.Equals(assertion.Version, version))
2661                 {
2662                     throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID4100, version));
2663                 }
2664
2665                 // @ID - required
2666                 value = realReader.GetAttribute(Saml2Constants.Attributes.ID);
2667                 if (string.IsNullOrEmpty(value))
2668                 {
2669                     throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.ID, Saml2Constants.Elements.Assertion));
2670                 }
2671
2672                 assertion.Id = new Saml2Id(value);
2673
2674                 // @IssueInstant - required
2675                 value = realReader.GetAttribute(Saml2Constants.Attributes.IssueInstant);
2676                 if (string.IsNullOrEmpty(value))
2677                 {
2678                     throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.IssueInstant, Saml2Constants.Elements.Assertion));
2679                 }
2680
2681                 assertion.IssueInstant = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
2682
2683                 // Process <elements>
2684                 realReader.Read();
2685
2686                 // <Issuer> 1
2687                 assertion.Issuer = this.ReadIssuer(realReader);
2688
2689                 // <ds:Signature> 0-1
2690                 realReader.TryReadSignature();
2691
2692                 // <Subject> 0-1
2693                 if (realReader.IsStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace))
2694                 {
2695                     assertion.Subject = this.ReadSubject(realReader);
2696                 }
2697
2698                 // <Conditions> 0-1
2699                 if (realReader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace))
2700                 {
2701                     assertion.Conditions = this.ReadConditions(realReader);
2702                 }
2703
2704                 // <Advice> 0-1
2705                 if (realReader.IsStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace))
2706                 {
2707                     assertion.Advice = this.ReadAdvice(realReader);
2708                 }
2709
2710                 // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO
2711                 while (realReader.IsStartElement())
2712                 {
2713                     Saml2Statement statement;
2714
2715                     if (realReader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
2716                     {
2717                         statement = this.ReadStatement(realReader);
2718                     }
2719                     else if (realReader.IsStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace))
2720                     {
2721                         statement = this.ReadAttributeStatement(realReader);
2722                     }
2723                     else if (realReader.IsStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace))
2724                     {
2725                         statement = this.ReadAuthenticationStatement(realReader);
2726                     }
2727                     else if (realReader.IsStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace))
2728                     {
2729                         statement = this.ReadAuthorizationDecisionStatement(realReader);
2730                     }
2731                     else
2732                     {
2733                         break;
2734                     }
2735
2736                     assertion.Statements.Add(statement);
2737                 }
2738
2739                 realReader.ReadEndElement();
2740
2741                 if (null == assertion.Subject)
2742                 {
2743                     // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585]
2744                     if (0 == assertion.Statements.Count)
2745                     {
2746                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4106));
2747                     }
2748
2749                     // Furthermore, the built-in statement types all require the presence of a subject.
2750                     // [Saml2Core, lines 1050, 1168, 1280]
2751                     foreach (Saml2Statement statement in assertion.Statements)
2752                     {
2753                         if (statement is Saml2AuthenticationStatement
2754                             || statement is Saml2AttributeStatement
2755                             || statement is Saml2AuthorizationDecisionStatement)
2756                         {
2757                             throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4119));
2758                         }
2759                     }
2760                 }
2761
2762                 // Reading the end element will complete the signature; 
2763                 // capture the signing creds
2764                 assertion.SigningCredentials = realReader.SigningCredentials;
2765
2766                 // Save the captured on-the-wire data, which can then be used
2767                 // to re-emit this assertion, preserving the same signature.
2768                 assertion.CaptureSourceData(realReader);
2769
2770                 return assertion;
2771             }
2772             catch (Exception e)
2773             {
2774                 if (System.Runtime.Fx.IsFatal(e))
2775                     throw;
2776                 
2777                 Exception wrapped = TryWrapReadException(realReader, e);
2778                 if (null == wrapped)
2779                 {
2780                     throw;
2781                 }
2782                 else
2783                 {
2784                     throw wrapped;
2785                 }
2786             }
2787         }
2788
2789         /// <summary>
2790         /// Serializes the provided SamlAssertion to the XmlWriter.
2791         /// </summary>
2792         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Assertion"/>.</param>
2793         /// <param name="data">The <see cref="Saml2Assertion"/> to serialize.</param>
2794         /// <exception cref="ArgumentNullException">The <paramref name="writer"/> or <paramref name="data"/> parameters are null.</exception>
2795         /// <exception cref="InvalidOperationException"> The <paramref name="data"/>  has both <see cref="EncryptingCredentials"/> and <see cref="ReceivedEncryptingCredentials"/> properties null.</exception>
2796         /// <exception cref="InvalidOperationException">The <paramref name="data"/> must have a <see cref="Saml2Subject"/> if no <see cref="Saml2Statement"/> are present.</exception>
2797         /// <exception cref="InvalidOperationException">The SAML2 authentication, attribute, and authorization decision <see cref="Saml2Statement"/> require a <see cref="Saml2Subject"/>.</exception>
2798         /// <exception cref="CryptographicException">Token encrypting credentials must have a Symmetric Key specified.</exception>
2799         protected virtual void WriteAssertion(XmlWriter writer, Saml2Assertion data)
2800         {
2801             if (null == writer)
2802             {
2803                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2804             }
2805
2806             if (null == data)
2807             {
2808                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
2809             }
2810
2811             XmlWriter originalWriter = writer;
2812             MemoryStream plaintextStream = null;
2813             XmlDictionaryWriter plaintextWriter = null;
2814
2815             // If an EncryptingCredentials is present then check if this is not of type ReceivedEncryptinCredentials.
2816             // ReceivedEncryptingCredentials mean that it was credentials that were hydrated from a token received
2817             // on the wire. We should not directly use this while re-serializing a token.
2818             if ((null != data.EncryptingCredentials) && !(data.EncryptingCredentials is ReceivedEncryptingCredentials))
2819             {
2820                 plaintextStream = new MemoryStream();
2821                 writer = plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false);
2822             }
2823             else if (data.ExternalEncryptedKeys == null || data.ExternalEncryptedKeys.Count > 0)
2824             {
2825                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4173)));
2826             }
2827
2828             // If we've saved off the token stream, re-emit it.
2829             if (data.CanWriteSourceData)
2830             {
2831                 data.WriteSourceData(writer);
2832             }
2833             else
2834             {
2835                 // Wrap the writer if necessary for a signature
2836                 // We do not dispose this writer, since as a delegating writer it would
2837                 // dispose the inner writer, which we don't properly own.
2838                 EnvelopedSignatureWriter signatureWriter = null;
2839                 if (null != data.SigningCredentials)
2840                 {
2841 #pragma warning suppress 56506
2842                     writer = signatureWriter = new EnvelopedSignatureWriter(writer, data.SigningCredentials, data.Id.Value, new WrappedSerializer(this, data));
2843                 }
2844
2845                 if (null == data.Subject)
2846                 {
2847                     // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585]
2848                     if (data.Statements == null || 0 == data.Statements.Count)
2849                     {
2850                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4106)));
2851                     }
2852
2853                     // Furthermore, the built-in statement types all require the presence of a subject.
2854                     // [Saml2Core, lines 1050, 1168, 1280]
2855                     foreach (Saml2Statement statement in data.Statements)
2856                     {
2857                         if (statement is Saml2AuthenticationStatement
2858                             || statement is Saml2AttributeStatement
2859                             || statement is Saml2AuthorizationDecisionStatement)
2860                         {
2861                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2862                                 new InvalidOperationException(SR.GetString(SR.ID4119)));
2863                         }
2864                     }
2865                 }
2866
2867                 // <Assertion>
2868                 writer.WriteStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace);
2869
2870                 // @ID - required
2871                 writer.WriteAttributeString(Saml2Constants.Attributes.ID, data.Id.Value);
2872
2873                 // @IssueInstant - required
2874                 writer.WriteAttributeString(Saml2Constants.Attributes.IssueInstant, XmlConvert.ToString(data.IssueInstant.ToUniversalTime(), DateTimeFormats.Generated));
2875
2876                 // @Version - required
2877                 writer.WriteAttributeString(Saml2Constants.Attributes.Version, data.Version);
2878
2879                 // <Issuer> 1
2880                 this.WriteIssuer(writer, data.Issuer);
2881
2882                 // <ds:Signature> 0-1
2883                 if (null != signatureWriter)
2884                 {
2885                     signatureWriter.WriteSignature();
2886                 }
2887
2888                 // <Subject> 0-1
2889                 if (null != data.Subject)
2890                 {
2891                     this.WriteSubject(writer, data.Subject);
2892                 }
2893
2894                 // <Conditions> 0-1
2895                 if (null != data.Conditions)
2896                 {
2897                     this.WriteConditions(writer, data.Conditions);
2898                 }
2899
2900                 // <Advice> 0-1
2901                 if (null != data.Advice)
2902                 {
2903                     this.WriteAdvice(writer, data.Advice);
2904                 }
2905
2906                 // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO
2907                 foreach (Saml2Statement statement in data.Statements)
2908                 {
2909                     this.WriteStatement(writer, statement);
2910                 }
2911
2912                 writer.WriteEndElement();
2913             }
2914
2915             // Finish off the encryption
2916             if (null != plaintextWriter)
2917             {
2918                 ((IDisposable)plaintextWriter).Dispose();
2919                 plaintextWriter = null;
2920
2921                 EncryptedDataElement encryptedData = new EncryptedDataElement();
2922                 encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
2923                 encryptedData.Algorithm = data.EncryptingCredentials.Algorithm;
2924                 encryptedData.KeyIdentifier = data.EncryptingCredentials.SecurityKeyIdentifier;
2925
2926                 // Get the encryption key, which must be symmetric
2927                 SymmetricSecurityKey encryptingKey = data.EncryptingCredentials.SecurityKey as SymmetricSecurityKey;
2928                 if (encryptingKey == null)
2929                 {
2930                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3064)));
2931                 }
2932
2933                 // Do the actual encryption
2934                 SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(data.EncryptingCredentials.Algorithm);
2935                 encryptedData.Encrypt(symmetricAlgorithm, plaintextStream.GetBuffer(), 0, (int)plaintextStream.Length);
2936                 ((IDisposable)plaintextStream).Dispose();
2937
2938                 originalWriter.WriteStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace);
2939                 encryptedData.WriteXml(originalWriter, this.KeyInfoSerializer);
2940                 foreach (EncryptedKeyIdentifierClause clause in data.ExternalEncryptedKeys)
2941                 {
2942                     this.KeyInfoSerializer.WriteKeyIdentifierClause(originalWriter, clause);
2943                 }
2944
2945                 originalWriter.WriteEndElement();
2946             }
2947         }
2948
2949         /// <summary>
2950         /// Reads the &lt;saml:Attribute> element.
2951         /// </summary>
2952         /// <remarks>
2953         /// The default implementation requires that the content of the 
2954         /// Attribute element be a simple string. To handle complex content
2955         /// or content of declared simple types other than xs:string, override
2956         /// this method.
2957         /// </remarks>
2958         /// <param name="reader">An <see cref="XmlReader"/> positioned at a <see cref="Saml2Attribute"/> element.</param>
2959         /// <returns>A <see cref="Saml2Attribute"/> instance.</returns>
2960         protected virtual Saml2Attribute ReadAttribute(XmlReader reader)
2961         {
2962             if (null == reader)
2963             {
2964                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2965             }
2966
2967             // throw if wrong element
2968             if (!reader.IsStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace))
2969             {
2970                 reader.ReadStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
2971             }
2972
2973             try
2974             {
2975                 Saml2Attribute attribute;
2976                 bool isEmpty = reader.IsEmptyElement;
2977
2978                 // @attributes
2979                 string value;
2980
2981                 // @xsi:type 
2982                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AttributeType, Saml2Constants.Namespace);
2983
2984                 // @Name - required
2985                 value = reader.GetAttribute(Saml2Constants.Attributes.Name);
2986                 if (string.IsNullOrEmpty(value))
2987                 {
2988                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Name, Saml2Constants.Elements.Attribute));
2989                 }
2990
2991                 attribute = new Saml2Attribute(value);
2992
2993                 // @NameFormat - optional
2994                 value = reader.GetAttribute(Saml2Constants.Attributes.NameFormat);
2995                 if (!string.IsNullOrEmpty(value))
2996                 {
2997                     if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
2998                     {
2999                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
3000                     }
3001
3002                     attribute.NameFormat = new Uri(value);
3003                 }
3004
3005                 // @FriendlyName - optional
3006                 attribute.FriendlyName = reader.GetAttribute(Saml2Constants.Attributes.FriendlyName);
3007
3008                 // @OriginalIssuer - optional.
3009                 // We are lax on read here, and will accept the following namespaces for original issuer, in order:
3010                 // http://schemas.xmlsoap.org/ws/2009/09/identity/claims
3011                 // http://schemas.microsoft.com/ws/2008/06/identity
3012                 string originalIssuer = reader.GetAttribute(Saml2Constants.Attributes.OriginalIssuer, ClaimType2009Namespace);
3013
3014                 if (originalIssuer == null)
3015                 {
3016                     originalIssuer = reader.GetAttribute(Saml2Constants.Attributes.OriginalIssuer, ProductConstants.NamespaceUri);
3017                 }
3018
3019                 if (originalIssuer == String.Empty)
3020                 {
3021                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4252)));
3022                 }
3023
3024                 attribute.OriginalIssuer = originalIssuer;
3025
3026                 // content
3027                 reader.Read();
3028                 if (!isEmpty)
3029                 {
3030                     while (reader.IsStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace))
3031                     {
3032                         bool isEmptyValue = reader.IsEmptyElement;
3033                         bool isNil = XmlUtil.IsNil(reader);
3034
3035                         // FIP 9570 - ENTERPRISE SCENARIO: Saml11SecurityTokenHandler.ReadAttribute is not checking the AttributeValue XSI type correctly.
3036                         // Lax on receive. If we dont find the AttributeValueXsiType in the format we are looking for in the xml, we default to string.
3037                         // 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".
3038                         // ":some-non-empty-string" and "some-non-empty-string:" are edge-cases where defaulting to string is reasonable.
3039                         // For attributeValueXsiTypeSuffix, we want the portion after the local prefix in "some-non-empty-local-prefix:some-non-empty-string"
3040                         // "some-non-empty-local-prefix:some-non-empty-string" case
3041                         string attributeValueXsiTypePrefix = null;
3042                         string attributeValueXsiTypeSuffix = null;
3043                         string attributeValueXsiTypeSuffixWithLocalPrefix = reader.GetAttribute("type", XmlSchema.InstanceNamespace);
3044                         if (!string.IsNullOrEmpty(attributeValueXsiTypeSuffixWithLocalPrefix))
3045                         {
3046                             // "some-non-empty-string" case
3047                             if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) == -1)
3048                             {
3049                                 attributeValueXsiTypePrefix = reader.LookupNamespace(String.Empty);
3050                                 attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix;
3051                             }
3052                             else if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) > 0 &&
3053                                       attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) < attributeValueXsiTypeSuffixWithLocalPrefix.Length - 1)
3054                             {
3055                                 string localPrefix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(0, attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal));
3056                                 attributeValueXsiTypePrefix = reader.LookupNamespace(localPrefix);
3057                                 attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) + 1);
3058                             }
3059                         }
3060
3061                         if (attributeValueXsiTypePrefix != null && attributeValueXsiTypeSuffix != null)
3062                         {
3063                             attribute.AttributeValueXsiType = String.Concat(attributeValueXsiTypePrefix, "#", attributeValueXsiTypeSuffix);
3064                         }
3065
3066                         if (isNil)
3067                         {
3068                             reader.Read();
3069                             if (!isEmptyValue)
3070                             {
3071                                 reader.ReadEndElement();
3072                             }
3073
3074                             attribute.Values.Add(null);
3075                         }
3076                         else if (isEmptyValue)
3077                         {
3078                             reader.Read();
3079                             attribute.Values.Add(string.Empty);
3080                         }
3081                         else
3082                         {
3083                             attribute.Values.Add(this.ReadAttributeValue(reader, attribute));
3084                         }
3085                     }
3086
3087                     reader.ReadEndElement();
3088                 }
3089
3090                 return attribute;
3091             }
3092             catch (Exception e)
3093             {
3094                 if (System.Runtime.Fx.IsFatal(e))
3095                     throw;
3096                 
3097                 Exception wrapped = TryWrapReadException(reader, e);
3098                 if (null == wrapped)
3099                 {
3100                     throw;
3101                 }
3102                 else
3103                 {
3104                     throw wrapped;
3105                 }
3106             }
3107         }
3108
3109         /// <summary>
3110         /// Reads an attribute value.
3111         /// </summary>
3112         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Attribute"/>.</param>
3113         /// <param name="attribute">The <see cref="Saml2Attribute"/>.</param>
3114         /// <returns>The attribute value as a string.</returns>
3115         /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
3116         protected virtual string ReadAttributeValue(XmlReader reader, Saml2Attribute attribute)
3117         {
3118             // This code was designed realizing that the writter of the xml controls how our
3119             // reader will report the NodeType. A completely differnet system (IBM, etc) could write the values. 
3120             // Considering NodeType is important, because we need to read the entire value, end element and not loose anything significant.
3121             // 
3122             // Couple of cases to help understand the design choices.
3123             //
3124             // 1. 
3125             // "<MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>"
3126             // Could result in the our reader reporting the NodeType as Text OR Element, depending if '<' was entitized to '&lt;'
3127             //
3128             // 2. 
3129             // " <MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>"
3130             // Could result in the our reader reporting the NodeType as Text OR Whitespace.  Post Whitespace processing, the NodeType could be 
3131             // reported as Text or Element, depending if '<' was entitized to '&lt;'
3132             //
3133             // 3. 
3134             // "/r/n/t   "
3135             // Could result in the our reader reporting the NodeType as whitespace.
3136             //
3137             // Since an AttributeValue with ONLY Whitespace and a complex Element proceeded by whitespace are reported as the same NodeType (2. and 3.)
3138             // 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
3139             // If we just skipped the Whitespace, then an AttributeValue that started with Whitespace would loose that part and claims generated from the AttributeValue
3140             // would be missing that part.
3141             // 
3142
3143             if (reader == null)
3144             {
3145                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3146             }
3147
3148             string result = String.Empty;
3149             string whiteSpace = String.Empty;
3150
3151             reader.ReadStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace);
3152
3153             while (reader.NodeType == XmlNodeType.Whitespace)
3154             {
3155                 whiteSpace += reader.Value;
3156                 reader.Read();
3157             }
3158
3159             reader.MoveToContent();
3160             if (reader.NodeType == XmlNodeType.Element)
3161             {
3162                 while (reader.NodeType == XmlNodeType.Element)
3163                 {
3164                     result += reader.ReadOuterXml();
3165                     reader.MoveToContent();
3166                 }
3167             }
3168             else
3169             {
3170                 result = whiteSpace;
3171                 result += reader.ReadContentAsString();
3172             }
3173
3174             reader.ReadEndElement();
3175             return result;
3176         }
3177
3178         /// <summary>
3179         /// Writes the &lt;saml:Attribute> element.
3180         /// </summary>
3181         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Attribute"/>.</param>
3182         /// <param name="data">The <see cref="Saml2Attribute"/> to serialize.</param>
3183         protected virtual void WriteAttribute(XmlWriter writer, Saml2Attribute data)
3184         {
3185             if (null == writer)
3186             {
3187                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3188             }
3189
3190             if (null == data)
3191             {
3192                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3193             }
3194
3195             // <Attribute>
3196             writer.WriteStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
3197
3198             // @Name - required
3199             writer.WriteAttributeString(Saml2Constants.Attributes.Name, data.Name);
3200
3201             // @NameFormat - optional
3202             if (null != data.NameFormat)
3203             {
3204                 writer.WriteAttributeString(Saml2Constants.Attributes.NameFormat, data.NameFormat.AbsoluteUri);
3205             }
3206
3207             // @FriendlyName - optional
3208             if (null != data.FriendlyName)
3209             {
3210                 writer.WriteAttributeString(Saml2Constants.Attributes.FriendlyName, data.FriendlyName);
3211             }
3212
3213             // @OriginalIssuer - optional
3214             if (null != data.OriginalIssuer)
3215             {
3216                 writer.WriteAttributeString(Saml2Constants.Attributes.OriginalIssuer, ClaimType2009Namespace, data.OriginalIssuer);
3217             }
3218
3219             string xsiTypePrefix = null;
3220             string xsiTypeSuffix = null;
3221             if (!StringComparer.Ordinal.Equals(data.AttributeValueXsiType, ClaimValueTypes.String))
3222             {
3223                 // ClaimValueTypes are URIs of the form prefix#suffix, while xsi:type should be a QName.
3224                 // Hence, the tokens-to-claims spec requires that ClaimValueTypes be serialized as xmlns:tn="prefix" xsi:type="tn:suffix"
3225                 int indexOfHash = data.AttributeValueXsiType.IndexOf('#');
3226                 xsiTypePrefix = data.AttributeValueXsiType.Substring(0, indexOfHash);
3227                 xsiTypeSuffix = data.AttributeValueXsiType.Substring(indexOfHash + 1);
3228             }
3229
3230             // <AttributeValue> 0-OO (nillable)
3231             foreach (string value in data.Values)
3232             {
3233                 writer.WriteStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace);
3234
3235                 if (null == value)
3236                 {
3237                     writer.WriteAttributeString("nil", XmlSchema.InstanceNamespace, XmlConvert.ToString(true));
3238                 }
3239                 else if (value.Length > 0)
3240                 {
3241                     if ((xsiTypePrefix != null) && (xsiTypeSuffix != null))
3242                     {
3243                         writer.WriteAttributeString("xmlns", ProductConstants.ClaimValueTypeSerializationPrefix, null, xsiTypePrefix);
3244                         writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, String.Concat(ProductConstants.ClaimValueTypeSerializationPrefixWithColon, xsiTypeSuffix));
3245                     }
3246
3247                     this.WriteAttributeValue(writer, value, data);
3248                 }
3249
3250                 writer.WriteEndElement();
3251             }
3252
3253             // </Attribute>
3254             writer.WriteEndElement();
3255         }
3256
3257         /// <summary>
3258         /// Writes the saml:Attribute value.
3259         /// </summary>
3260         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Attribute"/>.</param>
3261         /// <param name="value">The value of the attribute being serialized.</param>
3262         /// <param name="attribute">The <see cref="Saml2Attribute"/> to serialize.</param>
3263         /// <remarks>By default the method writes the value as a string.</remarks>
3264         /// <exception cref="ArgumentNullException">The input parameter 'writer' is null.</exception>
3265         protected virtual void WriteAttributeValue(XmlWriter writer, string value, Saml2Attribute attribute)
3266         {
3267             if (writer == null)
3268             {
3269                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3270             }
3271
3272             writer.WriteString(value);
3273         }
3274
3275         /// <summary>
3276         /// Reads the &lt;saml:AttributeStatement> element, or a 
3277         /// &lt;saml:Statement element that specifies an xsi:type of
3278         /// saml:AttributeStatementType.
3279         /// </summary>
3280         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AttributeStatement"/> element.</param>
3281         /// <returns>A <see cref="Saml2AttributeStatement"/> instance.</returns>
3282         protected virtual Saml2AttributeStatement ReadAttributeStatement(XmlReader reader)
3283         {
3284             if (null == reader)
3285             {
3286                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3287             }
3288
3289             // throw if wrong element
3290             bool isStatementElement = false;
3291             if (reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
3292             {
3293                 isStatementElement = true;
3294             }
3295             else if (!reader.IsStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace))
3296             {
3297                 reader.ReadStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace);
3298             }
3299
3300             try
3301             {
3302                 // defer disallowing empty element until checking xsi:type
3303                 bool isEmpty = reader.IsEmptyElement;
3304
3305                 // @attributes
3306
3307                 // @xsi:type
3308                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AttributeStatementType, Saml2Constants.Namespace, isStatementElement);
3309
3310                 // disallow empty element, since xsi:type is ok
3311                 if (isEmpty)
3312                 {
3313                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace));
3314                 }
3315
3316                 // Content
3317                 Saml2AttributeStatement statement = new Saml2AttributeStatement();
3318                 reader.Read();
3319
3320                 // <Attribute|EncryptedAttribute> 1-OO
3321                 while (reader.IsStartElement())
3322                 {
3323                     if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAttribute, Saml2Constants.Namespace))
3324                     {
3325                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4158));
3326                     }
3327                     else if (reader.IsStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace))
3328                     {
3329                         statement.Attributes.Add(this.ReadAttribute(reader));
3330                     }
3331                     else
3332                     {
3333                         break;
3334                     }
3335                 }
3336
3337                 // At least one attribute expected
3338                 if (0 == statement.Attributes.Count)
3339                 {
3340                     reader.ReadStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
3341                 }
3342
3343                 reader.ReadEndElement();
3344
3345                 return statement;
3346             }
3347             catch (Exception e)
3348             {
3349                 if (System.Runtime.Fx.IsFatal(e))
3350                     throw;
3351                 
3352                 Exception wrapped = TryWrapReadException(reader, e);
3353                 if (null == wrapped)
3354                 {
3355                     throw;
3356                 }
3357                 else
3358                 {
3359                     throw wrapped;
3360                 }
3361             }
3362         }
3363
3364         /// <summary>
3365         /// Writes the &lt;saml:AttributeStatement> element.
3366         /// </summary>
3367         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AttributeStatement"/>.</param>
3368         /// <param name="data">The <see cref="Saml2AttributeStatement"/> to serialize.</param>
3369         protected virtual void WriteAttributeStatement(XmlWriter writer, Saml2AttributeStatement data)
3370         {
3371             if (null == writer)
3372             {
3373                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3374             }
3375
3376             if (null == data)
3377             {
3378                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3379             }
3380
3381             if (data.Attributes == null || 0 == data.Attributes.Count)
3382             {
3383                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4124)));
3384             }
3385
3386             // <AttributeStatement>
3387             writer.WriteStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace);
3388
3389             // <Attribute> 1-OO
3390             foreach (Saml2Attribute attribute in data.Attributes)
3391             {
3392                 this.WriteAttribute(writer, attribute);
3393             }
3394
3395             // </AttributeStatement>
3396             writer.WriteEndElement();
3397         }
3398
3399         /// <summary>
3400         /// Reads the &lt;saml:AudienceRestriction> element or a 
3401         /// &lt;saml:Condition> element that specifies an xsi:type
3402         /// of saml:AudienceRestrictionType.
3403         /// </summary>
3404         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AudienceRestriction"/> element.</param>
3405         /// <returns>A <see cref="Saml2AudienceRestriction"/> instance.</returns>
3406         protected virtual Saml2AudienceRestriction ReadAudienceRestriction(XmlReader reader)
3407         {
3408             if (null == reader)
3409             {
3410                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3411             }
3412
3413             // throw if wrong element
3414             bool isConditionElement = false;
3415             if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace))
3416             {
3417                 isConditionElement = true;
3418             }
3419             else if (!reader.IsStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace))
3420             {
3421                 reader.ReadStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace);
3422             }
3423
3424             try
3425             {
3426                 Saml2AudienceRestriction audienceRestriction;
3427                 bool isEmpty = reader.IsEmptyElement;
3428
3429                 // @attributes
3430
3431                 // @xsi:type -- if we're a <Condition> element, this declaration must be present
3432                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AudienceRestrictionType, Saml2Constants.Namespace, isConditionElement);
3433
3434                 // disallow empty
3435                 if (isEmpty)
3436                 {
3437                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
3438                 }
3439
3440                 // content
3441                 reader.Read();
3442
3443                 // <Audience> - 1-OO
3444                 if (!reader.IsStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace))
3445                 {
3446                     reader.ReadStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace);
3447                 }
3448
3449                 // We are now laxing the uri check for audience restriction to support interop partners 
3450                 // This is a specific request from server : 
3451
3452                 audienceRestriction = new Saml2AudienceRestriction(ReadSimpleUriElement(reader, UriKind.RelativeOrAbsolute, true));
3453                 while (reader.IsStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace))
3454                 {
3455                     audienceRestriction.Audiences.Add(ReadSimpleUriElement(reader, UriKind.RelativeOrAbsolute, true));
3456                 }
3457
3458                 reader.ReadEndElement();
3459
3460                 return audienceRestriction;
3461             }
3462             catch (Exception e)
3463             {
3464                 if (System.Runtime.Fx.IsFatal(e))
3465                     throw;
3466                 
3467                 Exception wrapped = TryWrapReadException(reader, e);
3468                 if (null == wrapped)
3469                 {
3470                     throw;
3471                 }
3472                 else
3473                 {
3474                     throw wrapped;
3475                 }
3476             }
3477         }
3478
3479         /// <summary>
3480         /// Writes the &lt;saml:AudienceRestriction> element.
3481         /// </summary>
3482         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AudienceRestriction"/>.</param>
3483         /// <param name="data">The <see cref="Saml2AudienceRestriction"/> to serialize.</param>
3484         protected virtual void WriteAudienceRestriction(XmlWriter writer, Saml2AudienceRestriction data)
3485         {
3486             if (null == writer)
3487             {
3488                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3489             }
3490
3491             if (null == data)
3492             {
3493                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3494             }
3495
3496             // Schema requires at least one audience.
3497             if (data.Audiences == null || 0 == data.Audiences.Count)
3498             {
3499                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4159)));
3500             }
3501
3502             // <AudienceRestriction>
3503             writer.WriteStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace);
3504
3505             // <Audience> - 1-OO
3506             foreach (Uri audience in data.Audiences)
3507             {
3508                 // 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. 
3509                 writer.WriteElementString(Saml2Constants.Elements.Audience, Saml2Constants.Namespace, audience.OriginalString);
3510             }
3511
3512             // </AudienceRestriction>
3513             writer.WriteEndElement();
3514         }
3515
3516         /// <summary>
3517         /// Reads the &lt;saml:AuthnContext> element.
3518         /// </summary>
3519         /// <remarks>
3520         /// The default implementation does not handle the optional 
3521         /// &lt;saml:AuthnContextDecl> element. To handle by-value 
3522         /// authentication context declarations, override this method.
3523         /// </remarks>
3524         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AuthenticationContext"/> element.</param>
3525         /// <returns>A <see cref="Saml2AuthenticationContext"/> instance.</returns>
3526         protected virtual Saml2AuthenticationContext ReadAuthenticationContext(XmlReader reader)
3527         {
3528             if (null == reader)
3529             {
3530                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3531             }
3532
3533             // throw if wrong element
3534             if (!reader.IsStartElement(Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace))
3535             {
3536                 reader.ReadStartElement(Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace);
3537             }
3538
3539             try
3540             {
3541                 // Disallow empty
3542                 if (reader.IsEmptyElement)
3543                 {
3544                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace));
3545                 }
3546
3547                 // @attributes
3548
3549                 // @xsi:type
3550                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AuthnContextType, Saml2Constants.Namespace);
3551
3552                 // Content
3553                 reader.ReadStartElement();
3554
3555                 // At least one of ClassRef and ( Decl XOR DeclRef) must be present
3556                 // At this time, we do not support Decl, which is a by-value 
3557                 // authentication context declaration.
3558                 Uri classRef = null;
3559                 Uri declRef = null;
3560
3561                 // <AuthnContextClassRef> - see comment above
3562                 if (reader.IsStartElement(Saml2Constants.Elements.AuthnContextClassRef, Saml2Constants.Namespace))
3563                 {
3564                     classRef = ReadSimpleUriElement(reader);
3565                 }
3566
3567                 // <AuthnContextDecl> - see comment above
3568                 if (reader.IsStartElement(Saml2Constants.Elements.AuthnContextDecl, Saml2Constants.Namespace))
3569                 {
3570                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4118));
3571                 }
3572
3573                 // <AuthnContextDeclRef> - see comment above
3574                 // If there was no ClassRef, there must be a DeclRef
3575                 if (reader.IsStartElement(Saml2Constants.Elements.AuthnContextDeclRef, Saml2Constants.Namespace))
3576                 {
3577                     declRef = ReadSimpleUriElement(reader);
3578                 }
3579                 else if (null == classRef)
3580                 {
3581                     reader.ReadStartElement(Saml2Constants.Elements.AuthnContextDeclRef, Saml2Constants.Namespace);
3582                 }
3583
3584                 // Now we have enough data to create the object
3585                 Saml2AuthenticationContext authnContext = new Saml2AuthenticationContext(classRef, declRef);
3586
3587                 // <AuthenticatingAuthority> - 0-OO
3588                 while (reader.IsStartElement(Saml2Constants.Elements.AuthenticatingAuthority, Saml2Constants.Namespace))
3589                 {
3590                     authnContext.AuthenticatingAuthorities.Add(ReadSimpleUriElement(reader));
3591                 }
3592
3593                 reader.ReadEndElement();
3594
3595                 return authnContext;
3596             }
3597             catch (Exception e)
3598             {
3599                 if (System.Runtime.Fx.IsFatal(e))
3600                     throw;
3601                 
3602                 Exception wrapped = TryWrapReadException(reader, e);
3603                 if (null == wrapped)
3604                 {
3605                     throw;
3606                 }
3607                 else
3608                 {
3609                     throw wrapped;
3610                 }
3611             }
3612         }
3613
3614         /// <summary>
3615         /// Writes the &lt;saml:AuthnContext> element.
3616         /// </summary>
3617         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AuthenticationContext"/>.</param>
3618         /// <param name="data">The <see cref="Saml2AuthenticationContext"/> to serialize.</param>
3619         protected virtual void WriteAuthenticationContext(XmlWriter writer, Saml2AuthenticationContext data)
3620         {
3621             if (null == writer)
3622             {
3623                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3624             }
3625
3626             if (null == data)
3627             {
3628                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3629             }
3630
3631             // One of ClassRef and DeclRef must be present.
3632             if (null == data.ClassReference && null == data.DeclarationReference)
3633             {
3634                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3635                     new InvalidOperationException(SR.GetString(SR.ID4117)));
3636             }
3637
3638             // <AuthnContext>
3639             writer.WriteStartElement(Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace);
3640
3641             // <AuthnContextClassReference> 0-1
3642             if (null != data.ClassReference)
3643             {
3644                 writer.WriteElementString(Saml2Constants.Elements.AuthnContextClassRef, Saml2Constants.Namespace, data.ClassReference.AbsoluteUri);
3645             }
3646
3647             // <AuthnContextDeclRef> 0-1
3648             if (null != data.DeclarationReference)
3649             {
3650                 writer.WriteElementString(Saml2Constants.Elements.AuthnContextDeclRef, Saml2Constants.Namespace, data.DeclarationReference.AbsoluteUri);
3651             }
3652
3653             // <AuthenticatingAuthority> 0-OO
3654             foreach (Uri authority in data.AuthenticatingAuthorities)
3655             {
3656                 writer.WriteElementString(Saml2Constants.Elements.AuthenticatingAuthority, Saml2Constants.Namespace, authority.AbsoluteUri);
3657             }
3658
3659             // </AuthnContext>
3660             writer.WriteEndElement();
3661         }
3662
3663         /// <summary>
3664         /// Reads the &lt;saml:AuthnStatement> element or a &lt;saml:Statement>
3665         /// element that specifies an xsi:type of saml:AuthnStatementType.
3666         /// </summary>
3667         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AuthenticationStatement"/> element.</param>
3668         /// <returns>A <see cref="Saml2AuthenticationStatement"/> instance.</returns>
3669         protected virtual Saml2AuthenticationStatement ReadAuthenticationStatement(XmlReader reader)
3670         {
3671             if (null == reader)
3672             {
3673                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3674             }
3675
3676             // throw if wrong element
3677             bool isStatementElement = false;
3678             if (reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
3679             {
3680                 isStatementElement = true;
3681             }
3682             else if (!reader.IsStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace))
3683             {
3684                 reader.ReadStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace);
3685             }
3686
3687             try
3688             {
3689                 // Must cache the individual data since the required 
3690                 // AuthnContext comes last
3691                 DateTime authnInstant;
3692                 Saml2AuthenticationContext authnContext;
3693                 string sessionIndex;
3694                 DateTime? sessionNotOnOrAfter = null;
3695                 Saml2SubjectLocality subjectLocality = null;
3696
3697                 // defer disallowing empty until after xsi:type
3698                 bool isEmpty = reader.IsEmptyElement;
3699
3700                 // @attributes
3701                 string value;
3702
3703                 // @xsi:type -- if we're a <Statement> element, this declaration must be present
3704                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AuthnStatementType, Saml2Constants.Namespace, isStatementElement);
3705
3706                 // disallow empty, since xsi:type is ok
3707                 if (isEmpty)
3708                 {
3709                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace));
3710                 }
3711
3712                 // @AuthnInstant - required
3713                 value = reader.GetAttribute(Saml2Constants.Attributes.AuthnInstant);
3714                 if (string.IsNullOrEmpty(value))
3715                 {
3716                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.AuthnInstant, Saml2Constants.Elements.AuthnStatement));
3717                 }
3718
3719                 authnInstant = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
3720
3721                 // @SessionIndex - optional
3722                 sessionIndex = reader.GetAttribute(Saml2Constants.Attributes.SessionIndex);
3723
3724                 // @SessionNotOnOrAfter - optional
3725                 value = reader.GetAttribute(Saml2Constants.Attributes.SessionNotOnOrAfter);
3726                 if (!string.IsNullOrEmpty(value))
3727                 {
3728                     sessionNotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
3729                 }
3730
3731                 // Content
3732                 reader.Read();
3733
3734                 // <SubjectLocality> 0-1
3735                 if (reader.IsStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace))
3736                 {
3737                     subjectLocality = this.ReadSubjectLocality(reader);
3738                 }
3739
3740                 // <AuthnContext> 1
3741                 authnContext = this.ReadAuthenticationContext(reader);
3742
3743                 reader.ReadEndElement();
3744
3745                 // Construct the actual object
3746                 Saml2AuthenticationStatement authnStatement = new Saml2AuthenticationStatement(authnContext, authnInstant);
3747                 authnStatement.SessionIndex = sessionIndex;
3748                 authnStatement.SessionNotOnOrAfter = sessionNotOnOrAfter;
3749                 authnStatement.SubjectLocality = subjectLocality;
3750
3751                 return authnStatement;
3752             }
3753             catch (Exception e)
3754             {
3755                 if (System.Runtime.Fx.IsFatal(e))
3756                     throw;
3757                 
3758                 Exception wrapped = TryWrapReadException(reader, e);
3759                 if (null == wrapped)
3760                 {
3761                     throw;
3762                 }
3763                 else
3764                 {
3765                     throw wrapped;
3766                 }
3767             }
3768         }
3769
3770         /// <summary>
3771         /// Writes the &lt;saml:AuthnStatement> element.
3772         /// </summary>
3773         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AuthenticationStatement"/>.</param>
3774         /// <param name="data">The <see cref="Saml2AuthenticationStatement"/> to serialize.</param>
3775         protected virtual void WriteAuthenticationStatement(XmlWriter writer, Saml2AuthenticationStatement data)
3776         {
3777             if (null == writer)
3778             {
3779                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3780             }
3781
3782             if (null == data)
3783             {
3784                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3785             }
3786
3787             // <AuthnStatement>
3788             writer.WriteStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace);
3789
3790             // @AuthnInstant - required
3791             writer.WriteAttributeString(Saml2Constants.Attributes.AuthnInstant, XmlConvert.ToString(data.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated));
3792
3793             // @SessionIndex - optional
3794             if (null != data.SessionIndex)
3795             {
3796                 writer.WriteAttributeString(Saml2Constants.Attributes.SessionIndex, data.SessionIndex);
3797             }
3798
3799             // @SessionNotOnOrAfter - optional
3800             if (null != data.SessionNotOnOrAfter)
3801             {
3802                 writer.WriteAttributeString(Saml2Constants.Attributes.SessionNotOnOrAfter, XmlConvert.ToString(data.SessionNotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
3803             }
3804
3805             // <SubjectLocality> 0-1
3806             if (null != data.SubjectLocality)
3807             {
3808                 this.WriteSubjectLocality(writer, data.SubjectLocality);
3809             }
3810
3811             // <AuthnContext> 1
3812             this.WriteAuthenticationContext(writer, data.AuthenticationContext);
3813
3814             // </AuthnStatement>
3815             writer.WriteEndElement();
3816         }
3817
3818         /// <summary>
3819         /// Reads the &lt;saml:AuthzDecisionStatement> element or a 
3820         /// &lt;saml:Statement element that specifies an xsi:type of
3821         /// saml:AuthzDecisionStatementType.
3822         /// </summary>
3823         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AuthorizationDecisionStatement"/> element.</param>
3824         /// <returns>A <see cref="Saml2AuthorizationDecisionStatement"/> instance.</returns>
3825         protected virtual Saml2AuthorizationDecisionStatement ReadAuthorizationDecisionStatement(XmlReader reader)
3826         {
3827             if (null == reader)
3828             {
3829                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3830             }
3831
3832             // throw if wrong element
3833             bool isStatementElement = false;
3834             if (reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
3835             {
3836                 isStatementElement = true;
3837             }
3838             else if (!reader.IsStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace))
3839             {
3840                 reader.ReadStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace);
3841             }
3842
3843             try
3844             {
3845                 // Need the attributes before we can instantiate
3846                 Saml2AuthorizationDecisionStatement statement;
3847                 SamlAccessDecision decision;
3848                 Uri resource;
3849
3850                 // defer rejecting empty until processing xsi:type
3851                 bool isEmpty = reader.IsEmptyElement;
3852
3853                 // @attributes
3854                 string value;
3855
3856                 // @xsi:type
3857                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AuthzDecisionStatementType, Saml2Constants.Namespace, isStatementElement);
3858
3859                 // disallow empty, since xsi:type is ok
3860                 if (isEmpty)
3861                 {
3862                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace));
3863                 }
3864
3865                 // @Decision - required
3866                 value = reader.GetAttribute(Saml2Constants.Attributes.Decision);
3867                 if (string.IsNullOrEmpty(value))
3868                 {
3869                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Decision, Saml2Constants.Elements.AuthzDecisionStatement));
3870                 }
3871                 else if (StringComparer.Ordinal.Equals(SamlAccessDecision.Permit.ToString(), value))
3872                 {
3873                     decision = SamlAccessDecision.Permit;
3874                 }
3875                 else if (StringComparer.Ordinal.Equals(SamlAccessDecision.Deny.ToString(), value))
3876                 {
3877                     decision = SamlAccessDecision.Deny;
3878                 }
3879                 else if (StringComparer.Ordinal.Equals(SamlAccessDecision.Indeterminate.ToString(), value))
3880                 {
3881                     decision = SamlAccessDecision.Indeterminate;
3882                 }
3883                 else
3884                 {
3885                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4123, value));
3886                 }
3887
3888                 // @Resource - required
3889                 value = reader.GetAttribute(Saml2Constants.Attributes.Resource);
3890                 if (null == value)
3891                 {
3892                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Resource, Saml2Constants.Elements.AuthzDecisionStatement));
3893                 }
3894                 else if (0 == value.Length)
3895                 {
3896                     resource = Saml2AuthorizationDecisionStatement.EmptyResource;
3897                 }
3898                 else
3899                 {
3900                     if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
3901                     {
3902                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4121));
3903                     }
3904
3905                     resource = new Uri(value);
3906                 }
3907
3908                 // Content
3909                 statement = new Saml2AuthorizationDecisionStatement(resource, decision);
3910                 reader.Read();
3911
3912                 // <Action> 1-OO 
3913                 do
3914                 {
3915                     statement.Actions.Add(this.ReadAction(reader));
3916                 }
3917                 while (reader.IsStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace));
3918
3919                 // <Evidence> 0-1
3920                 if (reader.IsStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace))
3921                 {
3922                     statement.Evidence = this.ReadEvidence(reader);
3923                 }
3924
3925                 reader.ReadEndElement();
3926
3927                 return statement;
3928             }
3929             catch (Exception e)
3930             {
3931                 if (System.Runtime.Fx.IsFatal(e))
3932                     throw;
3933                 
3934                 Exception wrapped = TryWrapReadException(reader, e);
3935                 if (null == wrapped)
3936                 {
3937                     throw;
3938                 }
3939                 else
3940                 {
3941                     throw wrapped;
3942                 }
3943             }
3944         }
3945
3946         /// <summary>
3947         /// Writes the &lt;saml:AuthzDecisionStatement> element.
3948         /// </summary>
3949         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AuthorizationDecisionStatement"/>.</param>
3950         /// <param name="data">The <see cref="Saml2AuthorizationDecisionStatement"/> to serialize.</param>
3951         protected virtual void WriteAuthorizationDecisionStatement(XmlWriter writer, Saml2AuthorizationDecisionStatement data)
3952         {
3953             if (null == writer)
3954             {
3955                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3956             }
3957
3958             if (null == data)
3959             {
3960                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3961             }
3962
3963 #pragma warning suppress 56506 // actions are never null
3964             if (0 == data.Actions.Count)
3965             {
3966                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3967                     new InvalidOperationException(SR.GetString(SR.ID4122)));
3968             }
3969
3970             // <AuthzDecisionStatement>
3971             writer.WriteStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace);
3972
3973             // @Decision - required
3974             writer.WriteAttributeString(Saml2Constants.Attributes.Decision, data.Decision.ToString());
3975
3976             // @Resource - required
3977 #pragma warning suppress 56506 // Resource are never null
3978             writer.WriteAttributeString(Saml2Constants.Attributes.Resource, data.Resource.Equals(Saml2AuthorizationDecisionStatement.EmptyResource) ? data.Resource.ToString() : data.Resource.AbsoluteUri);
3979
3980             // @Action 1-OO
3981             foreach (Saml2Action action in data.Actions)
3982             {
3983                 this.WriteAction(writer, action);
3984             }
3985
3986             // Evidence 0-1
3987             if (null != data.Evidence)
3988             {
3989                 this.WriteEvidence(writer, data.Evidence);
3990             }
3991
3992             // </AuthzDecisionStatement>
3993             writer.WriteEndElement();
3994         }
3995
3996         /// <summary>
3997         /// Reads the &lt;saml:Conditions> element.
3998         /// </summary>
3999         /// <remarks>
4000         /// To handle custom &lt;saml:Condition> elements, override this 
4001         /// method.
4002         /// </remarks>
4003         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Conditions"/> element.</param>
4004         /// <returns>A <see cref="Saml2Conditions"/> instance.</returns>
4005         protected virtual Saml2Conditions ReadConditions(XmlReader reader)
4006         {
4007             if (null == reader)
4008             {
4009                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4010             }
4011
4012             // throw if wrong element
4013             if (!reader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace))
4014             {
4015                 reader.ReadStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace);
4016             }
4017
4018             try
4019             {
4020                 Saml2Conditions conditions = new Saml2Conditions();
4021
4022                 bool isEmpty = reader.IsEmptyElement;
4023
4024                 // @attributes
4025                 string value;
4026
4027                 // @xsi:type
4028                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ConditionsType, Saml2Constants.Namespace);
4029
4030                 // @NotBefore - optional
4031                 value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore);
4032                 if (!string.IsNullOrEmpty(value))
4033                 {
4034                     conditions.NotBefore = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
4035                 }
4036
4037                 // @NotOnOrAfter - optional
4038                 value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter);
4039                 if (!string.IsNullOrEmpty(value))
4040                 {
4041                     conditions.NotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
4042                 }
4043
4044                 // Content
4045                 reader.ReadStartElement();
4046                 if (!isEmpty)
4047                 {
4048                     // <Condition|AudienceRestriction|OneTimeUse|ProxyRestriction>, 0-OO
4049                     while (reader.IsStartElement())
4050                     {
4051                         // <Condition> - 0-OO
4052                         if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace))
4053                         {
4054                             // Since Condition is abstract, must process based on xsi:type
4055                             XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader);
4056
4057                             // No type, throw
4058                             if (null == declaredType
4059                                 || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.ConditionAbstractType, Saml2Constants.Namespace))
4060                             {
4061                                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI));
4062                             }
4063                             else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AudienceRestrictionType, Saml2Constants.Namespace))
4064                             {
4065                                 conditions.AudienceRestrictions.Add(this.ReadAudienceRestriction(reader));
4066                             }
4067                             else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.OneTimeUseType, Saml2Constants.Namespace))
4068                             {
4069                                 if (conditions.OneTimeUse)
4070                                 {
4071                                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.OneTimeUse));
4072                                 }
4073
4074                                 ReadEmptyContentElement(reader);
4075                                 conditions.OneTimeUse = true;
4076                             }
4077                             else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.ProxyRestrictionType, Saml2Constants.Namespace))
4078                             {
4079                                 if (null != conditions.ProxyRestriction)
4080                                 {
4081                                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.ProxyRestricton));
4082                                 }
4083
4084                                 conditions.ProxyRestriction = this.ReadProxyRestriction(reader);
4085                             }
4086                             else
4087                             {
4088                                 // Unknown type - Instruct the user to override to handle custom <Condition>
4089                                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4113));
4090                             }
4091                         }
4092                         else if (reader.IsStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace))
4093                         {
4094                             conditions.AudienceRestrictions.Add(this.ReadAudienceRestriction(reader));
4095                         }
4096                         else if (reader.IsStartElement(Saml2Constants.Elements.OneTimeUse, Saml2Constants.Namespace))
4097                         {
4098                             if (conditions.OneTimeUse)
4099                             {
4100                                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.OneTimeUse));
4101                             }
4102
4103                             ReadEmptyContentElement(reader);
4104                             conditions.OneTimeUse = true;
4105                         }
4106                         else if (reader.IsStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace))
4107                         {
4108                             if (null != conditions.ProxyRestriction)
4109                             {
4110                                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.ProxyRestricton));
4111                             }
4112
4113                             conditions.ProxyRestriction = this.ReadProxyRestriction(reader);
4114                         }
4115                         else
4116                         {
4117                             break;
4118                         }
4119                     }
4120
4121                     reader.ReadEndElement();
4122                 }
4123
4124                 return conditions;
4125             }
4126             catch (Exception e)
4127             {
4128                 if (System.Runtime.Fx.IsFatal(e))
4129                     throw;
4130                 
4131                 Exception wrapped = TryWrapReadException(reader, e);
4132                 if (null == wrapped)
4133                 {
4134                     throw;
4135                 }
4136                 else
4137                 {
4138                     throw wrapped;
4139                 }
4140             }
4141         }
4142
4143         /// <summary>
4144         /// Writes the &lt;saml:Conditions> element.
4145         /// </summary>
4146         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Conditions"/>.</param>
4147         /// <param name="data">The <see cref="Saml2Conditions"/> to serialize.</param>
4148         protected virtual void WriteConditions(XmlWriter writer, Saml2Conditions data)
4149         {
4150             if (null == writer)
4151             {
4152                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4153             }
4154
4155             if (null == data)
4156             {
4157                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4158             }
4159
4160             // <Conditions>
4161             writer.WriteStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace);
4162
4163             // @NotBefore - optional
4164             if (null != data.NotBefore)
4165             {
4166                 writer.WriteAttributeString(Saml2Constants.Attributes.NotBefore, XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated));
4167             }
4168
4169             // @NotOnOrAfter - optional
4170             if (null != data.NotOnOrAfter)
4171             {
4172                 writer.WriteAttributeString(Saml2Constants.Attributes.NotOnOrAfter, XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
4173             }
4174
4175             // <AudienceRestriction> 0-OO
4176             foreach (Saml2AudienceRestriction audienceRestriction in data.AudienceRestrictions)
4177             {
4178                 this.WriteAudienceRestriction(writer, audienceRestriction);
4179             }
4180
4181             // <OneTimeUse> - limited to one in SAML spec
4182             if (data.OneTimeUse)
4183             {
4184                 writer.WriteStartElement(Saml2Constants.Elements.OneTimeUse, Saml2Constants.Namespace);
4185                 writer.WriteEndElement();
4186             }
4187
4188             // <ProxyRestriction> - limited to one in SAML spec
4189             if (null != data.ProxyRestriction)
4190             {
4191                 this.WriteProxyRestriction(writer, data.ProxyRestriction);
4192             }
4193
4194             // </Conditions>
4195             writer.WriteEndElement();
4196         }
4197
4198         /// <summary>
4199         /// Reads the &lt;saml:Evidence> element.
4200         /// </summary>
4201         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Evidence"/> element.</param>
4202         /// <returns>A <see cref="Saml2Evidence"/> instance.</returns>
4203         protected virtual Saml2Evidence ReadEvidence(XmlReader reader)
4204         {
4205             if (null == reader)
4206             {
4207                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4208             }
4209
4210             // throw if wrong element
4211             if (!reader.IsStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace))
4212             {
4213                 reader.ReadStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace);
4214             }
4215
4216             // disallow empty
4217             if (reader.IsEmptyElement)
4218             {
4219                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.Evidence, Saml2Constants.Namespace));
4220             }
4221
4222             try
4223             {
4224                 Saml2Evidence evidence = new Saml2Evidence();
4225
4226                 // @attributes
4227
4228                 // @xsi:type
4229                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.EvidenceType, Saml2Constants.Namespace);
4230
4231                 reader.Read();
4232
4233                 // <AssertionIDRef|AssertionURIRef|Assertion|EncryptedAssertion> 0-OO
4234                 while (reader.IsStartElement())
4235                 {
4236                     if (reader.IsStartElement(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace))
4237                     {
4238                         evidence.AssertionIdReferences.Add(ReadSimpleNCNameElement(reader));
4239                     }
4240                     else if (reader.IsStartElement(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace))
4241                     {
4242                         evidence.AssertionUriReferences.Add(ReadSimpleUriElement(reader));
4243                     }
4244                     else if (reader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace))
4245                     {
4246                         evidence.Assertions.Add(this.ReadAssertion(reader));
4247                     }
4248                     else if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace))
4249                     {
4250                         evidence.Assertions.Add(this.ReadAssertion(reader));
4251                     }
4252                 }
4253
4254                 if (0 == evidence.AssertionIdReferences.Count
4255                         && 0 == evidence.Assertions.Count
4256                         && 0 == evidence.AssertionUriReferences.Count)
4257                 {
4258                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4120));
4259                 }
4260
4261                 reader.ReadEndElement();
4262
4263                 return evidence;
4264             }
4265             catch (Exception e)
4266             {
4267                 if (System.Runtime.Fx.IsFatal(e))
4268                     throw;
4269                 
4270                 Exception wrapped = TryWrapReadException(reader, e);
4271                 if (null == wrapped)
4272                 {
4273                     throw;
4274                 }
4275                 else
4276                 {
4277                     throw wrapped;
4278                 }
4279             }
4280         }
4281
4282         /// <summary>
4283         /// Writes the &lt;saml:Evidence> element.
4284         /// </summary>
4285         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Evidence"/>.</param>
4286         /// <param name="data">The <see cref="Saml2Evidence"/> to serialize.</param>
4287         protected virtual void WriteEvidence(XmlWriter writer, Saml2Evidence data)
4288         {
4289             if (null == writer)
4290             {
4291                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4292             }
4293
4294             if (null == data)
4295             {
4296                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4297             }
4298
4299             if ((data.AssertionIdReferences == null || 0 == data.AssertionIdReferences.Count)
4300                && (data.Assertions == null || 0 == data.Assertions.Count)
4301                && (data.AssertionUriReferences == null || 0 == data.AssertionUriReferences.Count))
4302             {
4303                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
4304                     new InvalidOperationException(SR.GetString(SR.ID4120)));
4305             }
4306
4307             // <Evidence>
4308             writer.WriteStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace);
4309
4310             // <AssertionIDRef> 0-OO
4311             foreach (Saml2Id id in data.AssertionIdReferences)
4312             {
4313                 writer.WriteElementString(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace, id.Value);
4314             }
4315
4316             // <AssertionURIRef> 0-OO
4317             foreach (Uri uri in data.AssertionUriReferences)
4318             {
4319                 writer.WriteElementString(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace, uri.AbsoluteUri);
4320             }
4321
4322             // <Assertion> 0-OO
4323             foreach (Saml2Assertion assertion in data.Assertions)
4324             {
4325                 this.WriteAssertion(writer, assertion);
4326             }
4327
4328             // </Evidence>
4329             writer.WriteEndElement();
4330         }
4331
4332         /// <summary>
4333         /// Reads the &lt;saml:Issuer> element.
4334         /// </summary>
4335         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
4336         /// <returns>A <see cref="Saml2NameIdentifier"/> instance.</returns>
4337         protected virtual Saml2NameIdentifier ReadIssuer(XmlReader reader)
4338         {
4339             if (null == reader)
4340             {
4341                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4342             }
4343
4344             // throw if wrong element
4345             if (!reader.IsStartElement(Saml2Constants.Elements.Issuer, Saml2Constants.Namespace))
4346             {
4347                 reader.ReadStartElement(Saml2Constants.Elements.Issuer, Saml2Constants.Namespace);
4348             }
4349
4350             return this.ReadNameIdType(reader);
4351         }
4352
4353         /// <summary>
4354         /// Writes the &lt;saml:Issuer> element.
4355         /// </summary>
4356         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
4357         /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
4358         protected virtual void WriteIssuer(XmlWriter writer, Saml2NameIdentifier data)
4359         {
4360             if (null == writer)
4361             {
4362                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4363             }
4364
4365             if (null == data)
4366             {
4367                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4368             }
4369
4370             writer.WriteStartElement(Saml2Constants.Elements.Issuer, Saml2Constants.Namespace);
4371             this.WriteNameIdType(writer, data);
4372             writer.WriteEndElement();
4373         }
4374
4375         /// <summary>
4376         /// Deserializes the SAML Subject KeyInfo.
4377         /// </summary>
4378         /// <param name="reader">XmlReader positioned at a ds:KeyInfo element.</param>
4379         /// <returns>A <see cref="SecurityKeyIdentifier"/> instance.</returns>
4380         /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
4381         protected virtual SecurityKeyIdentifier ReadSubjectKeyInfo(XmlReader reader)
4382         {
4383             if (null == reader)
4384             {
4385                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4386             }
4387
4388             return this.KeyInfoSerializer.ReadKeyIdentifier(reader);
4389         }
4390
4391         /// <summary>
4392         /// Deserializes the SAML Signing KeyInfo
4393         /// </summary>
4394         /// <param name="reader">A <see cref="XmlReader"/> positioned at a than can be positioned at a ds:KeyInfo element.</param>
4395         /// <param name="assertion">The <see cref="Saml2Assertion"/> that is having the signature checked.</param>
4396         /// <returns>The <see cref="SecurityKeyIdentifier"/> that defines the key to use to check the signature.</returns>
4397         /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
4398         protected virtual SecurityKeyIdentifier ReadSigningKeyInfo(XmlReader reader, Saml2Assertion assertion)
4399         {
4400             if (null == reader)
4401             {
4402                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4403             }
4404
4405             SecurityKeyIdentifier ski;
4406
4407             if (this.KeyInfoSerializer.CanReadKeyIdentifier(reader))
4408             {
4409                 ski = this.KeyInfoSerializer.ReadKeyIdentifier(reader);
4410             }
4411             else
4412             {
4413                 KeyInfo keyInfo = new KeyInfo(this.KeyInfoSerializer);
4414                 keyInfo.ReadXml(XmlDictionaryReader.CreateDictionaryReader(reader));
4415                 ski = keyInfo.KeyIdentifier;
4416             }
4417
4418             // no key info
4419             if (ski.Count == 0)
4420             {
4421                 return new SecurityKeyIdentifier(new Saml2SecurityKeyIdentifierClause(assertion));
4422             }
4423
4424             return ski;
4425         }
4426
4427         /// <summary>
4428         /// Serializes the Subject KeyInfo into the given XmlWriter.
4429         /// </summary>
4430         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifier"/>.</param>
4431         /// <param name="data">The <see cref="SecurityKeyIdentifier"/> to serialize.</param>
4432         /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'data' is null.</exception>
4433         protected virtual void WriteSubjectKeyInfo(XmlWriter writer, SecurityKeyIdentifier data)
4434         {
4435             if (null == writer)
4436             {
4437                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4438             }
4439
4440             if (null == data)
4441             {
4442                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4443             }
4444
4445             this.KeyInfoSerializer.WriteKeyIdentifier(writer, data);
4446         }
4447
4448         /// <summary>
4449         /// Serializes the Signing KeyInfo into the given XmlWriter.
4450         /// </summary>
4451         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifier"/>.</param>
4452         /// <param name="data">The <see cref="SecurityKeyIdentifier"/> to serialize.</param>
4453         /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'signingKeyIdentifier' is null.</exception>
4454         protected virtual void WriteSigningKeyInfo(XmlWriter writer, SecurityKeyIdentifier data)
4455         {
4456             if (null == writer)
4457             {
4458                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4459             }
4460
4461             if (null == data)
4462             {
4463                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4464             }
4465
4466             if (this.KeyInfoSerializer.CanWriteKeyIdentifier(data))
4467             {
4468                 this.KeyInfoSerializer.WriteKeyIdentifier(writer, data);
4469                 return;
4470             }
4471
4472             throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4221, data));
4473         }
4474
4475         /// <summary>
4476         /// Reads the &lt;saml:NameID> element.
4477         /// </summary>
4478         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
4479         /// <returns>An instance of <see cref="Saml2NameIdentifier"/></returns>
4480         /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
4481         protected virtual Saml2NameIdentifier ReadNameId(XmlReader reader)
4482         {
4483             if (null == reader)
4484             {
4485                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4486             }
4487
4488             // throw if wrong element
4489             if (!reader.IsStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace))
4490             {
4491                 reader.ReadStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
4492             }
4493
4494             return this.ReadNameIdType(reader);
4495         }
4496
4497         /// <summary>
4498         /// Writes the &lt;saml:NameID> element.
4499         /// </summary>
4500         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
4501         /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
4502         /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'data' is null.</exception>
4503         /// <exception cref="CryptographicException">Saml2NameIdentifier encrypting credentials must have a Symmetric Key specified.</exception>
4504         protected virtual void WriteNameId(XmlWriter writer, Saml2NameIdentifier data)
4505         {
4506             if (null == writer)
4507             {
4508                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4509             }
4510
4511             if (null == data)
4512             {
4513                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4514             }
4515
4516             // If there are encrypting credentials, then we need to encrypt the name identifier
4517             if (data.EncryptingCredentials != null)
4518             {
4519                 EncryptingCredentials encryptingCredentials = data.EncryptingCredentials;
4520
4521                 // Get the encryption key, which must be symmetric
4522                 SymmetricSecurityKey encryptingKey = encryptingCredentials.SecurityKey as SymmetricSecurityKey;
4523                 if (encryptingKey == null)
4524                 {
4525                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3284)));
4526                 }
4527
4528                 MemoryStream plaintextStream = null;
4529                 try
4530                 {
4531                     // Serialize an encrypted name ID
4532                     plaintextStream = new MemoryStream();
4533
4534                     using (XmlWriter plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false))
4535                     {
4536                         plaintextWriter.WriteStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
4537                         this.WriteNameIdType(plaintextWriter, data);
4538                         plaintextWriter.WriteEndElement();
4539                     }
4540
4541                     EncryptedDataElement encryptedData = new EncryptedDataElement();
4542                     encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
4543                     encryptedData.Algorithm = encryptingCredentials.Algorithm;
4544                     encryptedData.KeyIdentifier = encryptingCredentials.SecurityKeyIdentifier;
4545
4546                     // Perform encryption
4547                     SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(encryptingCredentials.Algorithm);
4548                     encryptedData.Encrypt(symmetricAlgorithm, plaintextStream.GetBuffer(), 0, (int)plaintextStream.Length);
4549                     ((IDisposable)plaintextStream).Dispose();
4550
4551                     writer.WriteStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace);
4552                     encryptedData.WriteXml(writer, this.KeyInfoSerializer);
4553
4554                     foreach (EncryptedKeyIdentifierClause clause in data.ExternalEncryptedKeys)
4555                     {
4556                         this.KeyInfoSerializer.WriteKeyIdentifierClause(writer, clause);
4557                     }
4558
4559                     writer.WriteEndElement();
4560                 }
4561                 finally
4562                 {
4563                     if (plaintextStream != null)
4564                     {
4565                         plaintextStream.Dispose();
4566                         plaintextStream = null;
4567                     }
4568                 }
4569             }
4570             else
4571             {
4572                 writer.WriteStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
4573                 this.WriteNameIdType(writer, data);
4574                 writer.WriteEndElement();
4575             }
4576         }
4577
4578         /// <summary>
4579         /// Both &lt;Issuer> and &lt;NameID> are of NameIDType. This method reads
4580         /// the content of either one of those elements. 
4581         /// </summary>
4582         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
4583         /// <returns>An instance of <see cref="Saml2NameIdentifier"/></returns>
4584         protected virtual Saml2NameIdentifier ReadNameIdType(XmlReader reader)
4585         {
4586             try
4587             {
4588                 reader.MoveToContent();
4589
4590                 Saml2NameIdentifier nameIdentifier = new Saml2NameIdentifier("__TemporaryName__");
4591
4592                 // @attributes
4593                 string value;
4594
4595                 // @xsi:type
4596                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.NameIDType, Saml2Constants.Namespace);
4597
4598                 // @Format - optional
4599                 value = reader.GetAttribute(Saml2Constants.Attributes.Format);
4600                 if (!string.IsNullOrEmpty(value))
4601                 {
4602                     if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
4603                     {
4604                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Format, Saml2Constants.Elements.NameID));
4605                     }
4606
4607                     nameIdentifier.Format = new Uri(value);
4608                 }
4609
4610                 // @NameQualifier - optional
4611                 value = reader.GetAttribute(Saml2Constants.Attributes.NameQualifier);
4612                 if (!string.IsNullOrEmpty(value))
4613                 {
4614                     nameIdentifier.NameQualifier = value;
4615                 }
4616
4617                 // @SPNameQualifier - optional
4618                 value = reader.GetAttribute(Saml2Constants.Attributes.SPNameQualifier);
4619                 if (!string.IsNullOrEmpty(value))
4620                 {
4621                     nameIdentifier.SPNameQualifier = value;
4622                 }
4623
4624                 // @SPProvidedID - optional
4625                 value = reader.GetAttribute(Saml2Constants.Attributes.SPProvidedID);
4626                 if (!string.IsNullOrEmpty(value))
4627                 {
4628                     nameIdentifier.SPProvidedId = value;
4629                 }
4630
4631                 // Content is string
4632                 nameIdentifier.Value = reader.ReadElementString();
4633
4634                 // According to section 8.3.6, if the name identifier format is of type 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'
4635                 // the name identifier value must be a uri and name qualifier, spname qualifier, and spproded id must be omitted.
4636                 if (nameIdentifier.Format != null &&
4637                     StringComparer.Ordinal.Equals(nameIdentifier.Format.AbsoluteUri, Saml2Constants.NameIdentifierFormats.Entity.AbsoluteUri))
4638                 {
4639                     if (!UriUtil.CanCreateValidUri(nameIdentifier.Value, UriKind.Absolute))
4640                     {
4641                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4262, nameIdentifier.Value, Saml2Constants.NameIdentifierFormats.Entity.AbsoluteUri));
4642                     }
4643
4644                     if (!string.IsNullOrEmpty(nameIdentifier.NameQualifier)
4645                         || !string.IsNullOrEmpty(nameIdentifier.SPNameQualifier)
4646                         || !string.IsNullOrEmpty(nameIdentifier.SPProvidedId))
4647                     {
4648                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4263, nameIdentifier.Value, Saml2Constants.NameIdentifierFormats.Entity.AbsoluteUri));
4649                     }
4650                 }
4651
4652                 return nameIdentifier;
4653             }
4654             catch (Exception e)
4655             {
4656                 if (System.Runtime.Fx.IsFatal(e))
4657                     throw;
4658                 
4659                 Exception wrapped = TryWrapReadException(reader, e);
4660                 if (null == wrapped)
4661                 {
4662                     throw;
4663                 }
4664                 else
4665                 {
4666                     throw wrapped;
4667                 }
4668             }
4669         }
4670
4671         /// <summary>
4672         /// Reads the &lt;saml:EncryptedId> element.
4673         /// </summary>
4674         /// <param name="reader"><see cref="XmlReader"/> pointing at the XML EncryptedId element</param>
4675         /// <returns>An instance of <see cref="Saml2NameIdentifier"/> representing the EncryptedId that was read</returns>
4676         /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
4677         /// <exception cref="XmlException">The 'reader' is not positioned at an 'EncryptedID' element.</exception>
4678         protected virtual Saml2NameIdentifier ReadEncryptedId(XmlReader reader)
4679         {
4680             if (null == reader)
4681             {
4682                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4683             }
4684
4685             reader.MoveToContent();
4686
4687             if (!reader.IsStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace))
4688             {
4689                 // throw if wrong element
4690                 reader.ReadStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace);
4691             }
4692
4693             Collection<EncryptedKeyIdentifierClause> clauses = new Collection<EncryptedKeyIdentifierClause>();
4694             EncryptingCredentials encryptingCredentials = null;
4695             Saml2NameIdentifier saml2NameIdentifier = null;
4696
4697             using (StringReader sr = new StringReader(reader.ReadOuterXml()))
4698             {
4699                 using (XmlDictionaryReader wrappedReader = new WrappedXmlDictionaryReader(XmlReader.Create(sr), XmlDictionaryReaderQuotas.Max))
4700                 {
4701                     XmlReader plaintextReader = CreatePlaintextReaderFromEncryptedData(
4702                                 wrappedReader,
4703                                 Configuration.ServiceTokenResolver,
4704                                 this.KeyInfoSerializer,
4705                                 clauses,
4706                                 out encryptingCredentials);
4707
4708                     saml2NameIdentifier = this.ReadNameIdType(plaintextReader);
4709                     saml2NameIdentifier.EncryptingCredentials = encryptingCredentials;
4710                     foreach (EncryptedKeyIdentifierClause clause in clauses)
4711                     {
4712                         saml2NameIdentifier.ExternalEncryptedKeys.Add(clause);
4713                     }
4714                 }
4715             }
4716
4717             return saml2NameIdentifier;
4718         }
4719
4720         /// <summary>
4721         /// Both &lt;Issuer> and &lt;NameID> are of NameIDType. This method writes
4722         /// the content of either one of those elements. 
4723         /// </summary>
4724         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
4725         /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
4726         protected virtual void WriteNameIdType(XmlWriter writer, Saml2NameIdentifier data)
4727         {
4728             // @Format - optional
4729             if (null != data.Format)
4730             {
4731                 writer.WriteAttributeString(Saml2Constants.Attributes.Format, data.Format.AbsoluteUri);
4732             }
4733
4734             // @NameQualifier - optional
4735             if (!string.IsNullOrEmpty(data.NameQualifier))
4736             {
4737                 writer.WriteAttributeString(Saml2Constants.Attributes.NameQualifier, data.NameQualifier);
4738             }
4739
4740             // @SPNameQualifier - optional
4741             if (!string.IsNullOrEmpty(data.SPNameQualifier))
4742             {
4743                 writer.WriteAttributeString(Saml2Constants.Attributes.SPNameQualifier, data.SPNameQualifier);
4744             }
4745
4746             // @SPProvidedId - optional
4747             if (!string.IsNullOrEmpty(data.SPProvidedId))
4748             {
4749                 writer.WriteAttributeString(Saml2Constants.Attributes.SPProvidedID, data.SPProvidedId);
4750             }
4751
4752             // Content is string
4753             writer.WriteString(data.Value);
4754         }
4755
4756         /// <summary>
4757         /// Reads the &lt;saml:ProxyRestriction> element, or a &lt;saml:Condition>
4758         /// element that specifies an xsi:type of saml:ProxyRestrictionType.
4759         /// </summary>
4760         /// <remarks>
4761         /// In the default implementation, the maximum value of the Count attribute 
4762         /// is limited to Int32.MaxValue.
4763         /// </remarks>
4764         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2ProxyRestriction"/> element.</param>
4765         /// <returns>An instance of <see cref="Saml2ProxyRestriction"/></returns>
4766         protected virtual Saml2ProxyRestriction ReadProxyRestriction(XmlReader reader)
4767         {
4768             if (null == reader)
4769             {
4770                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4771             }
4772
4773             // throw if wrong element
4774             bool isConditionElement = false;
4775             if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace))
4776             {
4777                 isConditionElement = true;
4778             }
4779             else if (!reader.IsStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace))
4780             {
4781                 reader.ReadStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace);
4782             }
4783
4784             try
4785             {
4786                 Saml2ProxyRestriction proxyRestriction = new Saml2ProxyRestriction();
4787
4788                 bool isEmpty = reader.IsEmptyElement;
4789
4790                 // @attributes
4791                 string value;
4792
4793                 // @xsi:type -- if we're a <Condition> element, this declaration must be present
4794                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ProxyRestrictionType, Saml2Constants.Namespace, isConditionElement);
4795
4796                 // @Count - optional
4797                 value = reader.GetAttribute(Saml2Constants.Attributes.Count);
4798                 if (!string.IsNullOrEmpty(value))
4799                 {
4800                     proxyRestriction.Count = XmlConvert.ToInt32(value);
4801                 }
4802
4803                 // content
4804                 reader.Read();
4805                 if (!isEmpty)
4806                 {
4807                     // <Audience> - 0-OO
4808                     while (reader.IsStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace))
4809                     {
4810                         proxyRestriction.Audiences.Add(ReadSimpleUriElement(reader));
4811                     }
4812
4813                     reader.ReadEndElement();
4814                 }
4815
4816                 return proxyRestriction;
4817             }
4818             catch (Exception e)
4819             {
4820                 if (System.Runtime.Fx.IsFatal(e))
4821                     throw;
4822                 
4823                 Exception wrapped = TryWrapReadException(reader, e);
4824                 if (null == wrapped)
4825                 {
4826                     throw;
4827                 }
4828                 else
4829                 {
4830                     throw wrapped;
4831                 }
4832             }
4833         }
4834
4835         /// <summary>
4836         /// Writes the &lt;saml:ProxyRestriction> element.
4837         /// </summary>
4838         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2ProxyRestriction"/>.</param>
4839         /// <param name="data">The <see cref="Saml2ProxyRestriction"/> to serialize.</param>
4840         protected virtual void WriteProxyRestriction(XmlWriter writer, Saml2ProxyRestriction data)
4841         {
4842             if (null == writer)
4843             {
4844                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4845             }
4846
4847             if (null == data)
4848             {
4849                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4850             }
4851
4852             writer.WriteStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace);
4853
4854             // @Count - optional
4855             if (null != data.Count)
4856             {
4857                 writer.WriteAttributeString(Saml2Constants.Attributes.Count, XmlConvert.ToString(data.Count.Value));
4858             }
4859
4860             // <Audience> - 0-OO
4861             foreach (Uri uri in data.Audiences)
4862             {
4863                 writer.WriteElementString(Saml2Constants.Elements.Audience, uri.AbsoluteUri);
4864             }
4865
4866             writer.WriteEndElement();
4867         }
4868
4869         /// <summary>
4870         /// Reads the &lt;saml:Statement> element.
4871         /// </summary>
4872         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Statement"/> element.</param>
4873         /// <returns>An instance of <see cref="Saml2Statement"/> derived type.</returns>
4874         /// <remarks>
4875         /// The default implementation only handles Statement elements which 
4876         /// specify an xsi:type of saml:AttributeStatementType, 
4877         /// saml:AuthnStatementType, and saml:AuthzDecisionStatementType. To 
4878         /// handle custom statements, override this method.
4879         /// </remarks>
4880         protected virtual Saml2Statement ReadStatement(XmlReader reader)
4881         {
4882             if (null == reader)
4883             {
4884                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4885             }
4886
4887             // throw if wrong element
4888             if (!reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
4889             {
4890                 reader.ReadStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace);
4891             }
4892
4893             // Since Statement is an abstract type, we have to switch off the xsi:type declaration
4894             XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader);
4895
4896             // No declaration, or declaring that this is just a "Statement", is invalid since 
4897             // statement is abstract
4898             if (null == declaredType
4899                 || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.StatementAbstractType, Saml2Constants.Namespace))
4900             {
4901                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI));
4902             }
4903
4904             // Reroute to the known statement types if applicable
4905             if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AttributeStatementType, Saml2Constants.Namespace))
4906             {
4907                 return this.ReadAttributeStatement(reader);
4908             }
4909             else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AuthnStatementType, Saml2Constants.Namespace))
4910             {
4911                 return this.ReadAuthenticationStatement(reader);
4912             }
4913             else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AuthzDecisionStatementType, Saml2Constants.Namespace))
4914             {
4915                 return this.ReadAuthorizationDecisionStatement(reader);
4916             }
4917             else
4918             {
4919                 // Throw if we encounter an unknown concrete type
4920                 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4105, declaredType.Name, declaredType.Namespace));
4921             }
4922         }
4923
4924         /// <summary>
4925         /// Writes a Saml2Statement.
4926         /// </summary>
4927         /// <remarks>
4928         /// This method may write a &lt;saml:AttributeStatement>, &lt;saml:AuthnStatement> 
4929         /// or &lt;saml:AuthzDecisionStatement> element. To handle custom Saml2Statement
4930         /// classes for writing a &lt;saml:Statement> element, override this method.
4931         /// </remarks>
4932         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Statement"/>.</param>
4933         /// <param name="data">The <see cref="Saml2Statement"/> to serialize.</param>
4934         protected virtual void WriteStatement(XmlWriter writer, Saml2Statement data)
4935         {
4936             if (null == writer)
4937             {
4938                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4939             }
4940
4941             if (null == data)
4942             {
4943                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4944             }
4945
4946             Saml2AttributeStatement attributeStatement = data as Saml2AttributeStatement;
4947             if (null != attributeStatement)
4948             {
4949                 this.WriteAttributeStatement(writer, attributeStatement);
4950                 return;
4951             }
4952
4953             Saml2AuthenticationStatement authnStatement = data as Saml2AuthenticationStatement;
4954             if (null != authnStatement)
4955             {
4956                 this.WriteAuthenticationStatement(writer, authnStatement);
4957                 return;
4958             }
4959
4960             Saml2AuthorizationDecisionStatement authzStatement = data as Saml2AuthorizationDecisionStatement;
4961             if (null != authzStatement)
4962             {
4963                 this.WriteAuthorizationDecisionStatement(writer, authzStatement);
4964                 return;
4965             }
4966
4967             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
4968                 new InvalidOperationException(SR.GetString(SR.ID4107, data.GetType().AssemblyQualifiedName)));
4969         }
4970
4971         /// <summary>
4972         /// Reads the &lt;saml:Subject> element.
4973         /// </summary>
4974         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Subject"/> element.</param>
4975         /// <returns>An instance of <see cref="Saml2Subject"/> .</returns>
4976         /// <remarks>
4977         /// The default implementation does not handle the optional
4978         /// &lt;EncryptedID> element. To handle encryped IDs in the Subject,
4979         /// override this method.
4980         /// </remarks>
4981         protected virtual Saml2Subject ReadSubject(XmlReader reader)
4982         {
4983             if (null == reader)
4984             {
4985                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4986             }
4987
4988             // throw if wrong element
4989             if (!reader.IsStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace))
4990             {
4991                 reader.ReadStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace);
4992             }
4993
4994             try
4995             {
4996                 // disallow empty
4997                 if (reader.IsEmptyElement)
4998                 {
4999 #pragma warning suppress 56504 // bogus - thinks reader.LocalName, reader.NamespaceURI need validation
5000                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
5001                 }
5002
5003                 // @attributes
5004
5005                 // @xsi:type
5006                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.SubjectType, Saml2Constants.Namespace);
5007
5008                 // <elements>
5009                 Saml2Subject subject = new Saml2Subject();
5010                 reader.Read();
5011
5012                 // <NameID> | <EncryptedID> | <BaseID> 0-1
5013                 subject.NameId = this.ReadSubjectId(reader, Saml2Constants.Elements.Subject);
5014
5015                 // <SubjectConfirmation> 0-OO
5016                 while (reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace))
5017                 {
5018                     subject.SubjectConfirmations.Add(this.ReadSubjectConfirmation(reader));
5019                 }
5020
5021                 reader.ReadEndElement();
5022
5023                 // Must have a NameID or a SubjectConfirmation
5024                 if (null == subject.NameId && 0 == subject.SubjectConfirmations.Count)
5025                 {
5026                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4108));
5027                 }
5028
5029                 return subject;
5030             }
5031             catch (Exception e)
5032             {
5033                 if (System.Runtime.Fx.IsFatal(e))
5034                     throw;
5035                 
5036                 Exception wrapped = TryWrapReadException(reader, e);
5037                 if (null == wrapped)
5038                 {
5039                     throw;
5040                 }
5041                 else
5042                 {
5043                     throw wrapped;
5044                 }
5045             }
5046         }
5047
5048         /// <summary>
5049         /// Writes the &lt;saml:Subject> element.
5050         /// </summary>
5051         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Subject"/>.</param>
5052         /// <param name="data">The <see cref="Saml2Subject"/> to serialize.</param>
5053         protected virtual void WriteSubject(XmlWriter writer, Saml2Subject data)
5054         {
5055             if (null == writer)
5056             {
5057                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5058             }
5059
5060             if (null == data)
5061             {
5062                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5063             }
5064
5065             // If there's no ID, there has to be a SubjectConfirmation
5066 #pragma warning suppress 56506 // SubjectConfirmations is never null
5067             if (null == data.NameId && 0 == data.SubjectConfirmations.Count)
5068             {
5069                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4108)));
5070             }
5071
5072             // <Subject>
5073             writer.WriteStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace);
5074
5075             // no attributes
5076
5077             // <NameID> 0-1
5078             if (null != data.NameId)
5079             {
5080                 this.WriteNameId(writer, data.NameId);
5081             }
5082
5083             // <SubjectConfirmation> 0-OO
5084             foreach (Saml2SubjectConfirmation subjectConfirmation in data.SubjectConfirmations)
5085             {
5086                 this.WriteSubjectConfirmation(writer, subjectConfirmation);
5087             }
5088
5089             // </Subject>
5090             writer.WriteEndElement();
5091         }
5092
5093         /// <summary>
5094         /// Reads the &lt;SubjectConfirmation> element.
5095         /// </summary>
5096         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectConfirmation"/> element.</param>
5097         /// <returns>An instance of <see cref="Saml2SubjectConfirmation"/> .</returns>
5098         protected virtual Saml2SubjectConfirmation ReadSubjectConfirmation(XmlReader reader)
5099         {
5100             if (null == reader)
5101             {
5102                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
5103             }
5104
5105             // throw if wrong element
5106             if (!reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace))
5107             {
5108                 reader.ReadStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace);
5109             }
5110
5111             try
5112             {
5113                 bool isEmpty = reader.IsEmptyElement;
5114
5115                 // @attributes
5116
5117                 // @xsi:type
5118                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.SubjectConfirmationType, Saml2Constants.Namespace);
5119
5120                 // @Method - required
5121                 string method = reader.GetAttribute(Saml2Constants.Attributes.Method);
5122                 if (string.IsNullOrEmpty(method))
5123                 {
5124                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Method, Saml2Constants.Elements.SubjectConfirmation));
5125                 }
5126
5127                 if (!UriUtil.CanCreateValidUri(method, UriKind.Absolute))
5128                 {
5129                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Method, Saml2Constants.Elements.SubjectConfirmation));
5130                 }
5131
5132                 // Construct the appropriate SubjectConfirmation based on the method
5133                 Saml2SubjectConfirmation subjectConfirmation = new Saml2SubjectConfirmation(new Uri(method));
5134
5135                 // <elements>
5136                 reader.Read();
5137                 if (!isEmpty)
5138                 {
5139                     // <NameID> | <EncryptedID> | <BaseID> 0-1
5140                     subjectConfirmation.NameIdentifier = this.ReadSubjectId(reader, Saml2Constants.Elements.SubjectConfirmation);
5141
5142                     // <SubjectConfirmationData> 0-1
5143                     if (reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace))
5144                     {
5145                         subjectConfirmation.SubjectConfirmationData = this.ReadSubjectConfirmationData(reader);
5146                     }
5147
5148                     reader.ReadEndElement();
5149                 }
5150
5151                 return subjectConfirmation;
5152             }
5153             catch (Exception e)
5154             {
5155                 if (System.Runtime.Fx.IsFatal(e))
5156                     throw;
5157                 
5158                 Exception wrapped = TryWrapReadException(reader, e);
5159                 if (null == wrapped)
5160                 {
5161                     throw;
5162                 }
5163                 else
5164                 {
5165                     throw wrapped;
5166                 }
5167             }
5168         }
5169
5170         /// <summary>
5171         /// Writes the &lt;saml:SubjectConfirmation> element.
5172         /// </summary>
5173         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectConfirmation"/>.</param>
5174         /// <param name="data">The <see cref="Saml2SubjectConfirmation"/> to serialize.</param>
5175         protected virtual void WriteSubjectConfirmation(XmlWriter writer, Saml2SubjectConfirmation data)
5176         {
5177             if (null == writer)
5178             {
5179                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5180             }
5181
5182             if (null == data)
5183             {
5184                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5185             }
5186
5187             if (null == data.Method)
5188             {
5189                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data.Method");
5190             }
5191
5192             if (string.IsNullOrEmpty(data.Method.ToString()))
5193             {
5194                 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("data.Method");
5195             }
5196
5197             // <SubjectConfirmation>
5198             writer.WriteStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace);
5199
5200             // @Method - required
5201             writer.WriteAttributeString(Saml2Constants.Attributes.Method, data.Method.AbsoluteUri);
5202
5203             // <NameID> 0-1
5204             if (null != data.NameIdentifier)
5205             {
5206                 this.WriteNameId(writer, data.NameIdentifier);
5207             }
5208
5209             // <SubjectConfirmationData> 0-1
5210             if (null != data.SubjectConfirmationData)
5211             {
5212                 this.WriteSubjectConfirmationData(writer, data.SubjectConfirmationData);
5213             }
5214
5215             // </SubjectConfirmation>
5216             writer.WriteEndElement();
5217         }
5218
5219         /// <summary>
5220         /// Reads the &lt;saml:SubjectConfirmationData> element.
5221         /// </summary>
5222         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectConfirmationData"/> element.</param>
5223         /// <returns>An instance of <see cref="Saml2SubjectConfirmationData"/> .</returns>
5224         /// <remarks>
5225         /// The default implementation handles the unextended element 
5226         /// as well as the extended type saml:KeyInfoConfirmationDataType.
5227         /// </remarks>
5228         protected virtual Saml2SubjectConfirmationData ReadSubjectConfirmationData(XmlReader reader)
5229         {
5230             if (null == reader)
5231             {
5232                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
5233             }
5234
5235             if (!reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace))
5236             {
5237                 reader.ReadStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace);
5238             }
5239
5240             try
5241             {
5242                 Saml2SubjectConfirmationData confirmationData = new Saml2SubjectConfirmationData();
5243                 bool isEmpty = reader.IsEmptyElement;
5244
5245                 // @attributes
5246                 string value;
5247
5248                 // @xsi:type
5249                 bool requireKeyInfo = false;
5250                 XmlQualifiedName type = XmlUtil.GetXsiType(reader);
5251
5252                 if (null != type)
5253                 {
5254                     if (XmlUtil.EqualsQName(type, Saml2Constants.Types.KeyInfoConfirmationDataType, Saml2Constants.Namespace))
5255                     {
5256                         requireKeyInfo = true;
5257                     }
5258                     else if (!XmlUtil.EqualsQName(type, Saml2Constants.Types.SubjectConfirmationDataType, Saml2Constants.Namespace))
5259                     {
5260                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4112, type.Name, type.Namespace));
5261                     }
5262                 }
5263
5264                 // KeyInfoConfirmationData cannot be empty
5265                 if (requireKeyInfo && isEmpty)
5266                 {
5267                     throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.GetString(SR.ID4111)));
5268                 }
5269
5270                 // @Address - optional
5271                 value = reader.GetAttribute(Saml2Constants.Attributes.Address);
5272                 if (!string.IsNullOrEmpty(value))
5273                 {
5274                     confirmationData.Address = value;
5275                 }
5276
5277                 // @InResponseTo - optional
5278                 value = reader.GetAttribute(Saml2Constants.Attributes.InResponseTo);
5279                 if (!string.IsNullOrEmpty(value))
5280                 {
5281                     confirmationData.InResponseTo = new Saml2Id(value);
5282                 }
5283
5284                 // @NotBefore - optional
5285                 value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore);
5286                 if (!string.IsNullOrEmpty(value))
5287                 {
5288                     confirmationData.NotBefore = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
5289                 }
5290
5291                 // @NotOnOrAfter - optional
5292                 value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter);
5293                 if (!string.IsNullOrEmpty(value))
5294                 {
5295                     confirmationData.NotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
5296                 }
5297
5298                 // @Recipient - optional
5299                 value = reader.GetAttribute(Saml2Constants.Attributes.Recipient);
5300                 if (!string.IsNullOrEmpty(value))
5301                 {
5302                     if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
5303                     {
5304                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Recipient, Saml2Constants.Elements.SubjectConfirmationData));
5305                     }
5306
5307                     confirmationData.Recipient = new Uri(value);
5308                 }
5309
5310                 // Contents
5311                 reader.Read();
5312
5313                 if (!isEmpty)
5314                 {
5315                     // <ds:KeyInfo> 0-OO OR 1-OO
5316                     if (requireKeyInfo)
5317                     {
5318                         confirmationData.KeyIdentifiers.Add(this.ReadSubjectKeyInfo(reader));
5319                     }
5320
5321                     while (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace))
5322                     {
5323                         confirmationData.KeyIdentifiers.Add(this.ReadSubjectKeyInfo(reader));
5324                     }
5325
5326                     // If this isn't KeyInfo restricted, there might be open content here ...
5327                     if (!requireKeyInfo && XmlNodeType.EndElement != reader.NodeType)
5328                     {
5329                         // So throw and tell the user how to handle the open content
5330                         throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4114, Saml2Constants.Elements.SubjectConfirmationData));
5331                     }
5332
5333                     reader.ReadEndElement();
5334                 }
5335
5336                 return confirmationData;
5337             }
5338             catch (Exception e)
5339             {
5340                 if (System.Runtime.Fx.IsFatal(e))
5341                     throw;
5342                 
5343                 Exception wrapped = TryWrapReadException(reader, e);
5344                 if (null == wrapped)
5345                 {
5346                     throw;
5347                 }
5348                 else
5349                 {
5350                     throw wrapped;
5351                 }
5352             }
5353         }
5354
5355         /// <summary>
5356         /// Writes the &lt;saml:SubjectConfirmationData> element.
5357         /// </summary>
5358         /// <remarks>
5359         /// When the data.KeyIdentifiers collection is not empty, an xsi:type
5360         /// attribute will be written specifying saml:KeyInfoConfirmationDataType.
5361         /// </remarks>
5362         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectConfirmationData"/>.</param>
5363         /// <param name="data">The <see cref="Saml2SubjectConfirmationData"/> to serialize.</param>
5364         protected virtual void WriteSubjectConfirmationData(XmlWriter writer, Saml2SubjectConfirmationData data)
5365         {
5366             if (null == writer)
5367             {
5368                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5369             }
5370
5371             if (null == data)
5372             {
5373                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5374             }
5375
5376             // <SubjectConfirmationData>
5377             writer.WriteStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace);
5378
5379             // @attributes
5380
5381             // @xsi:type
5382             if (data.KeyIdentifiers != null && data.KeyIdentifiers.Count > 0)
5383             {
5384                 writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, Saml2Constants.Types.KeyInfoConfirmationDataType);
5385             }
5386
5387             // @Address - optional
5388             if (!string.IsNullOrEmpty(data.Address))
5389             {
5390                 writer.WriteAttributeString(Saml2Constants.Attributes.Address, data.Address);
5391             }
5392
5393             // @InResponseTo - optional
5394             if (null != data.InResponseTo)
5395             {
5396                 writer.WriteAttributeString(Saml2Constants.Attributes.InResponseTo, data.InResponseTo.Value);
5397             }
5398
5399             // @NotBefore - optional
5400             if (null != data.NotBefore)
5401             {
5402                 writer.WriteAttributeString(Saml2Constants.Attributes.NotBefore, XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated));
5403             }
5404
5405             // @NotOnOrAfter - optional
5406             if (null != data.NotOnOrAfter)
5407             {
5408                 writer.WriteAttributeString(Saml2Constants.Attributes.NotOnOrAfter, XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
5409             }
5410
5411             // @Recipient - optional
5412             if (null != data.Recipient)
5413             {
5414                 writer.WriteAttributeString(Saml2Constants.Attributes.Recipient, data.Recipient.OriginalString);
5415             }
5416
5417             // Content
5418
5419             // <ds:KeyInfo> 0-OO
5420             foreach (SecurityKeyIdentifier keyIdentifier in data.KeyIdentifiers)
5421             {
5422                 this.WriteSubjectKeyInfo(writer, keyIdentifier);
5423             }
5424
5425             // </SubjectConfirmationData>
5426             writer.WriteEndElement();
5427         }
5428
5429         /// <summary>
5430         /// Reads the &lt;saml:SubjectLocality> element.
5431         /// </summary>
5432         /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectLocality"/> element.</param>
5433         /// <returns>An instance of <see cref="Saml2SubjectLocality"/> .</returns>
5434         protected virtual Saml2SubjectLocality ReadSubjectLocality(XmlReader reader)
5435         {
5436             if (null == reader)
5437             {
5438                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
5439             }
5440
5441             // throw if wrong element
5442             if (!reader.IsStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace))
5443             {
5444                 reader.ReadStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace);
5445             }
5446
5447             try
5448             {
5449                 Saml2SubjectLocality subjectLocality = new Saml2SubjectLocality();
5450                 bool isEmpty = reader.IsEmptyElement;
5451
5452                 // @attributes
5453
5454                 // @xsi:type
5455                 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.SubjectLocalityType, Saml2Constants.Namespace);
5456
5457                 // @Address - optional
5458                 subjectLocality.Address = reader.GetAttribute(Saml2Constants.Attributes.Address);
5459
5460                 // @DNSName - optional
5461                 subjectLocality.DnsName = reader.GetAttribute(Saml2Constants.Attributes.DNSName);
5462
5463                 // Empty content
5464                 reader.Read();
5465                 if (!isEmpty)
5466                 {
5467                     reader.ReadEndElement();
5468                 }
5469
5470                 return subjectLocality;
5471             }
5472             catch (Exception e)
5473             {
5474                 if (System.Runtime.Fx.IsFatal(e))
5475                     throw;
5476                 
5477                 Exception wrapped = TryWrapReadException(reader, e);
5478                 if (null == wrapped)
5479                 {
5480                     throw;
5481                 }
5482                 else
5483                 {
5484                     throw wrapped;
5485                 }
5486             }
5487         }
5488
5489         /// <summary>
5490         /// Writes the &lt;saml:SubjectLocality> element.
5491         /// </summary>
5492         /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectLocality"/>.</param>
5493         /// <param name="data">The <see cref="Saml2SubjectLocality"/> to serialize.</param>
5494         protected virtual void WriteSubjectLocality(XmlWriter writer, Saml2SubjectLocality data)
5495         {
5496             if (null == writer)
5497             {
5498                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5499             }
5500
5501             if (null == data)
5502             {
5503                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5504             }
5505
5506             // <SubjectLocality>
5507             writer.WriteStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace);
5508
5509             // @Address - optional
5510             if (null != data.Address)
5511             {
5512                 writer.WriteAttributeString(Saml2Constants.Attributes.Address, data.Address);
5513             }
5514
5515             // @DNSName - optional
5516             if (null != data.DnsName)
5517             {
5518                 writer.WriteAttributeString(Saml2Constants.Attributes.DNSName, data.DnsName);
5519             }
5520
5521             // </SubjectLocality>
5522             writer.WriteEndElement();
5523         }
5524
5525         // This thin wrapper is used to pass a serializer down into the 
5526         // EnvelpoedSignatureReader that will use the Saml2AssertionSerializer's
5527         // ReadKeyInfo method to read the KeyInfo.
5528         internal class WrappedSerializer : SecurityTokenSerializer
5529         {
5530             private Saml2SecurityTokenHandler parent;
5531             private Saml2Assertion assertion;
5532
5533             public WrappedSerializer(Saml2SecurityTokenHandler parent, Saml2Assertion assertion)
5534             {
5535                 this.assertion = assertion;
5536                 this.parent = parent;
5537             }
5538
5539             protected override bool CanReadKeyIdentifierClauseCore(XmlReader reader)
5540             {
5541                 return false;
5542             }
5543
5544             protected override bool CanReadKeyIdentifierCore(XmlReader reader)
5545             {
5546                 return true;
5547             }
5548
5549             protected override bool CanReadTokenCore(XmlReader reader)
5550             {
5551                 return false;
5552             }
5553
5554             protected override bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause)
5555             {
5556                 return false;
5557             }
5558
5559             protected override bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier)
5560             {
5561                 return false;
5562             }
5563
5564             protected override bool CanWriteTokenCore(SecurityToken token)
5565             {
5566                 return false;
5567             }
5568
5569             protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
5570             {
5571                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5572             }
5573
5574             protected override SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader)
5575             {
5576                 return this.parent.ReadSigningKeyInfo(reader, this.assertion);
5577             }
5578
5579             protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
5580             {
5581                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5582             }
5583
5584             /// <summary>
5585             /// Extensibility point for providing custom serialization.
5586             /// </summary>
5587             /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifierClause"/>.</param>
5588             /// <param name="keyIdentifierClause">The <see cref="SecurityKeyIdentifierClause"/> to serialize.</param>
5589             /// <remarks>This is not supported.</remarks>
5590             protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
5591             {
5592                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5593             }
5594
5595             /// <summary>
5596             /// Extensibility point for providing custom serialization.
5597             /// </summary>
5598             /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifier"/>.</param>
5599             /// <param name="keyIdentifier">The <see cref="SecurityKeyIdentifier"/> to serialize.</param>
5600             protected override void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier)
5601             {
5602                 this.parent.WriteSigningKeyInfo(writer, keyIdentifier);
5603             }
5604
5605             /// <summary>
5606             /// Extensibility point for providing custom serialization.
5607             /// </summary>
5608             /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityToken"/>.</param>
5609             /// <param name="token">The <see cref="SecurityToken"/> to serialize.</param>
5610             protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
5611             {
5612                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5613             }
5614         }
5615
5616         /// <summary>
5617         /// When encrypted SAML 2.0 token is received, the credentials that are used
5618         /// to encrypt the token will be hydrated as a ReceivedEncryptingCredentials.
5619         /// This is to distinguish the case between a user explicitly setting an 
5620         /// encrypting credentials and a re-serialize case where a received token
5621         /// is re-serialized by a proxy to a backend service, in which case the token 
5622         /// should not be encrypted.
5623         /// </summary>
5624         internal class ReceivedEncryptingCredentials : EncryptingCredentials
5625         {
5626             /// <summary>
5627             /// Constructs an ReceivedEncryptingCredentials with a security key, a security key identifier and
5628             /// the encryption algorithm.
5629             /// </summary>
5630             /// <param name="key">A security key for encryption.</param>
5631             /// <param name="keyIdentifier">A security key identifier for the encryption key.</param>
5632             /// <param name="algorithm">The encryption algorithm.</param>
5633             public ReceivedEncryptingCredentials(SecurityKey key, SecurityKeyIdentifier keyIdentifier, string algorithm)
5634                 : base(key, keyIdentifier, algorithm)
5635             {
5636             }
5637         }
5638     }
5639 }