1 //-----------------------------------------------------------------------
2 // <copyright file="Saml2SecurityTokenHandler.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace System.IdentityModel.Tokens
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Diagnostics;
13 using System.Globalization;
14 using System.IdentityModel.Configuration;
15 using System.IdentityModel.Diagnostics;
16 using System.IdentityModel.Protocols.WSTrust;
17 using System.IdentityModel.Selectors;
21 using System.Security.Claims;
22 using System.Security.Cryptography;
23 using System.Security.Principal;
26 using System.Xml.Schema;
27 using Claim = System.Security.Claims.Claim;
28 using SAML2 = System.IdentityModel.Tokens.Saml2Constants;
29 using WSC = System.IdentityModel.WSSecureConversationFeb2005Constants;
30 using WSC13 = System.IdentityModel.WSSecureConversation13Constants;
31 using WSSE = System.IdentityModel.WSSecurity10Constants;
32 using WSSE11 = System.IdentityModel.WSSecurity11Constants;
35 /// Creates SAML2 assertion-based security tokens
37 public class Saml2SecurityTokenHandler : SecurityTokenHandler
40 /// The key identifier value type for SAML 2.0 assertion IDs, as defined
41 /// by the OASIS Web Services Security SAML Token Profile 1.1.
43 public const string TokenProfile11ValueType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID";
44 private const string Actor = "Actor";
45 private const string Attribute = "Attribute";
46 private static string[] tokenTypeIdentifiers = new string[] { SecurityTokenTypes.Saml2TokenProfile11, SecurityTokenTypes.OasisWssSaml2TokenProfile11 };
47 private SamlSecurityTokenRequirement samlSecurityTokenRequirement;
48 private SecurityTokenSerializer keyInfoSerializer;
50 const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
51 object syncObject = new object();
54 /// Creates an instance of <see cref="Saml2SecurityTokenHandler"/>
56 public Saml2SecurityTokenHandler()
57 : this(new SamlSecurityTokenRequirement())
62 /// Creates an instance of <see cref="Saml2SecurityTokenHandler"/>
64 /// <param name="samlSecurityTokenRequirement">The SamlSecurityTokenRequirement to be used by the Saml2SecurityTokenHandler instance when validating tokens.</param>
65 public Saml2SecurityTokenHandler(SamlSecurityTokenRequirement samlSecurityTokenRequirement)
67 if (samlSecurityTokenRequirement == null)
69 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlSecurityTokenRequirement");
72 this.samlSecurityTokenRequirement = samlSecurityTokenRequirement;
76 /// Load custom configuration from Xml
78 /// <param name="customConfigElements">SAML token authentication requirements.</param>
79 /// <exception cref="ArgumentNullException">Input parameter 'customConfigElements' is null.</exception>
80 /// <exception cref="InvalidOperationException">Custom configuration specified was invalid.</exception>
81 public override void LoadCustomConfiguration(XmlNodeList customConfigElements)
83 if (customConfigElements == null)
85 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfigElements");
88 List<XmlElement> configNodes = XmlUtil.GetXmlElements(customConfigElements);
90 bool foundValidConfig = false;
92 foreach (XmlElement configElement in configNodes)
94 if (configElement.LocalName != ConfigurationStrings.SamlSecurityTokenRequirement)
101 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7026, ConfigurationStrings.SamlSecurityTokenRequirement));
104 this.samlSecurityTokenRequirement = new SamlSecurityTokenRequirement(configElement);
106 foundValidConfig = true;
109 if (!foundValidConfig)
111 this.samlSecurityTokenRequirement = new SamlSecurityTokenRequirement();
116 /// Returns a value that indicates if this handler can validate <see cref="SecurityToken"/>.
118 /// <returns>'True', indicating this instance can validate <see cref="SecurityToken"/>.</returns>
119 public override bool CanValidateToken
125 /// Gets the token type supported by this handler.
127 public override Type TokenType
129 get { return typeof(Saml2SecurityToken); }
133 /// Gets or sets the <see cref="X509CertificateValidator"/> that is used by the current instance to validate
134 /// certificates that have signed the <see cref="Saml2SecurityToken"/>.
136 public X509CertificateValidator CertificateValidator
140 if (this.samlSecurityTokenRequirement.CertificateValidator == null)
142 if (Configuration != null)
144 return Configuration.CertificateValidator;
153 return this.samlSecurityTokenRequirement.CertificateValidator;
159 this.samlSecurityTokenRequirement.CertificateValidator = value;
164 /// Gets or sets a <see cref="SecurityTokenSerializer"/> that will be used to serialize and deserialize
165 /// a <see cref="SecurityKeyIdentifier"/>. For example, SamlSubject SecurityKeyIdentifier or Signature
166 /// SecurityKeyIdentifier.
168 public SecurityTokenSerializer KeyInfoSerializer
172 if ( this.keyInfoSerializer == null )
174 lock ( this.syncObject )
176 if ( this.keyInfoSerializer == null )
178 SecurityTokenHandlerCollection sthc = ( ContainingCollection != null ) ?
179 ContainingCollection : SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
180 this.keyInfoSerializer = new SecurityTokenSerializerAdapter(sthc);
185 return this.keyInfoSerializer;
192 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
195 this.keyInfoSerializer = value;
200 /// Gets the value if this instance can write a token.
202 public override bool CanWriteToken
208 /// Gets or sets the <see cref="SamlSecurityTokenRequirement"/>.
210 public SamlSecurityTokenRequirement SamlSecurityTokenRequirement
214 return this.samlSecurityTokenRequirement;
221 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
224 this.samlSecurityTokenRequirement = value;
229 /// Creates a <see cref="SecurityKeyIdentifierClause"/> to be used as the security token reference when the token is not attached to the message.
231 /// <param name="token">The saml token.</param>
232 /// <param name="attached">Boolean that indicates if a attached or unattached
233 /// reference needs to be created.</param>
234 /// <returns>A <see cref="SamlAssertionKeyIdentifierClause"/> instance.</returns>
235 public override SecurityKeyIdentifierClause CreateSecurityTokenReference(SecurityToken token, bool attached)
239 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
242 return token.CreateKeyIdentifierClause<Saml2AssertionKeyIdentifierClause>();
246 /// Creates a <see cref="SecurityToken"/> based on a information contained in the <see cref="SecurityTokenDescriptor"/>.
248 /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that has creation information.</param>
249 /// <returns>A <see cref="SecurityToken"/> instance.</returns>
250 /// <exception cref="ArgumentNullException">Thrown if 'tokenDescriptor' is null.</exception>
251 public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor)
253 if (null == tokenDescriptor)
255 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
259 Saml2Assertion assertion = new Saml2Assertion(this.CreateIssuerNameIdentifier(tokenDescriptor));
262 assertion.Subject = this.CreateSamlSubject(tokenDescriptor);
265 assertion.SigningCredentials = this.GetSigningCredentials(tokenDescriptor);
268 assertion.Conditions = this.CreateConditions(tokenDescriptor.Lifetime, tokenDescriptor.AppliesToAddress, tokenDescriptor);
271 assertion.Advice = this.CreateAdvice(tokenDescriptor);
274 IEnumerable<Saml2Statement> statements = this.CreateStatements(tokenDescriptor);
275 if (null != statements)
277 foreach (Saml2Statement statement in statements)
279 assertion.Statements.Add(statement);
283 // encrypting credentials
284 assertion.EncryptingCredentials = this.GetEncryptingCredentials(tokenDescriptor);
286 SecurityToken token = new Saml2SecurityToken(assertion);
292 /// Gets the token type identifier(s) supported by this handler.
294 /// <returns>A collection of strings that identify the tokens this instance can handle.</returns>
295 public override string[] GetTokenTypeIdentifiers()
297 return tokenTypeIdentifiers;
301 /// Validates a <see cref="Saml2SecurityToken"/>.
303 /// <param name="token">The <see cref="Saml2SecurityToken"/> to validate.</param>
304 /// <returns>A <see cref="ReadOnlyCollection{T}"/> of <see cref="ClaimsIdentity"/> representing the identities contained in the token.</returns>
305 /// <exception cref="ArgumentNullException">The parameter 'token' is null.</exception>
306 /// <exception cref="ArgumentException">The token is not of assignable from <see cref="Saml2SecurityToken"/>.</exception>
307 /// <exception cref="InvalidOperationException">Configuration <see cref="SecurityTokenHandlerConfiguration"/>is null.</exception>
308 /// <exception cref="SecurityTokenValidationException">Thrown if Saml2SecurityToken.Assertion.IssuerToken is null.</exception>
309 /// <exception cref="SecurityTokenValidationException">Thrown if Saml2SecurityToken.Assertion.SigningToken is null.</exception>
310 /// <exception cref="InvalidOperationException">Saml2SecurityToken.Assertion is null.</exception>
311 public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
315 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
318 Saml2SecurityToken samlToken = token as Saml2SecurityToken;
319 if (samlToken == null)
321 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID4151));
324 if (this.Configuration == null)
326 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
331 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.Diagnostics, SR.GetString(SR.TraceValidateToken), new SecurityTraceRecordHelper.TokenTraceRecord(token), null, null);
333 if (samlToken.IssuerToken == null)
335 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4152)));
338 if (samlToken.Assertion == null)
340 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1034));
343 this.ValidateConditions(
344 samlToken.Assertion.Conditions,
345 SamlSecurityTokenRequirement.ShouldEnforceAudienceRestriction(this.Configuration.AudienceRestriction.AudienceMode, samlToken));
348 // We need something like AudienceUriMode and have a setting on Configuration to allow extensibility and custom settings
349 // By default we only check bearer tokens
351 if (this.Configuration.DetectReplayedTokens)
353 this.DetectReplayedToken(samlToken);
356 Saml2SubjectConfirmation subjectConfirmation = samlToken.Assertion.Subject.SubjectConfirmations[0];
357 if (subjectConfirmation.SubjectConfirmationData != null)
359 this.ValidateConfirmationData(subjectConfirmation.SubjectConfirmationData);
362 // If the backing token is x509, validate trust
363 X509SecurityToken issuerToken = samlToken.IssuerToken as X509SecurityToken;
364 if (issuerToken != null)
366 this.CertificateValidator.Validate(issuerToken.Certificate);
369 ClaimsIdentity claimsIdentity = null;
371 if (this.samlSecurityTokenRequirement.MapToWindows)
373 // TFS: 153865, Microsoft WindowsIdentity does not set Authtype. I don't think that authtype should be set here anyway.
374 // The authtype will be S4U (kerberos) it doesn't really matter that the upn arrived in a SAML token.
375 claimsIdentity = this.CreateWindowsIdentity(this.FindUpn(claimsIdentity));
377 // PARTIAL TRUST: will fail when adding claims, AddClaims is SecurityCritical.
378 claimsIdentity.AddClaims(this.CreateClaims(samlToken).Claims);
382 claimsIdentity = this.CreateClaims(samlToken);
385 if (this.Configuration.SaveBootstrapContext)
387 claimsIdentity.BootstrapContext = new BootstrapContext(token, this);
390 this.TraceTokenValidationSuccess(token);
392 List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1);
393 identities.Add(claimsIdentity);
394 return identities.AsReadOnly();
403 this.TraceTokenValidationFailure(token, e.Message);
409 /// Creates a <see cref="WindowsIdentity"/> object using the <paramref name="upn"/> value.
411 /// <param name="upn">The upn name.</param>
412 /// <returns>A <see cref="WindowsIdentity"/> object.</returns>
413 /// <exception cref="ArgumentException">If <paramref name="upn"/> is null or empty.</exception>
414 protected virtual WindowsIdentity CreateWindowsIdentity(string upn)
416 if (string.IsNullOrEmpty(upn))
418 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("upn");
421 WindowsIdentity wi = new WindowsIdentity(upn);
423 return new WindowsIdentity(wi.Token, AuthenticationTypes.Federation, WindowsAccountType.Normal, true);
427 /// Writes a Saml2 Token using the XmlWriter.
429 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityToken"/>.</param>
430 /// <param name="token">The <see cref="SecurityToken"/> to serialize.</param>
431 /// <exception cref="ArgumentNullException">The input argument 'writer' or 'token' is null.</exception>
432 /// <exception cref="ArgumentException">The input argument 'token' is not a <see cref="Saml2SecurityToken"/>.</exception>
433 public override void WriteToken(XmlWriter writer, SecurityToken token)
437 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
442 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
445 Saml2SecurityToken samlToken = token as Saml2SecurityToken;
447 if (null != samlToken)
449 this.WriteAssertion(writer, samlToken.Assertion);
453 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID4160));
458 /// Indicates whether the current XML element can be read as a token of the type handled by this instance.
460 /// <param name="reader">An <see cref="XmlReader"/> reader positioned at a start element. The reader should not be advanced.</param>
461 /// <returns>'True' if the ReadToken method can read the element.</returns>
462 public override bool CanReadToken(XmlReader reader)
469 return reader.IsStartElement(SAML2.Elements.Assertion, SAML2.Namespace)
470 || reader.IsStartElement(SAML2.Elements.EncryptedAssertion, SAML2.Namespace);
474 /// Indicates if the current XML element is pointing to a KeyIdentifierClause that
475 /// can be serialized by this instance.
477 /// <param name="reader">An <see cref="XmlReader"/> reader positioned at a start element. The reader should not be advanced.</param>
478 /// <returns>'True' if the ReadKeyIdentifierClause can read the element. 'False' otherwise.</returns>
479 public override bool CanReadKeyIdentifierClause(XmlReader reader)
481 return IsSaml2KeyIdentifierClause(reader);
485 /// Indicates if the given SecurityKeyIdentifierClause can be serialized by this
488 /// <param name="securityKeyIdentifierClause">SecurityKeyIdentifierClause to be serialized.</param>
489 /// <returns>"True' if the given SecurityKeyIdentifierClause can be serialized. 'False' otherwise.</returns>
490 public override bool CanWriteKeyIdentifierClause(SecurityKeyIdentifierClause securityKeyIdentifierClause)
492 return (securityKeyIdentifierClause is Saml2AssertionKeyIdentifierClause) ||
493 (securityKeyIdentifierClause is WrappedSaml2AssertionKeyIdentifierClause);
497 /// Reads a SecurityKeyIdentifierClause.
499 /// <param name="reader">A <see cref="XmlReader"/> reader positioned at a <see cref="SecurityKeyIdentifierClause"/> element.</param>
500 /// <returns>A <see cref="SecurityKeyIdentifierClause"/> instance.</returns>
501 /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
502 public override SecurityKeyIdentifierClause ReadKeyIdentifierClause(XmlReader reader)
506 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
509 if (!IsSaml2KeyIdentifierClause(reader))
511 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4161));
515 if (reader.IsEmptyElement)
517 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, WSSE.Elements.SecurityTokenReference, WSSE.Namespace));
528 // @wsse11:TokenType is checked by IsSaml2KeyIdentifierClause
530 // @wsc:Nonce and @wsc:Length, first try WSCFeb2005
531 value = reader.GetAttribute(WSC.Attributes.Nonce, WSC.Namespace);
532 if (!string.IsNullOrEmpty(value))
534 nonce = Convert.FromBase64String(value);
536 value = reader.GetAttribute(WSC.Attributes.Length, WSC.Namespace);
537 if (!string.IsNullOrEmpty(value))
539 length = XmlConvert.ToInt32(value);
543 length = WSC.DefaultDerivedKeyLength;
547 // @wsc:Nonce and @wsc:Length, now try WSC13
550 value = reader.GetAttribute(WSC13.Attributes.Nonce, WSC13.Namespace);
551 if (!string.IsNullOrEmpty(value))
553 nonce = Convert.FromBase64String(value);
555 value = reader.GetAttribute(WSC13.Attributes.Length, WSC13.Namespace);
556 if (!string.IsNullOrEmpty(value))
558 length = XmlConvert.ToInt32(value);
562 length = WSC13.DefaultDerivedKeyLength;
567 // <wsse:SecurityTokenReference> content begins
570 // <wsse:Reference> - throw exception
571 if (reader.IsStartElement(WSSE.Elements.Reference, WSSE.Namespace))
573 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4126));
576 // <wsse:KeyIdentifier>
577 if (!reader.IsStartElement(WSSE.Elements.KeyIdentifier, WSSE.Namespace))
579 reader.ReadStartElement(WSSE.Elements.KeyIdentifier, WSSE.Namespace);
582 // @ValueType - required
583 value = reader.GetAttribute(WSSE.Attributes.ValueType);
584 if (string.IsNullOrEmpty(value))
586 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, WSSE.Attributes.ValueType, WSSE.Elements.KeyIdentifier));
589 if (!StringComparer.Ordinal.Equals(TokenProfile11ValueType, value))
591 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4127, value));
594 // <wsse:KeyIdentifier> Content is string </wsse:KeyIdentifier>
595 id = reader.ReadElementString();
597 // </wsse:SecurityTokenReference>
598 reader.ReadEndElement();
600 return new Saml2AssertionKeyIdentifierClause(id, nonce, length);
602 catch (Exception inner)
604 // Wrap common data-validation exceptions that may have bubbled up
605 if (inner is FormatException
606 || inner is ArgumentException
607 || inner is InvalidOperationException
608 || inner is OverflowException)
610 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4125), inner);
620 /// Reads a SAML 2.0 token from the XmlReader.
622 /// <param name="reader">A <see cref="XmlReader"/> reader positioned at a <see cref="Saml2SecurityToken"/> element.</param>
623 /// <returns>An instance of <see cref="Saml2SecurityToken"/>.</returns>
624 /// <exception cref="InvalidOperationException">Is thrown if 'Configuration', 'Configruation.IssuerTokenResolver' or 'Configuration.ServiceTokenResolver is null.</exception>
625 public override SecurityToken ReadToken(XmlReader reader)
627 if (Configuration == null)
629 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
632 if (Configuration.IssuerTokenResolver == null)
634 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
637 if (Configuration.ServiceTokenResolver == null)
639 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
642 Saml2Assertion assertion = this.ReadAssertion(reader);
644 ReadOnlyCollection<SecurityKey> keys = this.ResolveSecurityKeys(assertion, Configuration.ServiceTokenResolver);
646 // Resolve signing token if one is present. It may be deferred and signed by reference.
647 SecurityToken issuerToken;
648 this.TryResolveIssuerToken(assertion, Configuration.IssuerTokenResolver, out issuerToken);
650 return new Saml2SecurityToken(assertion, keys, issuerToken);
654 /// Serializes a Saml2AssertionKeyIdentifierClause to the XmlWriter.
656 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifierClause"/>.</param>
657 /// <param name="securityKeyIdentifierClause">The <see cref="SecurityKeyIdentifierClause"/> to be serialized.</param>
658 /// <exception cref="ArgumentNullException">Input parameter 'writer' or 'securityKeyIdentifierClause' is null.</exception>
659 public override void WriteKeyIdentifierClause(XmlWriter writer, SecurityKeyIdentifierClause securityKeyIdentifierClause)
663 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
666 if (securityKeyIdentifierClause == null)
668 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyIdentifierClause");
671 Saml2AssertionKeyIdentifierClause samlClause = null;
672 WrappedSaml2AssertionKeyIdentifierClause wrappedClause = securityKeyIdentifierClause as WrappedSaml2AssertionKeyIdentifierClause;
674 if (wrappedClause != null)
676 samlClause = wrappedClause.WrappedClause;
680 samlClause = securityKeyIdentifierClause as Saml2AssertionKeyIdentifierClause;
683 if (null == samlClause)
685 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("keyIdentifierClause", SR.GetString(SR.ID4162));
688 // <wsse:SecurityTokenReference>
689 writer.WriteStartElement(WSSE.Elements.SecurityTokenReference, WSSE.Namespace);
692 byte[] nonce = samlClause.GetDerivationNonce();
695 writer.WriteAttributeString(WSC.Attributes.Nonce, WSC.Namespace, Convert.ToBase64String(nonce));
697 int length = samlClause.DerivationLength;
699 // Don't emit @wsc:Length since it's not actually in the spec/schema
700 if (length != 0 && length != WSC.DefaultDerivedKeyLength)
702 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
703 new InvalidOperationException(SR.GetString(SR.ID4129)));
708 writer.WriteAttributeString(WSSE11.Attributes.TokenType, WSSE11.Namespace, SecurityTokenTypes.OasisWssSaml2TokenProfile11);
710 // <wsse:KeyIdentifier>
711 writer.WriteStartElement(WSSE.Elements.KeyIdentifier, WSSE.Namespace);
714 writer.WriteAttributeString(WSSE.Attributes.ValueType, TokenProfile11ValueType);
716 // ID is the string content
717 writer.WriteString(samlClause.Id);
719 // </wsse:KeyIdentifier>
720 writer.WriteEndElement();
722 // </wsse:SecurityTokenReference>
723 writer.WriteEndElement();
726 internal static XmlDictionaryReader CreatePlaintextReaderFromEncryptedData(
727 XmlDictionaryReader reader,
728 SecurityTokenResolver serviceTokenResolver,
729 SecurityTokenSerializer keyInfoSerializer,
730 Collection<EncryptedKeyIdentifierClause> clauses,
731 out EncryptingCredentials encryptingCredentials)
735 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
738 reader.MoveToContent();
739 if (reader.IsEmptyElement)
741 #pragma warning suppress 56504 // bogus - thinks reader.LocalName, reader.NamespaceURI need validation
742 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
745 encryptingCredentials = null;
747 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.EncryptedElementType, Saml2Constants.Namespace);
749 reader.ReadStartElement();
750 EncryptedDataElement encryptedData = new EncryptedDataElement(keyInfoSerializer);
752 // <xenc:EncryptedData> 1
753 encryptedData.ReadXml(reader);
755 // <xenc:EncryptedKey> 0-oo
756 reader.MoveToContent();
757 while (reader.IsStartElement(XmlEncryptionConstants.Elements.EncryptedKey, XmlEncryptionConstants.Namespace))
759 SecurityKeyIdentifierClause skic;
760 if (keyInfoSerializer.CanReadKeyIdentifierClause(reader))
762 skic = keyInfoSerializer.ReadKeyIdentifierClause(reader);
766 EncryptedKeyElement encryptedKey = new EncryptedKeyElement(keyInfoSerializer);
767 encryptedKey.ReadXml(reader);
768 skic = encryptedKey.GetClause();
771 EncryptedKeyIdentifierClause encryptedKeyClause = skic as EncryptedKeyIdentifierClause;
772 if (null == encryptedKeyClause)
774 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4172));
777 clauses.Add(encryptedKeyClause);
780 reader.ReadEndElement();
782 // Try to resolve the decryption key from both the embedded
783 // KeyInfo and any external clauses
784 SecurityKey decryptionKey = null;
785 SecurityKeyIdentifierClause matchingClause = null;
787 foreach (SecurityKeyIdentifierClause clause in encryptedData.KeyIdentifier)
789 if (serviceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey))
791 matchingClause = clause;
796 if (null == decryptionKey)
798 foreach (SecurityKeyIdentifierClause clause in clauses)
800 if (serviceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey))
802 matchingClause = clause;
808 if (null == decryptionKey)
810 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
811 new EncryptedTokenDecryptionFailedException());
814 // Need a symmetric key
815 SymmetricSecurityKey symmetricKey = decryptionKey as SymmetricSecurityKey;
816 if (null == symmetricKey)
818 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
819 new SecurityTokenException(SR.GetString(SR.ID4023)));
822 // Do the actual decryption
823 SymmetricAlgorithm decryptor = symmetricKey.GetSymmetricAlgorithm(encryptedData.Algorithm);
824 byte[] plainText = encryptedData.Decrypt(decryptor);
826 // Save off the encrypting credentials for roundtrip
827 encryptingCredentials = new ReceivedEncryptingCredentials(decryptionKey, new SecurityKeyIdentifier(matchingClause), encryptedData.Algorithm);
829 return XmlDictionaryReader.CreateTextReader(plainText, reader.Quotas);
832 // Wraps common data validation exceptions with an XmlException
833 // associated with the failing reader
834 internal static Exception TryWrapReadException(XmlReader reader, Exception inner)
836 if (inner is FormatException
837 || inner is ArgumentException
838 || inner is InvalidOperationException
839 || inner is OverflowException)
841 return DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4125), inner);
848 /// Indicates if the current XML element is pointing to a Saml2SecurityKeyIdentifierClause.
850 /// <param name="reader">An <see cref="XmlReader"/> reader.</param>
851 /// <returns>'True' if reader contains a <see cref="Saml2SecurityKeyIdentifierClause"/>. 'False' otherwise.</returns>
852 internal static bool IsSaml2KeyIdentifierClause(XmlReader reader)
854 if (!reader.IsStartElement(WSSE.Elements.SecurityTokenReference, WSSE.Namespace))
859 string tokenType = reader.GetAttribute(WSSE11.Attributes.TokenType, WSSE11.Namespace);
860 return tokenTypeIdentifiers.Contains(tokenType);
864 /// Indicates if the current XML element is pointing to a Saml2Assertion.
866 /// <param name="reader">A reader that may contain a <see cref="Saml2Assertion"/>.</param>
867 /// <returns>'True' if reader contains a <see cref="Saml2Assertion"/>. 'False' otherwise.</returns>
868 internal static bool IsSaml2Assertion(XmlReader reader)
870 return reader.IsStartElement(SAML2.Elements.Assertion, SAML2.Namespace)
871 || reader.IsStartElement(SAML2.Elements.EncryptedAssertion, SAML2.Namespace);
874 // Read an element that must not contain content.
875 internal static void ReadEmptyContentElement(XmlReader reader)
877 bool isEmpty = reader.IsEmptyElement;
881 reader.ReadEndElement();
885 internal static Saml2Id ReadSimpleNCNameElement(XmlReader reader)
887 Fx.Assert(reader.IsStartElement(), "reader is not on start element");
891 if (reader.IsEmptyElement)
893 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
896 XmlUtil.ValidateXsiType(reader, "NCName", XmlSchema.Namespace);
898 reader.MoveToElement();
899 string value = reader.ReadElementContentAsString();
901 return new Saml2Id(value);
905 if (System.Runtime.Fx.IsFatal(e))
908 Exception wrapped = TryWrapReadException(reader, e);
920 // Reads an element with simple content anyURI. Since this is SAML,
921 // restricts the URI to absolute.
922 internal static Uri ReadSimpleUriElement(XmlReader reader)
924 return ReadSimpleUriElement(reader, UriKind.Absolute);
927 // Reads an element with simple content anyURI where a UriKind can be specified
928 internal static Uri ReadSimpleUriElement(XmlReader reader, UriKind kind)
930 return ReadSimpleUriElement(reader, kind, false);
933 // allow lax reading of relative URIs in some instances for interop
934 internal static Uri ReadSimpleUriElement(XmlReader reader, UriKind kind, bool allowLaxReading)
936 Fx.Assert(reader.IsStartElement(), "reader is not on start element");
940 if (reader.IsEmptyElement)
942 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
945 XmlUtil.ValidateXsiType(reader, "anyURI", XmlSchema.Namespace);
947 reader.MoveToElement();
948 string value = reader.ReadElementContentAsString();
950 if (string.IsNullOrEmpty(value))
952 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0022));
955 if (!allowLaxReading && !UriUtil.CanCreateValidUri(value, kind))
957 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(kind == UriKind.RelativeOrAbsolute ? SR.ID0019 : SR.ID0013));
960 return new Uri(value, kind);
964 if (System.Runtime.Fx.IsFatal(e))
967 Exception wrapped = TryWrapReadException(reader, e);
980 /// Creates the conditions for the assertion.
984 /// Generally, conditions should be included in assertions to limit the
985 /// impact of misuse of the assertion. Specifying the NotBefore and
986 /// NotOnOrAfter conditions can limit the period of vulnerability in
987 /// the case of a compromised assertion. The AudienceRestrictionCondition
988 /// can be used to explicitly state the intended relying party or parties
989 /// of the assertion, which coupled with appropriate audience restriction
990 /// enforcement at relying parties can help to mitigate spoofing attacks
991 /// between relying parties.
994 /// The default implementation creates NotBefore and NotOnOrAfter conditions
995 /// based on the tokenDescriptor.Lifetime. It will also generate an
996 /// AudienceRestrictionCondition limiting consumption of the assertion to
997 /// tokenDescriptor.Scope.Address.
1000 /// <param name="tokenLifetime">Lifetime of the Token.</param>
1001 /// <param name="relyingPartyAddress">The endpoint address to who the token is created. The address
1002 /// is modeled as an AudienceRestriction condition.</param>
1003 /// <param name="tokenDescriptor">The token descriptor.</param>
1004 /// <returns>A Saml2Conditions object.</returns>
1005 protected virtual Saml2Conditions CreateConditions(Lifetime tokenLifetime, string relyingPartyAddress, SecurityTokenDescriptor tokenDescriptor)
1007 bool hasLifetime = null != tokenLifetime;
1008 bool hasScope = !string.IsNullOrEmpty(relyingPartyAddress);
1009 if (!hasLifetime && !hasScope)
1014 Saml2Conditions conditions = new Saml2Conditions();
1017 conditions.NotBefore = tokenLifetime.Created;
1018 conditions.NotOnOrAfter = tokenLifetime.Expires;
1023 conditions.AudienceRestrictions.Add(new Saml2AudienceRestriction(new Uri(relyingPartyAddress)));
1030 /// Creates the advice for the assertion.
1033 /// By default, this method returns null.
1035 /// <param name="tokenDescriptor">The token descriptor.</param>
1036 /// <returns>A Saml2Advice object, default is null.</returns>
1037 protected virtual Saml2Advice CreateAdvice(SecurityTokenDescriptor tokenDescriptor)
1043 /// Creates a name identifier that identifies the assertion issuer.
1047 /// SAML2 assertions must contain a name identifier for the issuer.
1048 /// This method may not return null.
1051 /// The default implementation creates a simple name identifier
1052 /// from the tokenDescriptor.Issuer.
1055 /// <param name="tokenDescriptor">The token descriptor.</param>
1056 /// <returns>A <see cref="Saml2NameIdentifier"/> from the tokenDescriptor</returns>
1057 protected virtual Saml2NameIdentifier CreateIssuerNameIdentifier(SecurityTokenDescriptor tokenDescriptor)
1059 if (null == tokenDescriptor)
1061 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1064 string issuerName = tokenDescriptor.TokenIssuerName;
1066 // Must have an issuer
1067 if (string.IsNullOrEmpty(issuerName))
1069 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4138)));
1072 return new Saml2NameIdentifier(issuerName);
1076 /// Generates a Saml2Attribute from a claim.
1078 /// <param name="claim">The <see cref="Claim"/> from which to generate a <see cref="Saml2Attribute"/>.</param>
1079 /// <param name="tokenDescriptor">Contains all the information that is used in token issuance.</param>
1080 /// <returns>A <see cref="Saml2Attribute"/> based on the claim.</returns>
1081 /// <exception cref="ArgumentNullException">The parameter 'claim' is null.</exception>
1082 protected virtual Saml2Attribute CreateAttribute(Claim claim, SecurityTokenDescriptor tokenDescriptor)
1086 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim");
1089 Saml2Attribute attribute = new Saml2Attribute(claim.Type, claim.Value);
1090 if (!StringComparer.Ordinal.Equals(ClaimsIdentity.DefaultIssuer, claim.OriginalIssuer))
1092 attribute.OriginalIssuer = claim.OriginalIssuer;
1095 attribute.AttributeValueXsiType = claim.ValueType;
1097 if (claim.Properties.ContainsKey(ClaimProperties.SamlAttributeNameFormat))
1099 string nameFormat = claim.Properties[ClaimProperties.SamlAttributeNameFormat];
1100 if (!UriUtil.CanCreateValidUri(nameFormat, UriKind.Absolute))
1102 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("nameFormat", SR.GetString(SR.ID0013));
1105 attribute.NameFormat = new Uri(nameFormat);
1108 if (claim.Properties.ContainsKey(ClaimProperties.SamlAttributeDisplayName))
1110 attribute.FriendlyName = claim.Properties[ClaimProperties.SamlAttributeDisplayName];
1117 /// Creates <see cref="Saml2AttributeStatement"/> from a <see cref="SecurityTokenDescriptor"/> and a <see cref="ClaimsIdentity"/>
1119 /// <remarks>This method may return null if the token descriptor does not contain any subject or the subject does not have any claims.
1121 /// <param name="subject">The <see cref="ClaimsIdentity"/> that contains claims which will be converted to SAML Attributes.</param>
1122 /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that contains information on building the <see cref="Saml2AttributeStatement"/>.</param>
1123 /// <returns>A Saml2AttributeStatement.</returns>
1124 protected virtual Saml2AttributeStatement CreateAttributeStatement(ClaimsIdentity subject, SecurityTokenDescriptor tokenDescriptor)
1126 if (subject == null)
1131 // We treat everything else as an Attribute except the nameId claim, which is already processed
1133 // AuthenticationInstant and AuthenticationType are not converted to Claims
1134 if (subject.Claims != null)
1136 List<Saml2Attribute> attributes = new List<Saml2Attribute>();
1137 foreach (Claim claim in subject.Claims)
1139 if (claim != null && claim.Type != ClaimTypes.NameIdentifier)
1143 case ClaimTypes.AuthenticationInstant:
1144 case ClaimTypes.AuthenticationMethod:
1147 attributes.Add(this.CreateAttribute(claim, tokenDescriptor));
1153 this.AddDelegateToAttributes(subject, attributes, tokenDescriptor);
1155 ICollection<Saml2Attribute> collectedAttributes = this.CollectAttributeValues(attributes);
1156 if (collectedAttributes.Count > 0)
1158 return new Saml2AttributeStatement(collectedAttributes);
1166 /// Collects attributes with a common claim type, claim value type, and original issuer into a
1167 /// single attribute with multiple values.
1169 /// <param name="attributes">List of attributes generated from claims.</param>
1170 /// <returns>A <see cref="ICollection{T}"/> of <see cref="Saml2Attribute"/> with common attributes collected into value lists.</returns>
1171 protected virtual ICollection<Saml2Attribute> CollectAttributeValues(ICollection<Saml2Attribute> attributes)
1173 Dictionary<SamlAttributeKeyComparer.AttributeKey, Saml2Attribute> distinctAttributes = new Dictionary<SamlAttributeKeyComparer.AttributeKey, Saml2Attribute>(attributes.Count, new SamlAttributeKeyComparer());
1175 // Use unique attribute if name, value type, or issuer differ
1176 foreach (Saml2Attribute saml2Attribute in attributes)
1178 if (saml2Attribute != null)
1180 SamlAttributeKeyComparer.AttributeKey attributeKey = new SamlAttributeKeyComparer.AttributeKey(saml2Attribute);
1182 if (distinctAttributes.ContainsKey(attributeKey))
1184 foreach (string attributeValue in saml2Attribute.Values)
1186 distinctAttributes[attributeKey].Values.Add(attributeValue);
1191 distinctAttributes.Add(attributeKey, saml2Attribute);
1196 return distinctAttributes.Values;
1200 /// Adds all the delegates associated with the subject into the attribute collection.
1202 /// <param name="subject">The delegate of this <see cref="ClaimsIdentity"/> will be serialized into a <see cref="Saml2Attribute"/>.</param>
1203 /// <param name="attributes">A <see cref="ICollection{T}"/> of <see cref="Saml2Attribute"/>.</param>
1204 /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that contains information on building the delegate.</param>
1205 protected virtual void AddDelegateToAttributes(ClaimsIdentity subject, ICollection<Saml2Attribute> attributes, SecurityTokenDescriptor tokenDescriptor)
1207 if (subject == null)
1209 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1212 if (tokenDescriptor == null)
1214 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1217 if (subject.Actor == null)
1222 List<Saml2Attribute> actingAsAttributes = new List<Saml2Attribute>();
1224 foreach (Claim claim in subject.Actor.Claims)
1228 actingAsAttributes.Add(this.CreateAttribute(claim, tokenDescriptor));
1232 this.AddDelegateToAttributes(subject.Actor, actingAsAttributes, tokenDescriptor);
1234 ICollection<Saml2Attribute> collectedAttributes = this.CollectAttributeValues(actingAsAttributes);
1235 attributes.Add(this.CreateAttribute(new Claim(ClaimTypes.Actor, this.CreateXmlStringFromAttributes(collectedAttributes), ClaimValueTypes.String), tokenDescriptor));
1239 /// Builds an XML formatted string from a collection of SAML attributes that represent the Actor.
1241 /// <param name="attributes">An enumeration of Saml2Attributes.</param>
1242 /// <returns>A well-formed XML string.</returns>
1243 /// <remarks>The string is of the form ""<Actor><Attribute name, ns><AttributeValue>...</AttributeValue>, ...</Attribute>...</Actor>"</remarks>
1244 protected virtual string CreateXmlStringFromAttributes(IEnumerable<Saml2Attribute> attributes)
1246 bool actorElementWritten = false;
1248 using (MemoryStream ms = new MemoryStream())
1250 using (XmlDictionaryWriter dicWriter = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8, false))
1252 foreach (Saml2Attribute samlAttribute in attributes)
1254 if (samlAttribute != null)
1256 if (!actorElementWritten)
1258 dicWriter.WriteStartElement(Actor);
1259 actorElementWritten = true;
1262 this.WriteAttribute(dicWriter, samlAttribute);
1266 if (actorElementWritten)
1268 dicWriter.WriteEndElement();
1274 return Encoding.UTF8.GetString(ms.ToArray());
1279 /// Creates an <see cref="IEnumerable{T}"/> of <see cref="Saml2Statement"/> to be included in the assertion.
1283 /// Statements are not required in a SAML2 assertion. This method may
1284 /// return an empty collection.
1287 /// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> that contains information on creating the <see cref="Saml2Statement"/>.</param>
1288 /// <returns>An enumeration of Saml2Statements.</returns>
1289 protected virtual IEnumerable<Saml2Statement> CreateStatements(SecurityTokenDescriptor tokenDescriptor)
1291 if (tokenDescriptor == null)
1293 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1296 Collection<Saml2Statement> statements = new Collection<Saml2Statement>();
1298 Saml2AttributeStatement attributeStatement = this.CreateAttributeStatement(tokenDescriptor.Subject, tokenDescriptor);
1299 if (attributeStatement != null)
1301 statements.Add(attributeStatement);
1304 Saml2AuthenticationStatement authenticationStatement = this.CreateAuthenticationStatement(tokenDescriptor.AuthenticationInfo, tokenDescriptor);
1305 if (authenticationStatement != null)
1307 statements.Add(authenticationStatement);
1314 /// Given an AuthenticationInformation object, this routine creates a Saml2AuthenticationStatement
1315 /// to be added to the Saml2Assertion that is produced by the factory.
1317 /// <param name="authInfo">
1318 /// An AuthenticationInformation object containing the state to be wrapped as a Saml2AuthenticationStatement
1321 /// <param name="tokenDescriptor">The token descriptor.</param>
1323 /// The Saml2AuthenticationStatement to add to the assertion being created or null to ignore the AuthenticationInformation
1324 /// being wrapped as a statement.
1326 protected virtual Saml2AuthenticationStatement CreateAuthenticationStatement(AuthenticationInformation authInfo, SecurityTokenDescriptor tokenDescriptor)
1328 if (tokenDescriptor == null)
1330 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1333 if (tokenDescriptor.Subject == null)
1338 string authenticationMethod = null;
1339 string authenticationInstant = null;
1341 // Search for an Authentication Claim.
1342 IEnumerable<Claim> claimCollection = from c in tokenDescriptor.Subject.Claims where c.Type == ClaimTypes.AuthenticationMethod select c;
1343 if (claimCollection.Count<Claim>() > 0)
1345 // We support only one authentication statement and hence we just pick the first authentication type
1346 // claim found in the claim collection. Since the spec allows multiple Auth Statements,
1347 // we do not throw an error.
1348 authenticationMethod = claimCollection.First<Claim>().Value;
1351 claimCollection = from c in tokenDescriptor.Subject.Claims where c.Type == ClaimTypes.AuthenticationInstant select c;
1353 if (claimCollection.Count<Claim>() > 0)
1355 authenticationInstant = claimCollection.First<Claim>().Value;
1358 if (authenticationMethod == null && authenticationInstant == null)
1362 else if (authenticationMethod == null)
1364 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationMethod", "SAML2"));
1366 else if (authenticationInstant == null)
1368 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4270, "AuthenticationInstant", "SAML2"));
1371 Uri saml2AuthenticationClass;
1372 if (!UriUtil.TryCreateValidUri(this.DenormalizeAuthenticationType(authenticationMethod), UriKind.Absolute, out saml2AuthenticationClass))
1374 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4185, authenticationMethod));
1377 Saml2AuthenticationContext authCtx = new Saml2AuthenticationContext(saml2AuthenticationClass);
1378 DateTime authInstantTime = DateTime.ParseExact(authenticationInstant, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime();
1379 Saml2AuthenticationStatement authnStatement = new Saml2AuthenticationStatement(authCtx, authInstantTime);
1381 if (authInfo != null)
1383 if (!string.IsNullOrEmpty(authInfo.DnsName)
1384 || !string.IsNullOrEmpty(authInfo.Address))
1386 authnStatement.SubjectLocality
1387 = new Saml2SubjectLocality(authInfo.Address, authInfo.DnsName);
1390 if (!string.IsNullOrEmpty(authInfo.Session))
1392 authnStatement.SessionIndex = authInfo.Session;
1395 authnStatement.SessionNotOnOrAfter = authInfo.NotOnOrAfter;
1398 return authnStatement;
1402 /// Creates a SAML2 subject of the assertion.
1404 /// <param name="tokenDescriptor">The security token descriptor to create the subject.</param>
1405 /// <exception cref="ArgumentNullException">Thrown when 'tokenDescriptor' is null.</exception>
1406 /// <returns>A Saml2Subject.</returns>
1407 protected virtual Saml2Subject CreateSamlSubject(SecurityTokenDescriptor tokenDescriptor)
1409 if (null == tokenDescriptor)
1411 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1414 Saml2Subject saml2Subject = new Saml2Subject();
1416 // Look for name identifier claims
1417 string nameIdentifierClaim = null;
1418 string nameIdentifierFormat = null;
1419 string nameIdentifierNameQualifier = null;
1420 string nameIdentifierSpProviderId = null;
1421 string nameIdentifierSpNameQualifier = null;
1423 if (tokenDescriptor.Subject != null && tokenDescriptor.Subject.Claims != null)
1425 foreach (Claim claim in tokenDescriptor.Subject.Claims)
1427 if (claim.Type == ClaimTypes.NameIdentifier)
1429 // Do not allow multiple name identifier claim.
1430 if (null != nameIdentifierClaim)
1432 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4139)));
1435 nameIdentifierClaim = claim.Value;
1437 if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierFormat))
1439 nameIdentifierFormat = claim.Properties[ClaimProperties.SamlNameIdentifierFormat];
1442 if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierNameQualifier))
1444 nameIdentifierNameQualifier = claim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier];
1447 if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierSPNameQualifier))
1449 nameIdentifierSpNameQualifier = claim.Properties[ClaimProperties.SamlNameIdentifierSPNameQualifier];
1452 if (claim.Properties.ContainsKey(ClaimProperties.SamlNameIdentifierSPProvidedId))
1454 nameIdentifierSpProviderId = claim.Properties[ClaimProperties.SamlNameIdentifierSPProvidedId];
1460 if (nameIdentifierClaim != null)
1462 Saml2NameIdentifier nameIdentifier = new Saml2NameIdentifier(nameIdentifierClaim);
1464 if (nameIdentifierFormat != null && UriUtil.CanCreateValidUri(nameIdentifierFormat, UriKind.Absolute))
1466 nameIdentifier.Format = new Uri(nameIdentifierFormat);
1469 nameIdentifier.NameQualifier = nameIdentifierNameQualifier;
1470 nameIdentifier.SPNameQualifier = nameIdentifierSpNameQualifier;
1471 nameIdentifier.SPProvidedId = nameIdentifierSpProviderId;
1473 saml2Subject.NameId = nameIdentifier;
1476 // Add subject confirmation data
1477 Saml2SubjectConfirmation subjectConfirmation;
1478 if (null == tokenDescriptor.Proof)
1480 subjectConfirmation = new Saml2SubjectConfirmation(Saml2Constants.ConfirmationMethods.Bearer);
1484 subjectConfirmation = new Saml2SubjectConfirmation(Saml2Constants.ConfirmationMethods.HolderOfKey, new Saml2SubjectConfirmationData());
1485 subjectConfirmation.SubjectConfirmationData.KeyIdentifiers.Add(tokenDescriptor.Proof.KeyIdentifier);
1488 saml2Subject.SubjectConfirmations.Add(subjectConfirmation);
1490 return saml2Subject;
1494 /// Override this method to change the token encrypting credentials.
1496 /// <param name="tokenDescriptor">Retrieve some scope encrypting credentials from the Scope object</param>
1497 /// <returns>the token encrypting credentials</returns>
1498 /// <exception cref="ArgumentNullException">When the given tokenDescriptor is null</exception>
1499 protected virtual EncryptingCredentials GetEncryptingCredentials(SecurityTokenDescriptor tokenDescriptor)
1501 if (null == tokenDescriptor)
1503 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1506 EncryptingCredentials encryptingCredentials = null;
1508 if (null != tokenDescriptor.EncryptingCredentials)
1510 encryptingCredentials = tokenDescriptor.EncryptingCredentials;
1511 if (encryptingCredentials.SecurityKey is AsymmetricSecurityKey)
1513 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4178)));
1517 return encryptingCredentials;
1521 /// Gets the credentials for the signing the assertion.
1525 /// SAML2 assertions used as security tokens should be signed.
1528 /// The default implementation uses the
1529 /// tokenDescriptor.Scope.SigningCredentials.
1532 /// <param name="tokenDescriptor">The token descriptor.</param>
1533 /// <returns>The signing credential.</returns>
1534 protected virtual SigningCredentials GetSigningCredentials(SecurityTokenDescriptor tokenDescriptor)
1536 if (null == tokenDescriptor)
1538 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
1541 return tokenDescriptor.SigningCredentials;
1545 /// Rejects tokens that are not valid.
1548 /// The token may not be valid for a number of reasons. For example, the
1549 /// current time may not be within the token's validity period, the
1550 /// token may contain data that is contradictory or not valid, or the token
1551 /// may contain unsupported SAML2 elements.
1553 /// <param name="conditions">SAML 2.0 condition to be validated.</param>
1554 /// <param name="enforceAudienceRestriction">True to check for Audience Restriction condition.</param>
1555 protected virtual void ValidateConditions(Saml2Conditions conditions, bool enforceAudienceRestriction)
1557 if (conditions != null)
1559 DateTime now = DateTime.UtcNow;
1561 if (conditions.NotBefore != null
1562 && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < conditions.NotBefore.Value)
1564 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenNotYetValidException(SR.GetString(SR.ID4147, conditions.NotBefore.Value, now)));
1567 if (conditions.NotOnOrAfter != null
1568 && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= conditions.NotOnOrAfter.Value)
1570 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenExpiredException(SR.GetString(SR.ID4148, conditions.NotOnOrAfter.Value, now)));
1573 if (conditions.OneTimeUse)
1575 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4149)));
1578 if (conditions.ProxyRestriction != null)
1580 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4150)));
1584 if (enforceAudienceRestriction)
1586 if (this.Configuration == null || this.Configuration.AudienceRestriction.AllowedAudienceUris.Count == 0)
1588 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID1032)));
1591 if (conditions == null || conditions.AudienceRestrictions.Count == 0)
1593 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException(SR.GetString(SR.ID1035)));
1597 foreach (Saml2AudienceRestriction audienceRestriction in conditions.AudienceRestrictions)
1599 SamlSecurityTokenRequirement.ValidateAudienceRestriction(this.Configuration.AudienceRestriction.AllowedAudienceUris, audienceRestriction.Audiences);
1606 /// Finds the UPN claim value in the provided <see cref="ClaimsIdentity" /> object for the purpose
1607 /// of mapping the identity to a <see cref="WindowsIdentity" /> object.
1609 /// <param name="claimsIdentity">The claims identity object containing the desired UPN claim.</param>
1610 /// <returns>The UPN claim value found.</returns>
1611 protected virtual string FindUpn(ClaimsIdentity claimsIdentity)
1613 return ClaimsHelper.FindUpn(claimsIdentity);
1617 /// Returns the Saml2 AuthenticationContext matching a normalized value.
1619 /// <param name="normalizedAuthenticationType">Normalized value.</param>
1620 /// <returns>A string that represents the denormalized authentication type used to obtain the token.</returns>
1621 protected virtual string DenormalizeAuthenticationType(string normalizedAuthenticationType)
1623 return AuthenticationTypeMaps.Denormalize(normalizedAuthenticationType, AuthenticationTypeMaps.Saml2);
1627 /// Throws if a token is detected as being replayed. If the token is not found, it is added to the
1628 /// <see cref="TokenReplayCache" />.
1630 /// <param name="token">The token to detect for replay.</param>
1631 /// <exception cref="ArgumentNullException">The input argument 'token' is null.</exception>
1632 /// <exception cref="InvalidOperationException">Configuration or Configuration.TokenReplayCache property is null.</exception>
1633 /// <exception cref="ArgumentException">The input argument 'token' can not be cast as a 'Saml2SecurityToken'.</exception>
1634 /// <exception cref="SecurityTokenValidationException">The Saml2SecurityToken.Assertion.Id.Value is null or empty.</exception>
1635 /// <exception cref="SecurityTokenReplayDetectedException">The token is found in the <see cref="TokenReplayCache" />.</exception>
1636 /// <remarks>The default behavior is to only check tokens bearer tokens (tokens that do not have keys).</remarks>
1637 protected override void DetectReplayedToken(SecurityToken token)
1641 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
1644 Saml2SecurityToken samlToken = token as Saml2SecurityToken;
1645 if (null == samlToken)
1647 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID1064, token.GetType().ToString()));
1650 // by default we only check bearer tokens.
1651 if (samlToken.SecurityKeys.Count != 0)
1656 if (Configuration == null)
1658 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
1661 if (Configuration.Caches.TokenReplayCache == null)
1663 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4278));
1666 if (string.IsNullOrEmpty(samlToken.Assertion.Id.Value))
1668 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID1065)));
1671 StringBuilder stringBuilder = new StringBuilder();
1674 using (HashAlgorithm hashAlgorithm = CryptoHelper.NewSha256HashAlgorithm())
1676 if (string.IsNullOrEmpty(samlToken.Assertion.Issuer.Value))
1678 stringBuilder.AppendFormat("{0}{1}", samlToken.Assertion.Id.Value, tokenTypeIdentifiers[0]);
1682 stringBuilder.AppendFormat("{0}{1}{2}", samlToken.Assertion.Id.Value, samlToken.Assertion.Issuer.Value, tokenTypeIdentifiers[0]);
1685 key = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString())));
1688 if (Configuration.Caches.TokenReplayCache.Contains(key))
1690 string issuer = (samlToken.Assertion.Issuer.Value != null) ? samlToken.Assertion.Issuer.Value : String.Empty;
1692 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenReplayDetectedException(SR.GetString(SR.ID1066, typeof(Saml2SecurityToken).ToString(), samlToken.Assertion.Id.Value, issuer)));
1696 Configuration.Caches.TokenReplayCache.AddOrUpdate(key, token, DateTimeUtil.Add(this.GetTokenReplayCacheEntryExpirationTime(samlToken), Configuration.MaxClockSkew));
1701 /// Returns the time until which the token should be held in the token replay cache.
1703 /// <param name="token">The token to return an expiration time for.</param>
1704 /// <exception cref="ArgumentNullException">The input argument 'token' is null.</exception>
1705 /// <exception cref="SecurityTokenValidationException">The Saml2SecurityToken's validity period is greater than the expiration period set to TokenReplayCache.</exception>
1706 /// <returns>A DateTime representing the expiration time.</returns>
1707 /// <remarks>By default, this function returns the NotOnOrAfter of the SAML Condition if present.
1708 /// If that value does not exist, it returns the NotOnOrAfter of the first SubjectConfirmationData.
1709 /// This function will never return a value further from now than Configuration.TokenReplayCacheExpirationPeriod.</remarks>
1710 protected virtual DateTime GetTokenReplayCacheEntryExpirationTime(Saml2SecurityToken token)
1714 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
1717 DateTime? tokenExpiration = null;
1718 Saml2Assertion assertion = token.Assertion;
1719 if (assertion != null)
1721 if (assertion.Conditions != null && assertion.Conditions.NotOnOrAfter.HasValue)
1723 // The Condition has a NotOnOrAfter set, use that.
1724 tokenExpiration = assertion.Conditions.NotOnOrAfter.Value;
1726 else if (assertion.Subject != null && assertion.Subject.SubjectConfirmations != null &&
1727 assertion.Subject.SubjectConfirmations.Count != 0 &&
1728 assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData != null &&
1729 assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData.NotOnOrAfter.HasValue)
1731 // The condition did not have NotOnOrAfter set, but SCD[0] has a NotOnOrAfter set, use that.
1732 tokenExpiration = assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData.NotOnOrAfter.Value;
1736 // DateTimeUtil handles overflows
1737 DateTime maximumExpirationTime = DateTimeUtil.Add(DateTime.UtcNow, Configuration.TokenReplayCacheExpirationPeriod);
1739 // Use DateTime.MaxValue as expiration value for tokens without expiration
1740 tokenExpiration = tokenExpiration ?? DateTime.MaxValue;
1742 // If the refined token validity period is greater than the TokenReplayCacheExpirationPeriod, throw
1743 if (DateTime.Compare(maximumExpirationTime, tokenExpiration.Value) < 0)
1745 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1746 new SecurityTokenValidationException(SR.GetString(SR.ID1069, tokenExpiration.Value.ToString(), Configuration.TokenReplayCacheExpirationPeriod.ToString())));
1749 return tokenExpiration.Value;
1753 /// Returns the normalized value matching a SAML2 AuthenticationContext class reference.
1755 /// <param name="saml2AuthenticationContextClassReference">A string representing the <see cref="Saml2Constants.AuthenticationContextClasses"/></param>
1756 /// <returns>Normalized value.</returns>
1757 protected virtual string NormalizeAuthenticationContextClassReference(string saml2AuthenticationContextClassReference)
1759 return AuthenticationTypeMaps.Normalize(saml2AuthenticationContextClassReference, AuthenticationTypeMaps.Saml2);
1763 /// Creates claims from the Saml2Subject.
1765 /// <param name="assertionSubject">The Saml2Subject.</param>
1766 /// <param name="subject">The ClaimsIdentity subject.</param>
1767 /// <param name="issuer">The issuer.</param>
1768 protected virtual void ProcessSamlSubject(Saml2Subject assertionSubject, ClaimsIdentity subject, string issuer)
1770 if (assertionSubject == null)
1772 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertionSubject");
1775 if (subject == null)
1777 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1780 Saml2NameIdentifier nameId = assertionSubject.NameId;
1784 Claim claim = new Claim(ClaimTypes.NameIdentifier, nameId.Value, ClaimValueTypes.String, issuer);
1786 if (nameId.Format != null)
1788 claim.Properties[ClaimProperties.SamlNameIdentifierFormat] = nameId.Format.AbsoluteUri;
1791 if (nameId.NameQualifier != null)
1793 claim.Properties[ClaimProperties.SamlNameIdentifierNameQualifier] = nameId.NameQualifier;
1796 if (nameId.SPNameQualifier != null)
1798 claim.Properties[ClaimProperties.SamlNameIdentifierSPNameQualifier] = nameId.SPNameQualifier;
1801 if (nameId.SPProvidedId != null)
1803 claim.Properties[ClaimProperties.SamlNameIdentifierSPProvidedId] = nameId.SPProvidedId;
1806 subject.AddClaim(claim);
1811 /// Creates claims from a Saml2AttributeStatement.
1813 /// <param name="statement">The Saml2AttributeStatement.</param>
1814 /// <param name="subject">The subject.</param>
1815 /// <param name="issuer">The issuer.</param>
1816 protected virtual void ProcessAttributeStatement(Saml2AttributeStatement statement, ClaimsIdentity subject, string issuer)
1818 if (statement == null)
1820 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("statement");
1823 if (subject == null)
1825 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1828 foreach (Saml2Attribute attribute in statement.Attributes)
1830 if (StringComparer.Ordinal.Equals(attribute.Name, ClaimTypes.Actor))
1832 if (subject.Actor != null)
1834 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4218));
1837 this.SetDelegateFromAttribute(attribute, subject, issuer);
1841 foreach (string value in attribute.Values)
1845 string originalIssuer = issuer;
1846 if (attribute.OriginalIssuer != null)
1848 originalIssuer = attribute.OriginalIssuer;
1851 Claim claim = new Claim(attribute.Name, value, attribute.AttributeValueXsiType, issuer, originalIssuer);
1853 if (attribute.NameFormat != null)
1855 claim.Properties[ClaimProperties.SamlAttributeNameFormat] = attribute.NameFormat.AbsoluteUri;
1858 if (attribute.FriendlyName != null)
1860 claim.Properties[ClaimProperties.SamlAttributeDisplayName] = attribute.FriendlyName;
1863 subject.AddClaim(claim);
1871 /// This method gets called when a special type of Saml2Attribute is detected. The Saml2Attribute passed in
1872 /// wraps a Saml2Attribute that contains a collection of AttributeValues, each of which will get mapped to a
1873 /// claim. All of the claims will be returned in an ClaimsIdentity with the specified issuer.
1875 /// <param name="attribute">The <see cref="Saml2Attribute"/> to use.</param>
1876 /// <param name="subject">The <see cref="ClaimsIdentity"/> that is the subject of this token.</param>
1877 /// <param name="issuer">The issuer of the claim.</param>
1878 /// <exception cref="InvalidOperationException">Will be thrown if the Saml2Attribute does not contain any
1879 /// valid Saml2AttributeValues.
1881 protected virtual void SetDelegateFromAttribute(Saml2Attribute attribute, ClaimsIdentity subject, string issuer)
1883 // bail here; nothing to add.
1884 if (subject == null || attribute == null || attribute.Values == null || attribute.Values.Count < 1)
1889 Saml2Attribute actingAsAttribute = null;
1890 Collection<Claim> claims = new Collection<Claim>();
1892 foreach (string attributeValue in attribute.Values)
1894 if (attributeValue != null)
1896 using (XmlDictionaryReader dicReader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(attributeValue), XmlDictionaryReaderQuotas.Max))
1898 dicReader.MoveToContent();
1899 dicReader.ReadStartElement(Actor);
1901 while (dicReader.IsStartElement(Attribute))
1903 Saml2Attribute innerAttribute = this.ReadAttribute(dicReader);
1904 if (innerAttribute != null)
1906 if (innerAttribute.Name == ClaimTypes.Actor)
1908 // In this case, we have two delegates acting as an identity: we do not allow this.
1909 if (actingAsAttribute != null)
1911 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4218));
1914 actingAsAttribute = innerAttribute;
1918 string originalIssuer = innerAttribute.OriginalIssuer;
1919 for (int k = 0; k < innerAttribute.Values.Count; ++k)
1922 if (string.IsNullOrEmpty(originalIssuer))
1924 claim = new Claim(innerAttribute.Name, innerAttribute.Values[k], innerAttribute.AttributeValueXsiType, issuer);
1928 claim = new Claim(innerAttribute.Name, innerAttribute.Values[k], innerAttribute.AttributeValueXsiType, issuer, originalIssuer);
1931 if (innerAttribute.NameFormat != null)
1933 claim.Properties[ClaimProperties.SamlAttributeNameFormat] = innerAttribute.NameFormat.AbsoluteUri;
1936 if (innerAttribute.FriendlyName != null)
1938 claim.Properties[ClaimProperties.SamlAttributeDisplayName] = innerAttribute.FriendlyName;
1947 dicReader.ReadEndElement(); // Actor
1952 subject.Actor = new ClaimsIdentity(claims, AuthenticationTypes.Federation);
1954 this.SetDelegateFromAttribute(actingAsAttribute, subject.Actor, issuer);
1958 /// Creates claims from a Saml2AuthenticationStatement.
1960 /// <param name="statement">The Saml2AuthenticationStatement.</param>
1961 /// <param name="subject">The subject.</param>
1962 /// <param name="issuer">The issuer.</param>
1963 protected virtual void ProcessAuthenticationStatement(Saml2AuthenticationStatement statement, ClaimsIdentity subject, string issuer)
1965 if (subject == null)
1967 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subject");
1970 if (statement.AuthenticationContext.DeclarationReference != null)
1972 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4180));
1975 if (statement.AuthenticationContext.ClassReference != null)
1979 ClaimTypes.AuthenticationMethod,
1980 this.NormalizeAuthenticationContextClassReference(statement.AuthenticationContext.ClassReference.AbsoluteUri),
1981 ClaimValueTypes.String,
1985 subject.AddClaim(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(statement.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated), ClaimValueTypes.DateTime, issuer));
1989 /// Creates claims from a Saml2AuthorizationDecisionStatement.
1991 /// <param name="statement">The Saml2AuthorizationDecisionStatement.</param>
1992 /// <param name="subject">The subject.</param>
1993 /// <param name="issuer">The issuer.</param>
1994 protected virtual void ProcessAuthorizationDecisionStatement(Saml2AuthorizationDecisionStatement statement, ClaimsIdentity subject, string issuer)
1999 /// Processes all statements to generate claims.
2001 /// <param name="statements">A collection of Saml2Statement.</param>
2002 /// <param name="subject">The subject.</param>
2003 /// <param name="issuer">The issuer.</param>
2004 protected virtual void ProcessStatement(Collection<Saml2Statement> statements, ClaimsIdentity subject, string issuer)
2006 Collection<Saml2AuthenticationStatement> authnStatements = new Collection<Saml2AuthenticationStatement>();
2008 foreach (Saml2Statement statement in statements)
2010 Saml2AttributeStatement attrStatement = statement as Saml2AttributeStatement;
2011 if (attrStatement != null)
2013 this.ProcessAttributeStatement(attrStatement, subject, issuer);
2017 Saml2AuthenticationStatement authnStatement = statement as Saml2AuthenticationStatement;
2018 if (authnStatement != null)
2020 authnStatements.Add(authnStatement);
2024 Saml2AuthorizationDecisionStatement authzStatement = statement as Saml2AuthorizationDecisionStatement;
2025 if (authzStatement != null)
2027 this.ProcessAuthorizationDecisionStatement(authzStatement, subject, issuer);
2031 // We don't process custom statements. Just fall through.
2034 foreach (Saml2AuthenticationStatement authStatement in authnStatements)
2036 if (authStatement != null)
2038 this.ProcessAuthenticationStatement(authStatement, subject, issuer);
2044 /// Creates claims from a Saml2 token.
2046 /// <param name="samlToken">The Saml2SecurityToken.</param>
2047 /// <returns>An IClaimIdentity.</returns>
2048 protected virtual ClaimsIdentity CreateClaims(Saml2SecurityToken samlToken)
2050 if (samlToken == null)
2052 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("samlToken");
2055 ClaimsIdentity subject = new ClaimsIdentity(AuthenticationTypes.Federation, SamlSecurityTokenRequirement.NameClaimType, SamlSecurityTokenRequirement.RoleClaimType);
2057 Saml2Assertion assertion = samlToken.Assertion;
2059 if (assertion == null)
2061 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("samlToken", SR.GetString(SR.ID1034));
2064 if (this.Configuration == null)
2066 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
2069 if (this.Configuration.IssuerNameRegistry == null)
2071 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4277));
2074 string issuer = this.Configuration.IssuerNameRegistry.GetIssuerName(samlToken.IssuerToken, assertion.Issuer.Value);
2076 if (string.IsNullOrEmpty(issuer))
2078 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4175)));
2081 this.ProcessSamlSubject(assertion.Subject, subject, issuer);
2082 this.ProcessStatement(assertion.Statements, subject, issuer);
2088 /// Validates the Saml2SubjectConfirmation data.
2090 /// <param name="confirmationData">The Saml2 subject confirmation data.</param>
2091 protected virtual void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData)
2093 if (null == confirmationData)
2095 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("confirmationData");
2098 if (null != confirmationData.Address)
2100 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4153)));
2103 if (null != confirmationData.InResponseTo)
2105 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4154)));
2108 if (null != confirmationData.Recipient)
2110 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4157)));
2113 DateTime now = DateTime.UtcNow;
2115 if (null != confirmationData.NotBefore
2116 && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < confirmationData.NotBefore.Value)
2118 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4176, confirmationData.NotBefore.Value, now)));
2121 if (null != confirmationData.NotOnOrAfter
2122 && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= confirmationData.NotOnOrAfter.Value)
2124 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4177, confirmationData.NotOnOrAfter.Value, now)));
2129 /// Resolves the collection of <see cref="SecurityKey"/> referenced in a <see cref="Saml2Assertion"/>.
2131 /// <param name="assertion"><see cref="Saml2Assertion"/> to process.</param>
2132 /// <param name="resolver"><see cref="SecurityTokenResolver"/> to use in resolving the <see cref="SecurityKey"/>.</param>
2133 /// <returns>A read only collection of <see cref="SecurityKey"/> contained in the assertion.</returns>
2134 protected virtual ReadOnlyCollection<SecurityKey> ResolveSecurityKeys(Saml2Assertion assertion, SecurityTokenResolver resolver)
2136 if (null == assertion)
2138 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
2141 // Must have Subject
2142 Saml2Subject subject = assertion.Subject;
2143 if (null == subject)
2146 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4130)));
2149 // Must have one SubjectConfirmation
2150 if (0 == subject.SubjectConfirmations.Count)
2152 // No SubjectConfirmation
2153 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4131)));
2156 if (subject.SubjectConfirmations.Count > 1)
2158 // More than one SubjectConfirmation
2159 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4132)));
2162 // Extract the keys for the given method
2163 ReadOnlyCollection<SecurityKey> securityKeys;
2165 Saml2SubjectConfirmation subjectConfirmation = subject.SubjectConfirmations[0];
2167 // For bearer, ensure there are no keys, set the collection to empty
2168 // For HolderOfKey, ensure there is at least one key, resolve and create collection
2169 if (Saml2Constants.ConfirmationMethods.Bearer == subjectConfirmation.Method)
2171 if (null != subjectConfirmation.SubjectConfirmationData
2172 && 0 != subjectConfirmation.SubjectConfirmationData.KeyIdentifiers.Count)
2174 // Bearer but has keys
2175 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4133)));
2178 securityKeys = EmptyReadOnlyCollection<SecurityKey>.Instance;
2180 else if (Saml2Constants.ConfirmationMethods.HolderOfKey == subjectConfirmation.Method)
2182 if (null == subjectConfirmation.SubjectConfirmationData
2183 || 0 == subjectConfirmation.SubjectConfirmationData.KeyIdentifiers.Count)
2185 // Holder-of-key but no keys
2186 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4134)));
2189 List<SecurityKey> holderKeys = new List<SecurityKey>();
2192 foreach (SecurityKeyIdentifier keyIdentifier in subjectConfirmation.SubjectConfirmationData.KeyIdentifiers)
2196 // Try the resolver first
2197 foreach (SecurityKeyIdentifierClause clause in keyIdentifier)
2199 if (null != resolver
2200 && resolver.TryResolveSecurityKey(clause, out key))
2202 holderKeys.Add(key);
2207 // If that doesn't work, try to create the key (e.g. bare RSA or X509 raw)
2210 if (keyIdentifier.CanCreateKey)
2212 key = keyIdentifier.CreateKey();
2213 holderKeys.Add(key);
2217 holderKeys.Add(new SecurityKeyElement(keyIdentifier, resolver));
2222 securityKeys = holderKeys.AsReadOnly();
2226 // SenderVouches, as well as other random things, aren't accepted
2227 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4136, subjectConfirmation.Method)));
2230 return securityKeys;
2234 /// Resolves the Signing Key Identifier to a SecurityToken.
2236 /// <param name="assertion">The Assertion for which the Issuer token is to be resolved.</param>
2237 /// <param name="issuerResolver">The current SecurityTokenResolver associated with this handler.</param>
2238 /// <returns>Instance of SecurityToken</returns>
2239 /// <exception cref="ArgumentNullException">Input parameter 'assertion' is null.</exception>
2240 /// <exception cref="SecurityTokenException">Unable to resolve token.</exception>
2241 protected virtual SecurityToken ResolveIssuerToken(Saml2Assertion assertion, SecurityTokenResolver issuerResolver)
2243 if (null == assertion)
2245 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
2248 SecurityToken token;
2249 if (this.TryResolveIssuerToken(assertion, issuerResolver, out token))
2255 string exceptionMessage = SR.GetString(assertion.SigningCredentials == null ? SR.ID4141 : SR.ID4142);
2256 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(exceptionMessage));
2261 /// Resolves the Signing Key Identifier to a SecurityToken.
2263 /// <param name="assertion">The Assertion for which the Issuer token is to be resolved.</param>
2264 /// <param name="issuerResolver">The current SecurityTokenResolver associated with this handler.</param>
2265 /// <param name="token">Resolved token.</param>
2266 /// <returns>True if token is resolved.</returns>
2267 protected virtual bool TryResolveIssuerToken(Saml2Assertion assertion, SecurityTokenResolver issuerResolver, out SecurityToken token)
2269 if (null == assertion)
2271 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("assertion");
2274 if (assertion.SigningCredentials != null
2275 && assertion.SigningCredentials.SigningKeyIdentifier != null
2276 && issuerResolver != null)
2278 SecurityKeyIdentifier keyIdentifier = assertion.SigningCredentials.SigningKeyIdentifier;
2279 return issuerResolver.TryResolveToken(keyIdentifier, out token);
2289 /// This handles the construct used in <Subject> and <SubjectConfirmation> for ID:
2291 /// <element ref="saml:BaseID" />
2292 /// <element ref="saml:NameID" />
2293 /// <element ref="saml:EncryptedID" />
2296 /// <param name="reader">A <see cref="XmlReader"/> reader positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
2297 /// <param name="parentElement">The parent element this SubjectID is part of.</param>
2298 /// <returns>A <see cref="Saml2NameIdentifier"/> constructed from the XML.</returns>
2299 protected virtual Saml2NameIdentifier ReadSubjectId(XmlReader reader, string parentElement)
2301 // <NameID>, <EncryptedID>, <BaseID>
2302 if (reader.IsStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace))
2304 return this.ReadNameId(reader);
2306 else if (reader.IsStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace))
2308 return this.ReadEncryptedId(reader);
2310 else if (reader.IsStartElement(Saml2Constants.Elements.BaseID, Saml2Constants.Namespace))
2312 // Since BaseID is an abstract type, we have to switch off the xsi:type declaration
2313 XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader);
2315 // No declaration, or declaring that this is just a "BaseID", is invalid since
2316 // statement is abstract
2317 if (null == declaredType
2318 || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.BaseIDAbstractType, Saml2Constants.Namespace))
2320 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI));
2323 // If it's NameID we can handle it
2324 if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.NameIDType, Saml2Constants.Namespace))
2326 return this.ReadNameIdType(reader);
2330 // Instruct the user to override to handle custom <BaseID>
2331 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4110, parentElement, declaredType.Name, declaredType.Namespace));
2339 /// Reads the <saml:Action> element.
2341 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Action"/> element.</param>
2342 /// <returns>A <see cref="Saml2Action"/> instance.</returns>
2343 protected virtual Saml2Action ReadAction(XmlReader reader)
2347 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2350 // throw if wrong element
2351 if (!reader.IsStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace))
2353 reader.ReadStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace);
2357 if (reader.IsEmptyElement)
2359 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.Action, Saml2Constants.Namespace));
2364 // Need the content to instantiate, so use locals
2365 Uri actionNamespace;
2368 string attributeValue;
2371 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ActionType, Saml2Constants.Namespace);
2373 // @Namespace - required
2374 attributeValue = reader.GetAttribute(Saml2Constants.Attributes.Namespace);
2375 if (string.IsNullOrEmpty(attributeValue))
2377 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
2380 if (!UriUtil.CanCreateValidUri(attributeValue, UriKind.Absolute))
2382 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
2385 actionNamespace = new Uri(attributeValue);
2387 // Content is string
2388 return new Saml2Action(reader.ReadElementString(), actionNamespace);
2392 if (System.Runtime.Fx.IsFatal(e))
2395 Exception wrapped = TryWrapReadException(reader, e);
2396 if (null == wrapped)
2408 /// Writes the <saml:Action> element.
2410 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Action"/>.</param>
2411 /// <param name="data">The <see cref="Saml2Action"/> to serialize.</param>
2412 protected virtual void WriteAction(XmlWriter writer, Saml2Action data)
2416 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2421 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
2424 if (null == data.Namespace)
2426 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data.Namespace");
2429 if (string.IsNullOrEmpty(data.Namespace.ToString()))
2431 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("data.Namespace");
2435 writer.WriteStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace);
2437 // @Namespace - required
2438 writer.WriteAttributeString(Saml2Constants.Attributes.Namespace, data.Namespace.AbsoluteUri);
2441 writer.WriteString(data.Value);
2444 writer.WriteEndElement();
2448 /// Reads the <saml:Advice> element.
2452 /// The Advice element has an extensibility point to allow XML elements
2453 /// from non-SAML2 namespaces to be included. By default, because the
2454 /// Advice may be ignored without affecting the semantics of the
2455 /// assertion, any such elements are ignored. To handle the processing
2456 /// of those elements, override this method.
2459 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Advice"/> element.</param>
2460 /// <returns>A <see cref="Saml2Advice"/> instance.</returns>
2461 protected virtual Saml2Advice ReadAdvice(XmlReader reader)
2465 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2468 // throw if wrong element
2469 if (!reader.IsStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace))
2471 reader.ReadStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace);
2476 Saml2Advice advice = new Saml2Advice();
2477 bool isEmpty = reader.IsEmptyElement;
2482 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AdviceType, Saml2Constants.Namespace);
2487 // <AssertionIDRef|AssertionURIRef|Assertion|EncryptedAssertion|other:any> 0-OO
2488 while (reader.IsStartElement())
2490 // <AssertionIDRef>, <AssertionURIRef>, <Assertion>, <EncryptedAssertion>
2491 if (reader.IsStartElement(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace))
2493 advice.AssertionIdReferences.Add(ReadSimpleNCNameElement(reader));
2495 else if (reader.IsStartElement(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace))
2497 advice.AssertionUriReferences.Add(ReadSimpleUriElement(reader));
2499 else if (reader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace))
2501 advice.Assertions.Add(this.ReadAssertion(reader));
2503 else if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace))
2505 advice.Assertions.Add(this.ReadAssertion(reader));
2509 TraceUtility.TraceString(TraceEventType.Warning, SR.GetString(SR.ID8006, reader.LocalName, reader.NamespaceURI));
2514 reader.ReadEndElement();
2521 if (System.Runtime.Fx.IsFatal(e))
2524 Exception wrapped = TryWrapReadException(reader, e);
2525 if (null == wrapped)
2537 /// Writes the <saml:Advice> element.
2539 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Advice"/>.</param>
2540 /// <param name="data">The <see cref="Saml2Advice"/> to serialize.</param>
2541 protected virtual void WriteAdvice(XmlWriter writer, Saml2Advice data)
2545 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2550 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
2554 writer.WriteStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace);
2556 // <AssertionIDRef> 0-OO
2557 foreach (Saml2Id id in data.AssertionIdReferences)
2559 writer.WriteElementString(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace, id.Value);
2562 // <AssertionURIRef> 0-OO
2563 foreach (Uri uri in data.AssertionUriReferences)
2565 writer.WriteElementString(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace, uri.AbsoluteUri);
2569 foreach (Saml2Assertion assertion in data.Assertions)
2571 this.WriteAssertion(writer, assertion);
2575 writer.WriteEndElement();
2579 /// Reads the <saml:Assertion> element.
2581 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Assertion"/> element.</param>
2582 /// <returns>A <see cref="Saml2Assertion"/> instance.</returns>
2583 protected virtual Saml2Assertion ReadAssertion(XmlReader reader)
2587 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2590 if (this.Configuration == null)
2592 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
2595 if (this.Configuration.IssuerTokenResolver == null)
2597 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275));
2600 if (this.Configuration.ServiceTokenResolver == null)
2602 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
2605 XmlDictionaryReader plaintextReader = XmlDictionaryReader.CreateDictionaryReader(reader);
2607 Saml2Assertion assertion = new Saml2Assertion(new Saml2NameIdentifier("__TemporaryIssuer__"));
2609 // If it's an EncryptedAssertion, we need to retrieve the plaintext
2610 // and repoint our reader
2611 if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace))
2613 EncryptingCredentials encryptingCredentials = null;
2614 plaintextReader = CreatePlaintextReaderFromEncryptedData(
2616 Configuration.ServiceTokenResolver,
2617 this.KeyInfoSerializer,
2618 assertion.ExternalEncryptedKeys,
2619 out encryptingCredentials);
2621 assertion.EncryptingCredentials = encryptingCredentials;
2624 // Throw if wrong element
2625 if (!plaintextReader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace))
2627 plaintextReader.ReadStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace);
2631 if (plaintextReader.IsEmptyElement)
2633 #pragma warning suppress 56504 // bogus - thinks plaintextReader.LocalName, plaintextReader.NamespaceURI need validation
2634 throw DiagnosticUtility.ThrowHelperXml(plaintextReader, SR.GetString(SR.ID3061, plaintextReader.LocalName, plaintextReader.NamespaceURI));
2637 // Construct a wrapped serializer so that the EnvelopedSignatureReader's
2638 // attempt to read the <ds:KeyInfo> will hit our ReadKeyInfo virtual.
2639 WrappedSerializer wrappedSerializer = new WrappedSerializer(this, assertion);
2641 // SAML supports enveloped signature, so we need to wrap our reader.
2642 // We do not dispose this reader, since as a delegating reader it would
2643 // dispose the inner reader, which we don't properly own.
2644 EnvelopedSignatureReader realReader = new EnvelopedSignatureReader(plaintextReader, wrappedSerializer, this.Configuration.IssuerTokenResolver, false, false, false);
2647 // Process @attributes
2651 XmlUtil.ValidateXsiType(realReader, Saml2Constants.Types.AssertionType, Saml2Constants.Namespace);
2653 // @Version - required - must be "2.0"
2654 string version = realReader.GetAttribute(Saml2Constants.Attributes.Version);
2655 if (string.IsNullOrEmpty(version))
2657 throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Version, Saml2Constants.Elements.Assertion));
2660 if (!StringComparer.Ordinal.Equals(assertion.Version, version))
2662 throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID4100, version));
2666 value = realReader.GetAttribute(Saml2Constants.Attributes.ID);
2667 if (string.IsNullOrEmpty(value))
2669 throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.ID, Saml2Constants.Elements.Assertion));
2672 assertion.Id = new Saml2Id(value);
2674 // @IssueInstant - required
2675 value = realReader.GetAttribute(Saml2Constants.Attributes.IssueInstant);
2676 if (string.IsNullOrEmpty(value))
2678 throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.IssueInstant, Saml2Constants.Elements.Assertion));
2681 assertion.IssueInstant = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
2683 // Process <elements>
2687 assertion.Issuer = this.ReadIssuer(realReader);
2689 // <ds:Signature> 0-1
2690 realReader.TryReadSignature();
2693 if (realReader.IsStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace))
2695 assertion.Subject = this.ReadSubject(realReader);
2699 if (realReader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace))
2701 assertion.Conditions = this.ReadConditions(realReader);
2705 if (realReader.IsStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace))
2707 assertion.Advice = this.ReadAdvice(realReader);
2710 // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO
2711 while (realReader.IsStartElement())
2713 Saml2Statement statement;
2715 if (realReader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
2717 statement = this.ReadStatement(realReader);
2719 else if (realReader.IsStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace))
2721 statement = this.ReadAttributeStatement(realReader);
2723 else if (realReader.IsStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace))
2725 statement = this.ReadAuthenticationStatement(realReader);
2727 else if (realReader.IsStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace))
2729 statement = this.ReadAuthorizationDecisionStatement(realReader);
2736 assertion.Statements.Add(statement);
2739 realReader.ReadEndElement();
2741 if (null == assertion.Subject)
2743 // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585]
2744 if (0 == assertion.Statements.Count)
2746 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4106));
2749 // Furthermore, the built-in statement types all require the presence of a subject.
2750 // [Saml2Core, lines 1050, 1168, 1280]
2751 foreach (Saml2Statement statement in assertion.Statements)
2753 if (statement is Saml2AuthenticationStatement
2754 || statement is Saml2AttributeStatement
2755 || statement is Saml2AuthorizationDecisionStatement)
2757 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4119));
2762 // Reading the end element will complete the signature;
2763 // capture the signing creds
2764 assertion.SigningCredentials = realReader.SigningCredentials;
2766 // Save the captured on-the-wire data, which can then be used
2767 // to re-emit this assertion, preserving the same signature.
2768 assertion.CaptureSourceData(realReader);
2774 if (System.Runtime.Fx.IsFatal(e))
2777 Exception wrapped = TryWrapReadException(realReader, e);
2778 if (null == wrapped)
2790 /// Serializes the provided SamlAssertion to the XmlWriter.
2792 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Assertion"/>.</param>
2793 /// <param name="data">The <see cref="Saml2Assertion"/> to serialize.</param>
2794 /// <exception cref="ArgumentNullException">The <paramref name="writer"/> or <paramref name="data"/> parameters are null.</exception>
2795 /// <exception cref="InvalidOperationException"> The <paramref name="data"/> has both <see cref="EncryptingCredentials"/> and <see cref="ReceivedEncryptingCredentials"/> properties null.</exception>
2796 /// <exception cref="InvalidOperationException">The <paramref name="data"/> must have a <see cref="Saml2Subject"/> if no <see cref="Saml2Statement"/> are present.</exception>
2797 /// <exception cref="InvalidOperationException">The SAML2 authentication, attribute, and authorization decision <see cref="Saml2Statement"/> require a <see cref="Saml2Subject"/>.</exception>
2798 /// <exception cref="CryptographicException">Token encrypting credentials must have a Symmetric Key specified.</exception>
2799 protected virtual void WriteAssertion(XmlWriter writer, Saml2Assertion data)
2803 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
2808 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
2811 XmlWriter originalWriter = writer;
2812 MemoryStream plaintextStream = null;
2813 XmlDictionaryWriter plaintextWriter = null;
2815 // If an EncryptingCredentials is present then check if this is not of type ReceivedEncryptinCredentials.
2816 // ReceivedEncryptingCredentials mean that it was credentials that were hydrated from a token received
2817 // on the wire. We should not directly use this while re-serializing a token.
2818 if ((null != data.EncryptingCredentials) && !(data.EncryptingCredentials is ReceivedEncryptingCredentials))
2820 plaintextStream = new MemoryStream();
2821 writer = plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false);
2823 else if (data.ExternalEncryptedKeys == null || data.ExternalEncryptedKeys.Count > 0)
2825 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4173)));
2828 // If we've saved off the token stream, re-emit it.
2829 if (data.CanWriteSourceData)
2831 data.WriteSourceData(writer);
2835 // Wrap the writer if necessary for a signature
2836 // We do not dispose this writer, since as a delegating writer it would
2837 // dispose the inner writer, which we don't properly own.
2838 EnvelopedSignatureWriter signatureWriter = null;
2839 if (null != data.SigningCredentials)
2841 #pragma warning suppress 56506
2842 writer = signatureWriter = new EnvelopedSignatureWriter(writer, data.SigningCredentials, data.Id.Value, new WrappedSerializer(this, data));
2845 if (null == data.Subject)
2847 // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585]
2848 if (data.Statements == null || 0 == data.Statements.Count)
2850 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4106)));
2853 // Furthermore, the built-in statement types all require the presence of a subject.
2854 // [Saml2Core, lines 1050, 1168, 1280]
2855 foreach (Saml2Statement statement in data.Statements)
2857 if (statement is Saml2AuthenticationStatement
2858 || statement is Saml2AttributeStatement
2859 || statement is Saml2AuthorizationDecisionStatement)
2861 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2862 new InvalidOperationException(SR.GetString(SR.ID4119)));
2868 writer.WriteStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace);
2871 writer.WriteAttributeString(Saml2Constants.Attributes.ID, data.Id.Value);
2873 // @IssueInstant - required
2874 writer.WriteAttributeString(Saml2Constants.Attributes.IssueInstant, XmlConvert.ToString(data.IssueInstant.ToUniversalTime(), DateTimeFormats.Generated));
2876 // @Version - required
2877 writer.WriteAttributeString(Saml2Constants.Attributes.Version, data.Version);
2880 this.WriteIssuer(writer, data.Issuer);
2882 // <ds:Signature> 0-1
2883 if (null != signatureWriter)
2885 signatureWriter.WriteSignature();
2889 if (null != data.Subject)
2891 this.WriteSubject(writer, data.Subject);
2895 if (null != data.Conditions)
2897 this.WriteConditions(writer, data.Conditions);
2901 if (null != data.Advice)
2903 this.WriteAdvice(writer, data.Advice);
2906 // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO
2907 foreach (Saml2Statement statement in data.Statements)
2909 this.WriteStatement(writer, statement);
2912 writer.WriteEndElement();
2915 // Finish off the encryption
2916 if (null != plaintextWriter)
2918 ((IDisposable)plaintextWriter).Dispose();
2919 plaintextWriter = null;
2921 EncryptedDataElement encryptedData = new EncryptedDataElement();
2922 encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
2923 encryptedData.Algorithm = data.EncryptingCredentials.Algorithm;
2924 encryptedData.KeyIdentifier = data.EncryptingCredentials.SecurityKeyIdentifier;
2926 // Get the encryption key, which must be symmetric
2927 SymmetricSecurityKey encryptingKey = data.EncryptingCredentials.SecurityKey as SymmetricSecurityKey;
2928 if (encryptingKey == null)
2930 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3064)));
2933 // Do the actual encryption
2934 SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(data.EncryptingCredentials.Algorithm);
2935 encryptedData.Encrypt(symmetricAlgorithm, plaintextStream.GetBuffer(), 0, (int)plaintextStream.Length);
2936 ((IDisposable)plaintextStream).Dispose();
2938 originalWriter.WriteStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace);
2939 encryptedData.WriteXml(originalWriter, this.KeyInfoSerializer);
2940 foreach (EncryptedKeyIdentifierClause clause in data.ExternalEncryptedKeys)
2942 this.KeyInfoSerializer.WriteKeyIdentifierClause(originalWriter, clause);
2945 originalWriter.WriteEndElement();
2950 /// Reads the <saml:Attribute> element.
2953 /// The default implementation requires that the content of the
2954 /// Attribute element be a simple string. To handle complex content
2955 /// or content of declared simple types other than xs:string, override
2958 /// <param name="reader">An <see cref="XmlReader"/> positioned at a <see cref="Saml2Attribute"/> element.</param>
2959 /// <returns>A <see cref="Saml2Attribute"/> instance.</returns>
2960 protected virtual Saml2Attribute ReadAttribute(XmlReader reader)
2964 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
2967 // throw if wrong element
2968 if (!reader.IsStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace))
2970 reader.ReadStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
2975 Saml2Attribute attribute;
2976 bool isEmpty = reader.IsEmptyElement;
2982 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AttributeType, Saml2Constants.Namespace);
2985 value = reader.GetAttribute(Saml2Constants.Attributes.Name);
2986 if (string.IsNullOrEmpty(value))
2988 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Name, Saml2Constants.Elements.Attribute));
2991 attribute = new Saml2Attribute(value);
2993 // @NameFormat - optional
2994 value = reader.GetAttribute(Saml2Constants.Attributes.NameFormat);
2995 if (!string.IsNullOrEmpty(value))
2997 if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
2999 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
3002 attribute.NameFormat = new Uri(value);
3005 // @FriendlyName - optional
3006 attribute.FriendlyName = reader.GetAttribute(Saml2Constants.Attributes.FriendlyName);
3008 // @OriginalIssuer - optional.
3009 // We are lax on read here, and will accept the following namespaces for original issuer, in order:
3010 // http://schemas.xmlsoap.org/ws/2009/09/identity/claims
3011 // http://schemas.microsoft.com/ws/2008/06/identity
3012 string originalIssuer = reader.GetAttribute(Saml2Constants.Attributes.OriginalIssuer, ClaimType2009Namespace);
3014 if (originalIssuer == null)
3016 originalIssuer = reader.GetAttribute(Saml2Constants.Attributes.OriginalIssuer, ProductConstants.NamespaceUri);
3019 if (originalIssuer == String.Empty)
3021 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4252)));
3024 attribute.OriginalIssuer = originalIssuer;
3030 while (reader.IsStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace))
3032 bool isEmptyValue = reader.IsEmptyElement;
3033 bool isNil = XmlUtil.IsNil(reader);
3035 // FIP 9570 - ENTERPRISE SCENARIO: Saml11SecurityTokenHandler.ReadAttribute is not checking the AttributeValue XSI type correctly.
3036 // Lax on receive. If we dont find the AttributeValueXsiType in the format we are looking for in the xml, we default to string.
3037 // Read the xsi:type. We are expecting a value of the form "some-non-empty-string" or "some-non-empty-local-prefix:some-non-empty-string".
3038 // ":some-non-empty-string" and "some-non-empty-string:" are edge-cases where defaulting to string is reasonable.
3039 // For attributeValueXsiTypeSuffix, we want the portion after the local prefix in "some-non-empty-local-prefix:some-non-empty-string"
3040 // "some-non-empty-local-prefix:some-non-empty-string" case
3041 string attributeValueXsiTypePrefix = null;
3042 string attributeValueXsiTypeSuffix = null;
3043 string attributeValueXsiTypeSuffixWithLocalPrefix = reader.GetAttribute("type", XmlSchema.InstanceNamespace);
3044 if (!string.IsNullOrEmpty(attributeValueXsiTypeSuffixWithLocalPrefix))
3046 // "some-non-empty-string" case
3047 if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) == -1)
3049 attributeValueXsiTypePrefix = reader.LookupNamespace(String.Empty);
3050 attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix;
3052 else if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) > 0 &&
3053 attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) < attributeValueXsiTypeSuffixWithLocalPrefix.Length - 1)
3055 string localPrefix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(0, attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal));
3056 attributeValueXsiTypePrefix = reader.LookupNamespace(localPrefix);
3057 attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) + 1);
3061 if (attributeValueXsiTypePrefix != null && attributeValueXsiTypeSuffix != null)
3063 attribute.AttributeValueXsiType = String.Concat(attributeValueXsiTypePrefix, "#", attributeValueXsiTypeSuffix);
3071 reader.ReadEndElement();
3074 attribute.Values.Add(null);
3076 else if (isEmptyValue)
3079 attribute.Values.Add(string.Empty);
3083 attribute.Values.Add(this.ReadAttributeValue(reader, attribute));
3087 reader.ReadEndElement();
3094 if (System.Runtime.Fx.IsFatal(e))
3097 Exception wrapped = TryWrapReadException(reader, e);
3098 if (null == wrapped)
3110 /// Reads an attribute value.
3112 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Attribute"/>.</param>
3113 /// <param name="attribute">The <see cref="Saml2Attribute"/>.</param>
3114 /// <returns>The attribute value as a string.</returns>
3115 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
3116 protected virtual string ReadAttributeValue(XmlReader reader, Saml2Attribute attribute)
3118 // This code was designed realizing that the writter of the xml controls how our
3119 // reader will report the NodeType. A completely differnet system (IBM, etc) could write the values.
3120 // Considering NodeType is important, because we need to read the entire value, end element and not loose anything significant.
3122 // Couple of cases to help understand the design choices.
3125 // "<MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>"
3126 // Could result in the our reader reporting the NodeType as Text OR Element, depending if '<' was entitized to '<'
3129 // " <MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>"
3130 // Could result in the our reader reporting the NodeType as Text OR Whitespace. Post Whitespace processing, the NodeType could be
3131 // reported as Text or Element, depending if '<' was entitized to '<'
3135 // Could result in the our reader reporting the NodeType as whitespace.
3137 // Since an AttributeValue with ONLY Whitespace and a complex Element proceeded by whitespace are reported as the same NodeType (2. and 3.)
3138 // the whitespace is remembered and discarded if an found is found, otherwise it becomes the value. This is to help users who accidently put a space when adding claims in ADFS
3139 // If we just skipped the Whitespace, then an AttributeValue that started with Whitespace would loose that part and claims generated from the AttributeValue
3140 // would be missing that part.
3145 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3148 string result = String.Empty;
3149 string whiteSpace = String.Empty;
3151 reader.ReadStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace);
3153 while (reader.NodeType == XmlNodeType.Whitespace)
3155 whiteSpace += reader.Value;
3159 reader.MoveToContent();
3160 if (reader.NodeType == XmlNodeType.Element)
3162 while (reader.NodeType == XmlNodeType.Element)
3164 result += reader.ReadOuterXml();
3165 reader.MoveToContent();
3170 result = whiteSpace;
3171 result += reader.ReadContentAsString();
3174 reader.ReadEndElement();
3179 /// Writes the <saml:Attribute> element.
3181 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Attribute"/>.</param>
3182 /// <param name="data">The <see cref="Saml2Attribute"/> to serialize.</param>
3183 protected virtual void WriteAttribute(XmlWriter writer, Saml2Attribute data)
3187 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3192 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3196 writer.WriteStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
3199 writer.WriteAttributeString(Saml2Constants.Attributes.Name, data.Name);
3201 // @NameFormat - optional
3202 if (null != data.NameFormat)
3204 writer.WriteAttributeString(Saml2Constants.Attributes.NameFormat, data.NameFormat.AbsoluteUri);
3207 // @FriendlyName - optional
3208 if (null != data.FriendlyName)
3210 writer.WriteAttributeString(Saml2Constants.Attributes.FriendlyName, data.FriendlyName);
3213 // @OriginalIssuer - optional
3214 if (null != data.OriginalIssuer)
3216 writer.WriteAttributeString(Saml2Constants.Attributes.OriginalIssuer, ClaimType2009Namespace, data.OriginalIssuer);
3219 string xsiTypePrefix = null;
3220 string xsiTypeSuffix = null;
3221 if (!StringComparer.Ordinal.Equals(data.AttributeValueXsiType, ClaimValueTypes.String))
3223 // ClaimValueTypes are URIs of the form prefix#suffix, while xsi:type should be a QName.
3224 // Hence, the tokens-to-claims spec requires that ClaimValueTypes be serialized as xmlns:tn="prefix" xsi:type="tn:suffix"
3225 int indexOfHash = data.AttributeValueXsiType.IndexOf('#');
3226 xsiTypePrefix = data.AttributeValueXsiType.Substring(0, indexOfHash);
3227 xsiTypeSuffix = data.AttributeValueXsiType.Substring(indexOfHash + 1);
3230 // <AttributeValue> 0-OO (nillable)
3231 foreach (string value in data.Values)
3233 writer.WriteStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace);
3237 writer.WriteAttributeString("nil", XmlSchema.InstanceNamespace, XmlConvert.ToString(true));
3239 else if (value.Length > 0)
3241 if ((xsiTypePrefix != null) && (xsiTypeSuffix != null))
3243 writer.WriteAttributeString("xmlns", ProductConstants.ClaimValueTypeSerializationPrefix, null, xsiTypePrefix);
3244 writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, String.Concat(ProductConstants.ClaimValueTypeSerializationPrefixWithColon, xsiTypeSuffix));
3247 this.WriteAttributeValue(writer, value, data);
3250 writer.WriteEndElement();
3254 writer.WriteEndElement();
3258 /// Writes the saml:Attribute value.
3260 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Attribute"/>.</param>
3261 /// <param name="value">The value of the attribute being serialized.</param>
3262 /// <param name="attribute">The <see cref="Saml2Attribute"/> to serialize.</param>
3263 /// <remarks>By default the method writes the value as a string.</remarks>
3264 /// <exception cref="ArgumentNullException">The input parameter 'writer' is null.</exception>
3265 protected virtual void WriteAttributeValue(XmlWriter writer, string value, Saml2Attribute attribute)
3269 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3272 writer.WriteString(value);
3276 /// Reads the <saml:AttributeStatement> element, or a
3277 /// <saml:Statement element that specifies an xsi:type of
3278 /// saml:AttributeStatementType.
3280 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AttributeStatement"/> element.</param>
3281 /// <returns>A <see cref="Saml2AttributeStatement"/> instance.</returns>
3282 protected virtual Saml2AttributeStatement ReadAttributeStatement(XmlReader reader)
3286 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3289 // throw if wrong element
3290 bool isStatementElement = false;
3291 if (reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
3293 isStatementElement = true;
3295 else if (!reader.IsStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace))
3297 reader.ReadStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace);
3302 // defer disallowing empty element until checking xsi:type
3303 bool isEmpty = reader.IsEmptyElement;
3308 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AttributeStatementType, Saml2Constants.Namespace, isStatementElement);
3310 // disallow empty element, since xsi:type is ok
3313 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace));
3317 Saml2AttributeStatement statement = new Saml2AttributeStatement();
3320 // <Attribute|EncryptedAttribute> 1-OO
3321 while (reader.IsStartElement())
3323 if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAttribute, Saml2Constants.Namespace))
3325 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4158));
3327 else if (reader.IsStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace))
3329 statement.Attributes.Add(this.ReadAttribute(reader));
3337 // At least one attribute expected
3338 if (0 == statement.Attributes.Count)
3340 reader.ReadStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
3343 reader.ReadEndElement();
3349 if (System.Runtime.Fx.IsFatal(e))
3352 Exception wrapped = TryWrapReadException(reader, e);
3353 if (null == wrapped)
3365 /// Writes the <saml:AttributeStatement> element.
3367 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AttributeStatement"/>.</param>
3368 /// <param name="data">The <see cref="Saml2AttributeStatement"/> to serialize.</param>
3369 protected virtual void WriteAttributeStatement(XmlWriter writer, Saml2AttributeStatement data)
3373 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3378 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3381 if (data.Attributes == null || 0 == data.Attributes.Count)
3383 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4124)));
3386 // <AttributeStatement>
3387 writer.WriteStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace);
3390 foreach (Saml2Attribute attribute in data.Attributes)
3392 this.WriteAttribute(writer, attribute);
3395 // </AttributeStatement>
3396 writer.WriteEndElement();
3400 /// Reads the <saml:AudienceRestriction> element or a
3401 /// <saml:Condition> element that specifies an xsi:type
3402 /// of saml:AudienceRestrictionType.
3404 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AudienceRestriction"/> element.</param>
3405 /// <returns>A <see cref="Saml2AudienceRestriction"/> instance.</returns>
3406 protected virtual Saml2AudienceRestriction ReadAudienceRestriction(XmlReader reader)
3410 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3413 // throw if wrong element
3414 bool isConditionElement = false;
3415 if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace))
3417 isConditionElement = true;
3419 else if (!reader.IsStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace))
3421 reader.ReadStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace);
3426 Saml2AudienceRestriction audienceRestriction;
3427 bool isEmpty = reader.IsEmptyElement;
3431 // @xsi:type -- if we're a <Condition> element, this declaration must be present
3432 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AudienceRestrictionType, Saml2Constants.Namespace, isConditionElement);
3437 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
3443 // <Audience> - 1-OO
3444 if (!reader.IsStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace))
3446 reader.ReadStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace);
3449 // We are now laxing the uri check for audience restriction to support interop partners
3450 // This is a specific request from server :
3452 audienceRestriction = new Saml2AudienceRestriction(ReadSimpleUriElement(reader, UriKind.RelativeOrAbsolute, true));
3453 while (reader.IsStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace))
3455 audienceRestriction.Audiences.Add(ReadSimpleUriElement(reader, UriKind.RelativeOrAbsolute, true));
3458 reader.ReadEndElement();
3460 return audienceRestriction;
3464 if (System.Runtime.Fx.IsFatal(e))
3467 Exception wrapped = TryWrapReadException(reader, e);
3468 if (null == wrapped)
3480 /// Writes the <saml:AudienceRestriction> element.
3482 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AudienceRestriction"/>.</param>
3483 /// <param name="data">The <see cref="Saml2AudienceRestriction"/> to serialize.</param>
3484 protected virtual void WriteAudienceRestriction(XmlWriter writer, Saml2AudienceRestriction data)
3488 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3493 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3496 // Schema requires at least one audience.
3497 if (data.Audiences == null || 0 == data.Audiences.Count)
3499 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4159)));
3502 // <AudienceRestriction>
3503 writer.WriteStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace);
3505 // <Audience> - 1-OO
3506 foreach (Uri audience in data.Audiences)
3508 // When writing out the audience uri we use the OriginalString property to preserve the value that was initially passed down during token creation as-is.
3509 writer.WriteElementString(Saml2Constants.Elements.Audience, Saml2Constants.Namespace, audience.OriginalString);
3512 // </AudienceRestriction>
3513 writer.WriteEndElement();
3517 /// Reads the <saml:AuthnContext> element.
3520 /// The default implementation does not handle the optional
3521 /// <saml:AuthnContextDecl> element. To handle by-value
3522 /// authentication context declarations, override this method.
3524 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AuthenticationContext"/> element.</param>
3525 /// <returns>A <see cref="Saml2AuthenticationContext"/> instance.</returns>
3526 protected virtual Saml2AuthenticationContext ReadAuthenticationContext(XmlReader reader)
3530 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3533 // throw if wrong element
3534 if (!reader.IsStartElement(Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace))
3536 reader.ReadStartElement(Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace);
3542 if (reader.IsEmptyElement)
3544 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace));
3550 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AuthnContextType, Saml2Constants.Namespace);
3553 reader.ReadStartElement();
3555 // At least one of ClassRef and ( Decl XOR DeclRef) must be present
3556 // At this time, we do not support Decl, which is a by-value
3557 // authentication context declaration.
3558 Uri classRef = null;
3561 // <AuthnContextClassRef> - see comment above
3562 if (reader.IsStartElement(Saml2Constants.Elements.AuthnContextClassRef, Saml2Constants.Namespace))
3564 classRef = ReadSimpleUriElement(reader);
3567 // <AuthnContextDecl> - see comment above
3568 if (reader.IsStartElement(Saml2Constants.Elements.AuthnContextDecl, Saml2Constants.Namespace))
3570 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4118));
3573 // <AuthnContextDeclRef> - see comment above
3574 // If there was no ClassRef, there must be a DeclRef
3575 if (reader.IsStartElement(Saml2Constants.Elements.AuthnContextDeclRef, Saml2Constants.Namespace))
3577 declRef = ReadSimpleUriElement(reader);
3579 else if (null == classRef)
3581 reader.ReadStartElement(Saml2Constants.Elements.AuthnContextDeclRef, Saml2Constants.Namespace);
3584 // Now we have enough data to create the object
3585 Saml2AuthenticationContext authnContext = new Saml2AuthenticationContext(classRef, declRef);
3587 // <AuthenticatingAuthority> - 0-OO
3588 while (reader.IsStartElement(Saml2Constants.Elements.AuthenticatingAuthority, Saml2Constants.Namespace))
3590 authnContext.AuthenticatingAuthorities.Add(ReadSimpleUriElement(reader));
3593 reader.ReadEndElement();
3595 return authnContext;
3599 if (System.Runtime.Fx.IsFatal(e))
3602 Exception wrapped = TryWrapReadException(reader, e);
3603 if (null == wrapped)
3615 /// Writes the <saml:AuthnContext> element.
3617 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AuthenticationContext"/>.</param>
3618 /// <param name="data">The <see cref="Saml2AuthenticationContext"/> to serialize.</param>
3619 protected virtual void WriteAuthenticationContext(XmlWriter writer, Saml2AuthenticationContext data)
3623 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3628 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3631 // One of ClassRef and DeclRef must be present.
3632 if (null == data.ClassReference && null == data.DeclarationReference)
3634 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3635 new InvalidOperationException(SR.GetString(SR.ID4117)));
3639 writer.WriteStartElement(Saml2Constants.Elements.AuthnContext, Saml2Constants.Namespace);
3641 // <AuthnContextClassReference> 0-1
3642 if (null != data.ClassReference)
3644 writer.WriteElementString(Saml2Constants.Elements.AuthnContextClassRef, Saml2Constants.Namespace, data.ClassReference.AbsoluteUri);
3647 // <AuthnContextDeclRef> 0-1
3648 if (null != data.DeclarationReference)
3650 writer.WriteElementString(Saml2Constants.Elements.AuthnContextDeclRef, Saml2Constants.Namespace, data.DeclarationReference.AbsoluteUri);
3653 // <AuthenticatingAuthority> 0-OO
3654 foreach (Uri authority in data.AuthenticatingAuthorities)
3656 writer.WriteElementString(Saml2Constants.Elements.AuthenticatingAuthority, Saml2Constants.Namespace, authority.AbsoluteUri);
3660 writer.WriteEndElement();
3664 /// Reads the <saml:AuthnStatement> element or a <saml:Statement>
3665 /// element that specifies an xsi:type of saml:AuthnStatementType.
3667 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AuthenticationStatement"/> element.</param>
3668 /// <returns>A <see cref="Saml2AuthenticationStatement"/> instance.</returns>
3669 protected virtual Saml2AuthenticationStatement ReadAuthenticationStatement(XmlReader reader)
3673 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3676 // throw if wrong element
3677 bool isStatementElement = false;
3678 if (reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
3680 isStatementElement = true;
3682 else if (!reader.IsStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace))
3684 reader.ReadStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace);
3689 // Must cache the individual data since the required
3690 // AuthnContext comes last
3691 DateTime authnInstant;
3692 Saml2AuthenticationContext authnContext;
3693 string sessionIndex;
3694 DateTime? sessionNotOnOrAfter = null;
3695 Saml2SubjectLocality subjectLocality = null;
3697 // defer disallowing empty until after xsi:type
3698 bool isEmpty = reader.IsEmptyElement;
3703 // @xsi:type -- if we're a <Statement> element, this declaration must be present
3704 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AuthnStatementType, Saml2Constants.Namespace, isStatementElement);
3706 // disallow empty, since xsi:type is ok
3709 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace));
3712 // @AuthnInstant - required
3713 value = reader.GetAttribute(Saml2Constants.Attributes.AuthnInstant);
3714 if (string.IsNullOrEmpty(value))
3716 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.AuthnInstant, Saml2Constants.Elements.AuthnStatement));
3719 authnInstant = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
3721 // @SessionIndex - optional
3722 sessionIndex = reader.GetAttribute(Saml2Constants.Attributes.SessionIndex);
3724 // @SessionNotOnOrAfter - optional
3725 value = reader.GetAttribute(Saml2Constants.Attributes.SessionNotOnOrAfter);
3726 if (!string.IsNullOrEmpty(value))
3728 sessionNotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
3734 // <SubjectLocality> 0-1
3735 if (reader.IsStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace))
3737 subjectLocality = this.ReadSubjectLocality(reader);
3741 authnContext = this.ReadAuthenticationContext(reader);
3743 reader.ReadEndElement();
3745 // Construct the actual object
3746 Saml2AuthenticationStatement authnStatement = new Saml2AuthenticationStatement(authnContext, authnInstant);
3747 authnStatement.SessionIndex = sessionIndex;
3748 authnStatement.SessionNotOnOrAfter = sessionNotOnOrAfter;
3749 authnStatement.SubjectLocality = subjectLocality;
3751 return authnStatement;
3755 if (System.Runtime.Fx.IsFatal(e))
3758 Exception wrapped = TryWrapReadException(reader, e);
3759 if (null == wrapped)
3771 /// Writes the <saml:AuthnStatement> element.
3773 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AuthenticationStatement"/>.</param>
3774 /// <param name="data">The <see cref="Saml2AuthenticationStatement"/> to serialize.</param>
3775 protected virtual void WriteAuthenticationStatement(XmlWriter writer, Saml2AuthenticationStatement data)
3779 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3784 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3788 writer.WriteStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace);
3790 // @AuthnInstant - required
3791 writer.WriteAttributeString(Saml2Constants.Attributes.AuthnInstant, XmlConvert.ToString(data.AuthenticationInstant.ToUniversalTime(), DateTimeFormats.Generated));
3793 // @SessionIndex - optional
3794 if (null != data.SessionIndex)
3796 writer.WriteAttributeString(Saml2Constants.Attributes.SessionIndex, data.SessionIndex);
3799 // @SessionNotOnOrAfter - optional
3800 if (null != data.SessionNotOnOrAfter)
3802 writer.WriteAttributeString(Saml2Constants.Attributes.SessionNotOnOrAfter, XmlConvert.ToString(data.SessionNotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
3805 // <SubjectLocality> 0-1
3806 if (null != data.SubjectLocality)
3808 this.WriteSubjectLocality(writer, data.SubjectLocality);
3812 this.WriteAuthenticationContext(writer, data.AuthenticationContext);
3814 // </AuthnStatement>
3815 writer.WriteEndElement();
3819 /// Reads the <saml:AuthzDecisionStatement> element or a
3820 /// <saml:Statement element that specifies an xsi:type of
3821 /// saml:AuthzDecisionStatementType.
3823 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2AuthorizationDecisionStatement"/> element.</param>
3824 /// <returns>A <see cref="Saml2AuthorizationDecisionStatement"/> instance.</returns>
3825 protected virtual Saml2AuthorizationDecisionStatement ReadAuthorizationDecisionStatement(XmlReader reader)
3829 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
3832 // throw if wrong element
3833 bool isStatementElement = false;
3834 if (reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
3836 isStatementElement = true;
3838 else if (!reader.IsStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace))
3840 reader.ReadStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace);
3845 // Need the attributes before we can instantiate
3846 Saml2AuthorizationDecisionStatement statement;
3847 SamlAccessDecision decision;
3850 // defer rejecting empty until processing xsi:type
3851 bool isEmpty = reader.IsEmptyElement;
3857 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AuthzDecisionStatementType, Saml2Constants.Namespace, isStatementElement);
3859 // disallow empty, since xsi:type is ok
3862 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace));
3865 // @Decision - required
3866 value = reader.GetAttribute(Saml2Constants.Attributes.Decision);
3867 if (string.IsNullOrEmpty(value))
3869 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Decision, Saml2Constants.Elements.AuthzDecisionStatement));
3871 else if (StringComparer.Ordinal.Equals(SamlAccessDecision.Permit.ToString(), value))
3873 decision = SamlAccessDecision.Permit;
3875 else if (StringComparer.Ordinal.Equals(SamlAccessDecision.Deny.ToString(), value))
3877 decision = SamlAccessDecision.Deny;
3879 else if (StringComparer.Ordinal.Equals(SamlAccessDecision.Indeterminate.ToString(), value))
3881 decision = SamlAccessDecision.Indeterminate;
3885 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4123, value));
3888 // @Resource - required
3889 value = reader.GetAttribute(Saml2Constants.Attributes.Resource);
3892 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Resource, Saml2Constants.Elements.AuthzDecisionStatement));
3894 else if (0 == value.Length)
3896 resource = Saml2AuthorizationDecisionStatement.EmptyResource;
3900 if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
3902 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4121));
3905 resource = new Uri(value);
3909 statement = new Saml2AuthorizationDecisionStatement(resource, decision);
3915 statement.Actions.Add(this.ReadAction(reader));
3917 while (reader.IsStartElement(Saml2Constants.Elements.Action, Saml2Constants.Namespace));
3920 if (reader.IsStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace))
3922 statement.Evidence = this.ReadEvidence(reader);
3925 reader.ReadEndElement();
3931 if (System.Runtime.Fx.IsFatal(e))
3934 Exception wrapped = TryWrapReadException(reader, e);
3935 if (null == wrapped)
3947 /// Writes the <saml:AuthzDecisionStatement> element.
3949 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2AuthorizationDecisionStatement"/>.</param>
3950 /// <param name="data">The <see cref="Saml2AuthorizationDecisionStatement"/> to serialize.</param>
3951 protected virtual void WriteAuthorizationDecisionStatement(XmlWriter writer, Saml2AuthorizationDecisionStatement data)
3955 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
3960 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
3963 #pragma warning suppress 56506 // actions are never null
3964 if (0 == data.Actions.Count)
3966 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3967 new InvalidOperationException(SR.GetString(SR.ID4122)));
3970 // <AuthzDecisionStatement>
3971 writer.WriteStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace);
3973 // @Decision - required
3974 writer.WriteAttributeString(Saml2Constants.Attributes.Decision, data.Decision.ToString());
3976 // @Resource - required
3977 #pragma warning suppress 56506 // Resource are never null
3978 writer.WriteAttributeString(Saml2Constants.Attributes.Resource, data.Resource.Equals(Saml2AuthorizationDecisionStatement.EmptyResource) ? data.Resource.ToString() : data.Resource.AbsoluteUri);
3981 foreach (Saml2Action action in data.Actions)
3983 this.WriteAction(writer, action);
3987 if (null != data.Evidence)
3989 this.WriteEvidence(writer, data.Evidence);
3992 // </AuthzDecisionStatement>
3993 writer.WriteEndElement();
3997 /// Reads the <saml:Conditions> element.
4000 /// To handle custom <saml:Condition> elements, override this
4003 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Conditions"/> element.</param>
4004 /// <returns>A <see cref="Saml2Conditions"/> instance.</returns>
4005 protected virtual Saml2Conditions ReadConditions(XmlReader reader)
4009 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4012 // throw if wrong element
4013 if (!reader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace))
4015 reader.ReadStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace);
4020 Saml2Conditions conditions = new Saml2Conditions();
4022 bool isEmpty = reader.IsEmptyElement;
4028 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ConditionsType, Saml2Constants.Namespace);
4030 // @NotBefore - optional
4031 value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore);
4032 if (!string.IsNullOrEmpty(value))
4034 conditions.NotBefore = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
4037 // @NotOnOrAfter - optional
4038 value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter);
4039 if (!string.IsNullOrEmpty(value))
4041 conditions.NotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
4045 reader.ReadStartElement();
4048 // <Condition|AudienceRestriction|OneTimeUse|ProxyRestriction>, 0-OO
4049 while (reader.IsStartElement())
4051 // <Condition> - 0-OO
4052 if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace))
4054 // Since Condition is abstract, must process based on xsi:type
4055 XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader);
4058 if (null == declaredType
4059 || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.ConditionAbstractType, Saml2Constants.Namespace))
4061 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI));
4063 else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AudienceRestrictionType, Saml2Constants.Namespace))
4065 conditions.AudienceRestrictions.Add(this.ReadAudienceRestriction(reader));
4067 else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.OneTimeUseType, Saml2Constants.Namespace))
4069 if (conditions.OneTimeUse)
4071 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.OneTimeUse));
4074 ReadEmptyContentElement(reader);
4075 conditions.OneTimeUse = true;
4077 else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.ProxyRestrictionType, Saml2Constants.Namespace))
4079 if (null != conditions.ProxyRestriction)
4081 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.ProxyRestricton));
4084 conditions.ProxyRestriction = this.ReadProxyRestriction(reader);
4088 // Unknown type - Instruct the user to override to handle custom <Condition>
4089 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4113));
4092 else if (reader.IsStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace))
4094 conditions.AudienceRestrictions.Add(this.ReadAudienceRestriction(reader));
4096 else if (reader.IsStartElement(Saml2Constants.Elements.OneTimeUse, Saml2Constants.Namespace))
4098 if (conditions.OneTimeUse)
4100 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.OneTimeUse));
4103 ReadEmptyContentElement(reader);
4104 conditions.OneTimeUse = true;
4106 else if (reader.IsStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace))
4108 if (null != conditions.ProxyRestriction)
4110 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.ProxyRestricton));
4113 conditions.ProxyRestriction = this.ReadProxyRestriction(reader);
4121 reader.ReadEndElement();
4128 if (System.Runtime.Fx.IsFatal(e))
4131 Exception wrapped = TryWrapReadException(reader, e);
4132 if (null == wrapped)
4144 /// Writes the <saml:Conditions> element.
4146 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Conditions"/>.</param>
4147 /// <param name="data">The <see cref="Saml2Conditions"/> to serialize.</param>
4148 protected virtual void WriteConditions(XmlWriter writer, Saml2Conditions data)
4152 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4157 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4161 writer.WriteStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace);
4163 // @NotBefore - optional
4164 if (null != data.NotBefore)
4166 writer.WriteAttributeString(Saml2Constants.Attributes.NotBefore, XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated));
4169 // @NotOnOrAfter - optional
4170 if (null != data.NotOnOrAfter)
4172 writer.WriteAttributeString(Saml2Constants.Attributes.NotOnOrAfter, XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
4175 // <AudienceRestriction> 0-OO
4176 foreach (Saml2AudienceRestriction audienceRestriction in data.AudienceRestrictions)
4178 this.WriteAudienceRestriction(writer, audienceRestriction);
4181 // <OneTimeUse> - limited to one in SAML spec
4182 if (data.OneTimeUse)
4184 writer.WriteStartElement(Saml2Constants.Elements.OneTimeUse, Saml2Constants.Namespace);
4185 writer.WriteEndElement();
4188 // <ProxyRestriction> - limited to one in SAML spec
4189 if (null != data.ProxyRestriction)
4191 this.WriteProxyRestriction(writer, data.ProxyRestriction);
4195 writer.WriteEndElement();
4199 /// Reads the <saml:Evidence> element.
4201 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Evidence"/> element.</param>
4202 /// <returns>A <see cref="Saml2Evidence"/> instance.</returns>
4203 protected virtual Saml2Evidence ReadEvidence(XmlReader reader)
4207 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4210 // throw if wrong element
4211 if (!reader.IsStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace))
4213 reader.ReadStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace);
4217 if (reader.IsEmptyElement)
4219 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, Saml2Constants.Elements.Evidence, Saml2Constants.Namespace));
4224 Saml2Evidence evidence = new Saml2Evidence();
4229 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.EvidenceType, Saml2Constants.Namespace);
4233 // <AssertionIDRef|AssertionURIRef|Assertion|EncryptedAssertion> 0-OO
4234 while (reader.IsStartElement())
4236 if (reader.IsStartElement(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace))
4238 evidence.AssertionIdReferences.Add(ReadSimpleNCNameElement(reader));
4240 else if (reader.IsStartElement(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace))
4242 evidence.AssertionUriReferences.Add(ReadSimpleUriElement(reader));
4244 else if (reader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace))
4246 evidence.Assertions.Add(this.ReadAssertion(reader));
4248 else if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace))
4250 evidence.Assertions.Add(this.ReadAssertion(reader));
4254 if (0 == evidence.AssertionIdReferences.Count
4255 && 0 == evidence.Assertions.Count
4256 && 0 == evidence.AssertionUriReferences.Count)
4258 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4120));
4261 reader.ReadEndElement();
4267 if (System.Runtime.Fx.IsFatal(e))
4270 Exception wrapped = TryWrapReadException(reader, e);
4271 if (null == wrapped)
4283 /// Writes the <saml:Evidence> element.
4285 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Evidence"/>.</param>
4286 /// <param name="data">The <see cref="Saml2Evidence"/> to serialize.</param>
4287 protected virtual void WriteEvidence(XmlWriter writer, Saml2Evidence data)
4291 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4296 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4299 if ((data.AssertionIdReferences == null || 0 == data.AssertionIdReferences.Count)
4300 && (data.Assertions == null || 0 == data.Assertions.Count)
4301 && (data.AssertionUriReferences == null || 0 == data.AssertionUriReferences.Count))
4303 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
4304 new InvalidOperationException(SR.GetString(SR.ID4120)));
4308 writer.WriteStartElement(Saml2Constants.Elements.Evidence, Saml2Constants.Namespace);
4310 // <AssertionIDRef> 0-OO
4311 foreach (Saml2Id id in data.AssertionIdReferences)
4313 writer.WriteElementString(Saml2Constants.Elements.AssertionIDRef, Saml2Constants.Namespace, id.Value);
4316 // <AssertionURIRef> 0-OO
4317 foreach (Uri uri in data.AssertionUriReferences)
4319 writer.WriteElementString(Saml2Constants.Elements.AssertionURIRef, Saml2Constants.Namespace, uri.AbsoluteUri);
4323 foreach (Saml2Assertion assertion in data.Assertions)
4325 this.WriteAssertion(writer, assertion);
4329 writer.WriteEndElement();
4333 /// Reads the <saml:Issuer> element.
4335 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
4336 /// <returns>A <see cref="Saml2NameIdentifier"/> instance.</returns>
4337 protected virtual Saml2NameIdentifier ReadIssuer(XmlReader reader)
4341 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4344 // throw if wrong element
4345 if (!reader.IsStartElement(Saml2Constants.Elements.Issuer, Saml2Constants.Namespace))
4347 reader.ReadStartElement(Saml2Constants.Elements.Issuer, Saml2Constants.Namespace);
4350 return this.ReadNameIdType(reader);
4354 /// Writes the <saml:Issuer> element.
4356 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
4357 /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
4358 protected virtual void WriteIssuer(XmlWriter writer, Saml2NameIdentifier data)
4362 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4367 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4370 writer.WriteStartElement(Saml2Constants.Elements.Issuer, Saml2Constants.Namespace);
4371 this.WriteNameIdType(writer, data);
4372 writer.WriteEndElement();
4376 /// Deserializes the SAML Subject KeyInfo.
4378 /// <param name="reader">XmlReader positioned at a ds:KeyInfo element.</param>
4379 /// <returns>A <see cref="SecurityKeyIdentifier"/> instance.</returns>
4380 /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
4381 protected virtual SecurityKeyIdentifier ReadSubjectKeyInfo(XmlReader reader)
4385 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4388 return this.KeyInfoSerializer.ReadKeyIdentifier(reader);
4392 /// Deserializes the SAML Signing KeyInfo
4394 /// <param name="reader">A <see cref="XmlReader"/> positioned at a than can be positioned at a ds:KeyInfo element.</param>
4395 /// <param name="assertion">The <see cref="Saml2Assertion"/> that is having the signature checked.</param>
4396 /// <returns>The <see cref="SecurityKeyIdentifier"/> that defines the key to use to check the signature.</returns>
4397 /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
4398 protected virtual SecurityKeyIdentifier ReadSigningKeyInfo(XmlReader reader, Saml2Assertion assertion)
4402 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4405 SecurityKeyIdentifier ski;
4407 if (this.KeyInfoSerializer.CanReadKeyIdentifier(reader))
4409 ski = this.KeyInfoSerializer.ReadKeyIdentifier(reader);
4413 KeyInfo keyInfo = new KeyInfo(this.KeyInfoSerializer);
4414 keyInfo.ReadXml(XmlDictionaryReader.CreateDictionaryReader(reader));
4415 ski = keyInfo.KeyIdentifier;
4421 return new SecurityKeyIdentifier(new Saml2SecurityKeyIdentifierClause(assertion));
4428 /// Serializes the Subject KeyInfo into the given XmlWriter.
4430 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifier"/>.</param>
4431 /// <param name="data">The <see cref="SecurityKeyIdentifier"/> to serialize.</param>
4432 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'data' is null.</exception>
4433 protected virtual void WriteSubjectKeyInfo(XmlWriter writer, SecurityKeyIdentifier data)
4437 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4442 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4445 this.KeyInfoSerializer.WriteKeyIdentifier(writer, data);
4449 /// Serializes the Signing KeyInfo into the given XmlWriter.
4451 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifier"/>.</param>
4452 /// <param name="data">The <see cref="SecurityKeyIdentifier"/> to serialize.</param>
4453 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'signingKeyIdentifier' is null.</exception>
4454 protected virtual void WriteSigningKeyInfo(XmlWriter writer, SecurityKeyIdentifier data)
4458 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4463 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4466 if (this.KeyInfoSerializer.CanWriteKeyIdentifier(data))
4468 this.KeyInfoSerializer.WriteKeyIdentifier(writer, data);
4472 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4221, data));
4476 /// Reads the <saml:NameID> element.
4478 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
4479 /// <returns>An instance of <see cref="Saml2NameIdentifier"/></returns>
4480 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
4481 protected virtual Saml2NameIdentifier ReadNameId(XmlReader reader)
4485 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4488 // throw if wrong element
4489 if (!reader.IsStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace))
4491 reader.ReadStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
4494 return this.ReadNameIdType(reader);
4498 /// Writes the <saml:NameID> element.
4500 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
4501 /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
4502 /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'data' is null.</exception>
4503 /// <exception cref="CryptographicException">Saml2NameIdentifier encrypting credentials must have a Symmetric Key specified.</exception>
4504 protected virtual void WriteNameId(XmlWriter writer, Saml2NameIdentifier data)
4508 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4513 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4516 // If there are encrypting credentials, then we need to encrypt the name identifier
4517 if (data.EncryptingCredentials != null)
4519 EncryptingCredentials encryptingCredentials = data.EncryptingCredentials;
4521 // Get the encryption key, which must be symmetric
4522 SymmetricSecurityKey encryptingKey = encryptingCredentials.SecurityKey as SymmetricSecurityKey;
4523 if (encryptingKey == null)
4525 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3284)));
4528 MemoryStream plaintextStream = null;
4531 // Serialize an encrypted name ID
4532 plaintextStream = new MemoryStream();
4534 using (XmlWriter plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false))
4536 plaintextWriter.WriteStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
4537 this.WriteNameIdType(plaintextWriter, data);
4538 plaintextWriter.WriteEndElement();
4541 EncryptedDataElement encryptedData = new EncryptedDataElement();
4542 encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
4543 encryptedData.Algorithm = encryptingCredentials.Algorithm;
4544 encryptedData.KeyIdentifier = encryptingCredentials.SecurityKeyIdentifier;
4546 // Perform encryption
4547 SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(encryptingCredentials.Algorithm);
4548 encryptedData.Encrypt(symmetricAlgorithm, plaintextStream.GetBuffer(), 0, (int)plaintextStream.Length);
4549 ((IDisposable)plaintextStream).Dispose();
4551 writer.WriteStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace);
4552 encryptedData.WriteXml(writer, this.KeyInfoSerializer);
4554 foreach (EncryptedKeyIdentifierClause clause in data.ExternalEncryptedKeys)
4556 this.KeyInfoSerializer.WriteKeyIdentifierClause(writer, clause);
4559 writer.WriteEndElement();
4563 if (plaintextStream != null)
4565 plaintextStream.Dispose();
4566 plaintextStream = null;
4572 writer.WriteStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
4573 this.WriteNameIdType(writer, data);
4574 writer.WriteEndElement();
4579 /// Both <Issuer> and <NameID> are of NameIDType. This method reads
4580 /// the content of either one of those elements.
4582 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2NameIdentifier"/> element.</param>
4583 /// <returns>An instance of <see cref="Saml2NameIdentifier"/></returns>
4584 protected virtual Saml2NameIdentifier ReadNameIdType(XmlReader reader)
4588 reader.MoveToContent();
4590 Saml2NameIdentifier nameIdentifier = new Saml2NameIdentifier("__TemporaryName__");
4596 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.NameIDType, Saml2Constants.Namespace);
4598 // @Format - optional
4599 value = reader.GetAttribute(Saml2Constants.Attributes.Format);
4600 if (!string.IsNullOrEmpty(value))
4602 if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
4604 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Format, Saml2Constants.Elements.NameID));
4607 nameIdentifier.Format = new Uri(value);
4610 // @NameQualifier - optional
4611 value = reader.GetAttribute(Saml2Constants.Attributes.NameQualifier);
4612 if (!string.IsNullOrEmpty(value))
4614 nameIdentifier.NameQualifier = value;
4617 // @SPNameQualifier - optional
4618 value = reader.GetAttribute(Saml2Constants.Attributes.SPNameQualifier);
4619 if (!string.IsNullOrEmpty(value))
4621 nameIdentifier.SPNameQualifier = value;
4624 // @SPProvidedID - optional
4625 value = reader.GetAttribute(Saml2Constants.Attributes.SPProvidedID);
4626 if (!string.IsNullOrEmpty(value))
4628 nameIdentifier.SPProvidedId = value;
4631 // Content is string
4632 nameIdentifier.Value = reader.ReadElementString();
4634 // According to section 8.3.6, if the name identifier format is of type 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'
4635 // the name identifier value must be a uri and name qualifier, spname qualifier, and spproded id must be omitted.
4636 if (nameIdentifier.Format != null &&
4637 StringComparer.Ordinal.Equals(nameIdentifier.Format.AbsoluteUri, Saml2Constants.NameIdentifierFormats.Entity.AbsoluteUri))
4639 if (!UriUtil.CanCreateValidUri(nameIdentifier.Value, UriKind.Absolute))
4641 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4262, nameIdentifier.Value, Saml2Constants.NameIdentifierFormats.Entity.AbsoluteUri));
4644 if (!string.IsNullOrEmpty(nameIdentifier.NameQualifier)
4645 || !string.IsNullOrEmpty(nameIdentifier.SPNameQualifier)
4646 || !string.IsNullOrEmpty(nameIdentifier.SPProvidedId))
4648 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4263, nameIdentifier.Value, Saml2Constants.NameIdentifierFormats.Entity.AbsoluteUri));
4652 return nameIdentifier;
4656 if (System.Runtime.Fx.IsFatal(e))
4659 Exception wrapped = TryWrapReadException(reader, e);
4660 if (null == wrapped)
4672 /// Reads the <saml:EncryptedId> element.
4674 /// <param name="reader"><see cref="XmlReader"/> pointing at the XML EncryptedId element</param>
4675 /// <returns>An instance of <see cref="Saml2NameIdentifier"/> representing the EncryptedId that was read</returns>
4676 /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
4677 /// <exception cref="XmlException">The 'reader' is not positioned at an 'EncryptedID' element.</exception>
4678 protected virtual Saml2NameIdentifier ReadEncryptedId(XmlReader reader)
4682 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4685 reader.MoveToContent();
4687 if (!reader.IsStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace))
4689 // throw if wrong element
4690 reader.ReadStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace);
4693 Collection<EncryptedKeyIdentifierClause> clauses = new Collection<EncryptedKeyIdentifierClause>();
4694 EncryptingCredentials encryptingCredentials = null;
4695 Saml2NameIdentifier saml2NameIdentifier = null;
4697 using (StringReader sr = new StringReader(reader.ReadOuterXml()))
4699 using (XmlDictionaryReader wrappedReader = new WrappedXmlDictionaryReader(XmlReader.Create(sr), XmlDictionaryReaderQuotas.Max))
4701 XmlReader plaintextReader = CreatePlaintextReaderFromEncryptedData(
4703 Configuration.ServiceTokenResolver,
4704 this.KeyInfoSerializer,
4706 out encryptingCredentials);
4708 saml2NameIdentifier = this.ReadNameIdType(plaintextReader);
4709 saml2NameIdentifier.EncryptingCredentials = encryptingCredentials;
4710 foreach (EncryptedKeyIdentifierClause clause in clauses)
4712 saml2NameIdentifier.ExternalEncryptedKeys.Add(clause);
4717 return saml2NameIdentifier;
4721 /// Both <Issuer> and <NameID> are of NameIDType. This method writes
4722 /// the content of either one of those elements.
4724 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
4725 /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
4726 protected virtual void WriteNameIdType(XmlWriter writer, Saml2NameIdentifier data)
4728 // @Format - optional
4729 if (null != data.Format)
4731 writer.WriteAttributeString(Saml2Constants.Attributes.Format, data.Format.AbsoluteUri);
4734 // @NameQualifier - optional
4735 if (!string.IsNullOrEmpty(data.NameQualifier))
4737 writer.WriteAttributeString(Saml2Constants.Attributes.NameQualifier, data.NameQualifier);
4740 // @SPNameQualifier - optional
4741 if (!string.IsNullOrEmpty(data.SPNameQualifier))
4743 writer.WriteAttributeString(Saml2Constants.Attributes.SPNameQualifier, data.SPNameQualifier);
4746 // @SPProvidedId - optional
4747 if (!string.IsNullOrEmpty(data.SPProvidedId))
4749 writer.WriteAttributeString(Saml2Constants.Attributes.SPProvidedID, data.SPProvidedId);
4752 // Content is string
4753 writer.WriteString(data.Value);
4757 /// Reads the <saml:ProxyRestriction> element, or a <saml:Condition>
4758 /// element that specifies an xsi:type of saml:ProxyRestrictionType.
4761 /// In the default implementation, the maximum value of the Count attribute
4762 /// is limited to Int32.MaxValue.
4764 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2ProxyRestriction"/> element.</param>
4765 /// <returns>An instance of <see cref="Saml2ProxyRestriction"/></returns>
4766 protected virtual Saml2ProxyRestriction ReadProxyRestriction(XmlReader reader)
4770 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4773 // throw if wrong element
4774 bool isConditionElement = false;
4775 if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace))
4777 isConditionElement = true;
4779 else if (!reader.IsStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace))
4781 reader.ReadStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace);
4786 Saml2ProxyRestriction proxyRestriction = new Saml2ProxyRestriction();
4788 bool isEmpty = reader.IsEmptyElement;
4793 // @xsi:type -- if we're a <Condition> element, this declaration must be present
4794 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ProxyRestrictionType, Saml2Constants.Namespace, isConditionElement);
4796 // @Count - optional
4797 value = reader.GetAttribute(Saml2Constants.Attributes.Count);
4798 if (!string.IsNullOrEmpty(value))
4800 proxyRestriction.Count = XmlConvert.ToInt32(value);
4807 // <Audience> - 0-OO
4808 while (reader.IsStartElement(Saml2Constants.Elements.Audience, Saml2Constants.Namespace))
4810 proxyRestriction.Audiences.Add(ReadSimpleUriElement(reader));
4813 reader.ReadEndElement();
4816 return proxyRestriction;
4820 if (System.Runtime.Fx.IsFatal(e))
4823 Exception wrapped = TryWrapReadException(reader, e);
4824 if (null == wrapped)
4836 /// Writes the <saml:ProxyRestriction> element.
4838 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2ProxyRestriction"/>.</param>
4839 /// <param name="data">The <see cref="Saml2ProxyRestriction"/> to serialize.</param>
4840 protected virtual void WriteProxyRestriction(XmlWriter writer, Saml2ProxyRestriction data)
4844 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4849 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4852 writer.WriteStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace);
4854 // @Count - optional
4855 if (null != data.Count)
4857 writer.WriteAttributeString(Saml2Constants.Attributes.Count, XmlConvert.ToString(data.Count.Value));
4860 // <Audience> - 0-OO
4861 foreach (Uri uri in data.Audiences)
4863 writer.WriteElementString(Saml2Constants.Elements.Audience, uri.AbsoluteUri);
4866 writer.WriteEndElement();
4870 /// Reads the <saml:Statement> element.
4872 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Statement"/> element.</param>
4873 /// <returns>An instance of <see cref="Saml2Statement"/> derived type.</returns>
4875 /// The default implementation only handles Statement elements which
4876 /// specify an xsi:type of saml:AttributeStatementType,
4877 /// saml:AuthnStatementType, and saml:AuthzDecisionStatementType. To
4878 /// handle custom statements, override this method.
4880 protected virtual Saml2Statement ReadStatement(XmlReader reader)
4884 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4887 // throw if wrong element
4888 if (!reader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace))
4890 reader.ReadStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace);
4893 // Since Statement is an abstract type, we have to switch off the xsi:type declaration
4894 XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader);
4896 // No declaration, or declaring that this is just a "Statement", is invalid since
4897 // statement is abstract
4898 if (null == declaredType
4899 || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.StatementAbstractType, Saml2Constants.Namespace))
4901 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI));
4904 // Reroute to the known statement types if applicable
4905 if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AttributeStatementType, Saml2Constants.Namespace))
4907 return this.ReadAttributeStatement(reader);
4909 else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AuthnStatementType, Saml2Constants.Namespace))
4911 return this.ReadAuthenticationStatement(reader);
4913 else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AuthzDecisionStatementType, Saml2Constants.Namespace))
4915 return this.ReadAuthorizationDecisionStatement(reader);
4919 // Throw if we encounter an unknown concrete type
4920 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4105, declaredType.Name, declaredType.Namespace));
4925 /// Writes a Saml2Statement.
4928 /// This method may write a <saml:AttributeStatement>, <saml:AuthnStatement>
4929 /// or <saml:AuthzDecisionStatement> element. To handle custom Saml2Statement
4930 /// classes for writing a <saml:Statement> element, override this method.
4932 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Statement"/>.</param>
4933 /// <param name="data">The <see cref="Saml2Statement"/> to serialize.</param>
4934 protected virtual void WriteStatement(XmlWriter writer, Saml2Statement data)
4938 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
4943 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
4946 Saml2AttributeStatement attributeStatement = data as Saml2AttributeStatement;
4947 if (null != attributeStatement)
4949 this.WriteAttributeStatement(writer, attributeStatement);
4953 Saml2AuthenticationStatement authnStatement = data as Saml2AuthenticationStatement;
4954 if (null != authnStatement)
4956 this.WriteAuthenticationStatement(writer, authnStatement);
4960 Saml2AuthorizationDecisionStatement authzStatement = data as Saml2AuthorizationDecisionStatement;
4961 if (null != authzStatement)
4963 this.WriteAuthorizationDecisionStatement(writer, authzStatement);
4967 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
4968 new InvalidOperationException(SR.GetString(SR.ID4107, data.GetType().AssemblyQualifiedName)));
4972 /// Reads the <saml:Subject> element.
4974 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Subject"/> element.</param>
4975 /// <returns>An instance of <see cref="Saml2Subject"/> .</returns>
4977 /// The default implementation does not handle the optional
4978 /// <EncryptedID> element. To handle encryped IDs in the Subject,
4979 /// override this method.
4981 protected virtual Saml2Subject ReadSubject(XmlReader reader)
4985 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
4988 // throw if wrong element
4989 if (!reader.IsStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace))
4991 reader.ReadStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace);
4997 if (reader.IsEmptyElement)
4999 #pragma warning suppress 56504 // bogus - thinks reader.LocalName, reader.NamespaceURI need validation
5000 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
5006 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.SubjectType, Saml2Constants.Namespace);
5009 Saml2Subject subject = new Saml2Subject();
5012 // <NameID> | <EncryptedID> | <BaseID> 0-1
5013 subject.NameId = this.ReadSubjectId(reader, Saml2Constants.Elements.Subject);
5015 // <SubjectConfirmation> 0-OO
5016 while (reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace))
5018 subject.SubjectConfirmations.Add(this.ReadSubjectConfirmation(reader));
5021 reader.ReadEndElement();
5023 // Must have a NameID or a SubjectConfirmation
5024 if (null == subject.NameId && 0 == subject.SubjectConfirmations.Count)
5026 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4108));
5033 if (System.Runtime.Fx.IsFatal(e))
5036 Exception wrapped = TryWrapReadException(reader, e);
5037 if (null == wrapped)
5049 /// Writes the <saml:Subject> element.
5051 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Subject"/>.</param>
5052 /// <param name="data">The <see cref="Saml2Subject"/> to serialize.</param>
5053 protected virtual void WriteSubject(XmlWriter writer, Saml2Subject data)
5057 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5062 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5065 // If there's no ID, there has to be a SubjectConfirmation
5066 #pragma warning suppress 56506 // SubjectConfirmations is never null
5067 if (null == data.NameId && 0 == data.SubjectConfirmations.Count)
5069 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4108)));
5073 writer.WriteStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace);
5078 if (null != data.NameId)
5080 this.WriteNameId(writer, data.NameId);
5083 // <SubjectConfirmation> 0-OO
5084 foreach (Saml2SubjectConfirmation subjectConfirmation in data.SubjectConfirmations)
5086 this.WriteSubjectConfirmation(writer, subjectConfirmation);
5090 writer.WriteEndElement();
5094 /// Reads the <SubjectConfirmation> element.
5096 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectConfirmation"/> element.</param>
5097 /// <returns>An instance of <see cref="Saml2SubjectConfirmation"/> .</returns>
5098 protected virtual Saml2SubjectConfirmation ReadSubjectConfirmation(XmlReader reader)
5102 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
5105 // throw if wrong element
5106 if (!reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace))
5108 reader.ReadStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace);
5113 bool isEmpty = reader.IsEmptyElement;
5118 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.SubjectConfirmationType, Saml2Constants.Namespace);
5120 // @Method - required
5121 string method = reader.GetAttribute(Saml2Constants.Attributes.Method);
5122 if (string.IsNullOrEmpty(method))
5124 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Method, Saml2Constants.Elements.SubjectConfirmation));
5127 if (!UriUtil.CanCreateValidUri(method, UriKind.Absolute))
5129 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Method, Saml2Constants.Elements.SubjectConfirmation));
5132 // Construct the appropriate SubjectConfirmation based on the method
5133 Saml2SubjectConfirmation subjectConfirmation = new Saml2SubjectConfirmation(new Uri(method));
5139 // <NameID> | <EncryptedID> | <BaseID> 0-1
5140 subjectConfirmation.NameIdentifier = this.ReadSubjectId(reader, Saml2Constants.Elements.SubjectConfirmation);
5142 // <SubjectConfirmationData> 0-1
5143 if (reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace))
5145 subjectConfirmation.SubjectConfirmationData = this.ReadSubjectConfirmationData(reader);
5148 reader.ReadEndElement();
5151 return subjectConfirmation;
5155 if (System.Runtime.Fx.IsFatal(e))
5158 Exception wrapped = TryWrapReadException(reader, e);
5159 if (null == wrapped)
5171 /// Writes the <saml:SubjectConfirmation> element.
5173 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectConfirmation"/>.</param>
5174 /// <param name="data">The <see cref="Saml2SubjectConfirmation"/> to serialize.</param>
5175 protected virtual void WriteSubjectConfirmation(XmlWriter writer, Saml2SubjectConfirmation data)
5179 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5184 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5187 if (null == data.Method)
5189 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data.Method");
5192 if (string.IsNullOrEmpty(data.Method.ToString()))
5194 throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("data.Method");
5197 // <SubjectConfirmation>
5198 writer.WriteStartElement(Saml2Constants.Elements.SubjectConfirmation, Saml2Constants.Namespace);
5200 // @Method - required
5201 writer.WriteAttributeString(Saml2Constants.Attributes.Method, data.Method.AbsoluteUri);
5204 if (null != data.NameIdentifier)
5206 this.WriteNameId(writer, data.NameIdentifier);
5209 // <SubjectConfirmationData> 0-1
5210 if (null != data.SubjectConfirmationData)
5212 this.WriteSubjectConfirmationData(writer, data.SubjectConfirmationData);
5215 // </SubjectConfirmation>
5216 writer.WriteEndElement();
5220 /// Reads the <saml:SubjectConfirmationData> element.
5222 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectConfirmationData"/> element.</param>
5223 /// <returns>An instance of <see cref="Saml2SubjectConfirmationData"/> .</returns>
5225 /// The default implementation handles the unextended element
5226 /// as well as the extended type saml:KeyInfoConfirmationDataType.
5228 protected virtual Saml2SubjectConfirmationData ReadSubjectConfirmationData(XmlReader reader)
5232 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
5235 if (!reader.IsStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace))
5237 reader.ReadStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace);
5242 Saml2SubjectConfirmationData confirmationData = new Saml2SubjectConfirmationData();
5243 bool isEmpty = reader.IsEmptyElement;
5249 bool requireKeyInfo = false;
5250 XmlQualifiedName type = XmlUtil.GetXsiType(reader);
5254 if (XmlUtil.EqualsQName(type, Saml2Constants.Types.KeyInfoConfirmationDataType, Saml2Constants.Namespace))
5256 requireKeyInfo = true;
5258 else if (!XmlUtil.EqualsQName(type, Saml2Constants.Types.SubjectConfirmationDataType, Saml2Constants.Namespace))
5260 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4112, type.Name, type.Namespace));
5264 // KeyInfoConfirmationData cannot be empty
5265 if (requireKeyInfo && isEmpty)
5267 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.GetString(SR.ID4111)));
5270 // @Address - optional
5271 value = reader.GetAttribute(Saml2Constants.Attributes.Address);
5272 if (!string.IsNullOrEmpty(value))
5274 confirmationData.Address = value;
5277 // @InResponseTo - optional
5278 value = reader.GetAttribute(Saml2Constants.Attributes.InResponseTo);
5279 if (!string.IsNullOrEmpty(value))
5281 confirmationData.InResponseTo = new Saml2Id(value);
5284 // @NotBefore - optional
5285 value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore);
5286 if (!string.IsNullOrEmpty(value))
5288 confirmationData.NotBefore = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
5291 // @NotOnOrAfter - optional
5292 value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter);
5293 if (!string.IsNullOrEmpty(value))
5295 confirmationData.NotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted);
5298 // @Recipient - optional
5299 value = reader.GetAttribute(Saml2Constants.Attributes.Recipient);
5300 if (!string.IsNullOrEmpty(value))
5302 if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
5304 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Recipient, Saml2Constants.Elements.SubjectConfirmationData));
5307 confirmationData.Recipient = new Uri(value);
5315 // <ds:KeyInfo> 0-OO OR 1-OO
5318 confirmationData.KeyIdentifiers.Add(this.ReadSubjectKeyInfo(reader));
5321 while (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace))
5323 confirmationData.KeyIdentifiers.Add(this.ReadSubjectKeyInfo(reader));
5326 // If this isn't KeyInfo restricted, there might be open content here ...
5327 if (!requireKeyInfo && XmlNodeType.EndElement != reader.NodeType)
5329 // So throw and tell the user how to handle the open content
5330 throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4114, Saml2Constants.Elements.SubjectConfirmationData));
5333 reader.ReadEndElement();
5336 return confirmationData;
5340 if (System.Runtime.Fx.IsFatal(e))
5343 Exception wrapped = TryWrapReadException(reader, e);
5344 if (null == wrapped)
5356 /// Writes the <saml:SubjectConfirmationData> element.
5359 /// When the data.KeyIdentifiers collection is not empty, an xsi:type
5360 /// attribute will be written specifying saml:KeyInfoConfirmationDataType.
5362 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectConfirmationData"/>.</param>
5363 /// <param name="data">The <see cref="Saml2SubjectConfirmationData"/> to serialize.</param>
5364 protected virtual void WriteSubjectConfirmationData(XmlWriter writer, Saml2SubjectConfirmationData data)
5368 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5373 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5376 // <SubjectConfirmationData>
5377 writer.WriteStartElement(Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace);
5382 if (data.KeyIdentifiers != null && data.KeyIdentifiers.Count > 0)
5384 writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, Saml2Constants.Types.KeyInfoConfirmationDataType);
5387 // @Address - optional
5388 if (!string.IsNullOrEmpty(data.Address))
5390 writer.WriteAttributeString(Saml2Constants.Attributes.Address, data.Address);
5393 // @InResponseTo - optional
5394 if (null != data.InResponseTo)
5396 writer.WriteAttributeString(Saml2Constants.Attributes.InResponseTo, data.InResponseTo.Value);
5399 // @NotBefore - optional
5400 if (null != data.NotBefore)
5402 writer.WriteAttributeString(Saml2Constants.Attributes.NotBefore, XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated));
5405 // @NotOnOrAfter - optional
5406 if (null != data.NotOnOrAfter)
5408 writer.WriteAttributeString(Saml2Constants.Attributes.NotOnOrAfter, XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated));
5411 // @Recipient - optional
5412 if (null != data.Recipient)
5414 writer.WriteAttributeString(Saml2Constants.Attributes.Recipient, data.Recipient.OriginalString);
5419 // <ds:KeyInfo> 0-OO
5420 foreach (SecurityKeyIdentifier keyIdentifier in data.KeyIdentifiers)
5422 this.WriteSubjectKeyInfo(writer, keyIdentifier);
5425 // </SubjectConfirmationData>
5426 writer.WriteEndElement();
5430 /// Reads the <saml:SubjectLocality> element.
5432 /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2SubjectLocality"/> element.</param>
5433 /// <returns>An instance of <see cref="Saml2SubjectLocality"/> .</returns>
5434 protected virtual Saml2SubjectLocality ReadSubjectLocality(XmlReader reader)
5438 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
5441 // throw if wrong element
5442 if (!reader.IsStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace))
5444 reader.ReadStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace);
5449 Saml2SubjectLocality subjectLocality = new Saml2SubjectLocality();
5450 bool isEmpty = reader.IsEmptyElement;
5455 XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.SubjectLocalityType, Saml2Constants.Namespace);
5457 // @Address - optional
5458 subjectLocality.Address = reader.GetAttribute(Saml2Constants.Attributes.Address);
5460 // @DNSName - optional
5461 subjectLocality.DnsName = reader.GetAttribute(Saml2Constants.Attributes.DNSName);
5467 reader.ReadEndElement();
5470 return subjectLocality;
5474 if (System.Runtime.Fx.IsFatal(e))
5477 Exception wrapped = TryWrapReadException(reader, e);
5478 if (null == wrapped)
5490 /// Writes the <saml:SubjectLocality> element.
5492 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2SubjectLocality"/>.</param>
5493 /// <param name="data">The <see cref="Saml2SubjectLocality"/> to serialize.</param>
5494 protected virtual void WriteSubjectLocality(XmlWriter writer, Saml2SubjectLocality data)
5498 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
5503 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
5506 // <SubjectLocality>
5507 writer.WriteStartElement(Saml2Constants.Elements.SubjectLocality, Saml2Constants.Namespace);
5509 // @Address - optional
5510 if (null != data.Address)
5512 writer.WriteAttributeString(Saml2Constants.Attributes.Address, data.Address);
5515 // @DNSName - optional
5516 if (null != data.DnsName)
5518 writer.WriteAttributeString(Saml2Constants.Attributes.DNSName, data.DnsName);
5521 // </SubjectLocality>
5522 writer.WriteEndElement();
5525 // This thin wrapper is used to pass a serializer down into the
5526 // EnvelpoedSignatureReader that will use the Saml2AssertionSerializer's
5527 // ReadKeyInfo method to read the KeyInfo.
5528 internal class WrappedSerializer : SecurityTokenSerializer
5530 private Saml2SecurityTokenHandler parent;
5531 private Saml2Assertion assertion;
5533 public WrappedSerializer(Saml2SecurityTokenHandler parent, Saml2Assertion assertion)
5535 this.assertion = assertion;
5536 this.parent = parent;
5539 protected override bool CanReadKeyIdentifierClauseCore(XmlReader reader)
5544 protected override bool CanReadKeyIdentifierCore(XmlReader reader)
5549 protected override bool CanReadTokenCore(XmlReader reader)
5554 protected override bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause)
5559 protected override bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier)
5564 protected override bool CanWriteTokenCore(SecurityToken token)
5569 protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
5571 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5574 protected override SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader)
5576 return this.parent.ReadSigningKeyInfo(reader, this.assertion);
5579 protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
5581 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5585 /// Extensibility point for providing custom serialization.
5587 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifierClause"/>.</param>
5588 /// <param name="keyIdentifierClause">The <see cref="SecurityKeyIdentifierClause"/> to serialize.</param>
5589 /// <remarks>This is not supported.</remarks>
5590 protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
5592 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5596 /// Extensibility point for providing custom serialization.
5598 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityKeyIdentifier"/>.</param>
5599 /// <param name="keyIdentifier">The <see cref="SecurityKeyIdentifier"/> to serialize.</param>
5600 protected override void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier)
5602 this.parent.WriteSigningKeyInfo(writer, keyIdentifier);
5606 /// Extensibility point for providing custom serialization.
5608 /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="SecurityToken"/>.</param>
5609 /// <param name="token">The <see cref="SecurityToken"/> to serialize.</param>
5610 protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
5612 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
5617 /// When encrypted SAML 2.0 token is received, the credentials that are used
5618 /// to encrypt the token will be hydrated as a ReceivedEncryptingCredentials.
5619 /// This is to distinguish the case between a user explicitly setting an
5620 /// encrypting credentials and a re-serialize case where a received token
5621 /// is re-serialized by a proxy to a backend service, in which case the token
5622 /// should not be encrypted.
5624 internal class ReceivedEncryptingCredentials : EncryptingCredentials
5627 /// Constructs an ReceivedEncryptingCredentials with a security key, a security key identifier and
5628 /// the encryption algorithm.
5630 /// <param name="key">A security key for encryption.</param>
5631 /// <param name="keyIdentifier">A security key identifier for the encryption key.</param>
5632 /// <param name="algorithm">The encryption algorithm.</param>
5633 public ReceivedEncryptingCredentials(SecurityKey key, SecurityKeyIdentifier keyIdentifier, string algorithm)
5634 : base(key, keyIdentifier, algorithm)