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