/// <summary> /// This gets called on the relying party on deserialization. /// /// The token looks like this /// <MyDecisionToken Id="someId here"> /// <Decision>True</Decision> /// <Key>key bytes</Key> /// <Signature>...</Signature> /// </MyDecisionToken> /// This sample is expecting the token in the right format. Omit some xml validation for simplicity. /// </summary> /// <param name="reader">The xml reader.</param> /// <returns>The security token.</returns> /// <exception cref="InvalidOperationException">When the token cannot be read.</exception> public override SecurityToken ReadToken(XmlReader reader) { Console.WriteLine("CustomTokenHandler.ReadToken called"); if (!CanReadToken(reader)) { throw new InvalidOperationException("Reader must be positioned at the beginning of the DecisionToken. "); } EnvelopedSignatureReader envReader = new EnvelopedSignatureReader(reader, WSSecurityTokenSerializer.DefaultInstance); // Read the Id attribute string id = envReader.GetAttribute(Id); envReader.Read(); envReader.MoveToContent(); // Read the Decision element string decision = envReader.ReadElementContentAsString(Decision, TokenNamespace); Console.WriteLine("- decision read: {0}", decision); // Read the Key element byte[] key = Convert.FromBase64String(envReader.ReadElementString(Key, TokenNamespace)); envReader.ReadEndElement(); return(new MyDecisionToken(Convert.ToBoolean(decision), envReader.SigningCredentials, key, id)); }
/// <summary> /// Reads a <saml:Assertion> element. /// </summary> /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Assertion"/> element.</param> /// <exception cref="ArgumentNullException">if <paramref name="reader"/> is null.</exception> /// <exception cref="NotSupportedException">if assertion is encrypted.</exception> /// <exception cref="Saml2SecurityTokenReadException">If <paramref name="reader"/> is not positioned at a Saml2Assertion.</exception> /// <exception cref="Saml2SecurityTokenReadException">If Version is not '2.0'.</exception> /// <exception cref="Saml2SecurityTokenReadException">If 'Id' is missing.</exception>> /// <exception cref="Saml2SecurityTokenReadException">If 'IssueInstant' is missing.</exception>> /// <exception cref="Saml2SecurityTokenReadException">If no statements are found.</exception>> /// <returns>A <see cref="Saml2Assertion"/> instance.</returns> public override Saml2Assertion ReadAssertion(XmlReader reader) { XmlUtil.CheckReaderOnEntry(reader, Saml2Constants.Elements.Assertion, Saml2Constants.Namespace); if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace)) { var encrypted = new XmlDocument(); encrypted.PreserveWhitespace = true; encrypted.Load(reader); XmlElement decrypted = null; foreach (var cert in DecryptionCertificates) { try { decrypted = encrypted.DocumentElement.Decrypt(cert.PrivateKey); break; } catch (CryptographicException) { } } if (decrypted == null) { throw new InvalidOperationException( "Encrypted assertion could not be decrypted using any available decryption certificate"); } reader = new XmlNodeReader(decrypted); } var envelopeReader = new EnvelopedSignatureReader(reader); var assertion = new Saml2Assertion(new Saml2NameIdentifier("__TemporaryIssuer__")); try { // @xsi:type XmlUtil.ValidateXsiType(envelopeReader, Saml2Constants.Types.AssertionType, Saml2Constants.Namespace); // @Version - required - must be "2.0" string version = envelopeReader.GetAttribute(Saml2Constants.Attributes.Version); if (string.IsNullOrEmpty(version)) { throw LogReadException(LogMessages.IDX13106, Saml2Constants.Elements.Assertion, Saml2Constants.Attributes.Version); } if (!StringComparer.Ordinal.Equals(Saml2Constants.Version, version)) { throw LogReadException(LogMessages.IDX13137, version); } // @ID - required string value = envelopeReader.GetAttribute(Saml2Constants.Attributes.ID); if (string.IsNullOrEmpty(value)) { throw LogReadException(LogMessages.IDX13106, Saml2Constants.Elements.Assertion, Saml2Constants.Attributes.ID); } assertion.Id = new Saml2Id(value); // @IssueInstant - required value = envelopeReader.GetAttribute(Saml2Constants.Attributes.IssueInstant); if (string.IsNullOrEmpty(value)) { throw LogReadException(LogMessages.IDX13106, Saml2Constants.Elements.Assertion, Saml2Constants.Attributes.IssueInstant); } assertion.IssueInstant = DateTime.ParseExact(value, Saml2Constants.AcceptedDateTimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime(); // will move to next element // <ds:Signature> 0-1 read by EnvelopedSignatureReader envelopeReader.Read(); // <Issuer> 1 assertion.Issuer = ReadIssuer(envelopeReader); // <Subject> 0-1 if (envelopeReader.IsStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace)) { assertion.Subject = ReadSubject(envelopeReader); } // <Conditions> 0-1 if (envelopeReader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace)) { assertion.Conditions = ReadConditions(envelopeReader); } // <Advice> 0-1 if (envelopeReader.IsStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace)) { assertion.Advice = ReadAdvice(envelopeReader); } // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO while (envelopeReader.IsStartElement()) { Saml2Statement statement; if (envelopeReader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace)) { statement = ReadStatement(envelopeReader); } else if (envelopeReader.IsStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace)) { statement = ReadAttributeStatement(envelopeReader); } else if (envelopeReader.IsStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace)) { if (IgnoreAuthenticationContext) { envelopeReader.Skip(); continue; } statement = ReadAuthenticationStatement(envelopeReader); } else if (envelopeReader.IsStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace)) { statement = ReadAuthorizationDecisionStatement(envelopeReader); } else { break; } assertion.Statements.Add(statement); } envelopeReader.ReadEndElement(); if (assertion.Subject == null) { // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585] if (0 == assertion.Statements.Count) { throw LogReadException(LogMessages.IDX13108, Saml2Constants.Elements.Assertion); } // Furthermore, the built-in statement types all require the presence of a subject. // [Saml2Core, lines 1050, 1168, 1280] foreach (Saml2Statement statement in assertion.Statements) { if (statement is Saml2AuthenticationStatement || statement is Saml2AttributeStatement || statement is Saml2AuthorizationDecisionStatement) { throw LogReadException(LogMessages.IDX13109, Saml2Constants.Elements.Assertion); } } } // attach signedXml for validation of signature assertion.Signature = envelopeReader.Signature; return(assertion); } catch (Exception ex) { if (ex is Saml2SecurityTokenReadException) { throw; } throw LogReadException(LogMessages.IDX13102, ex, Saml2Constants.Elements.Assertion, ex); } }
/// <summary> /// Reads the <saml:Assertion> element. /// </summary> /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Assertion"/> element.</param> /// <returns>A <see cref="Saml2Assertion"/> instance.</returns> protected virtual Saml2Assertion ReadAssertion(XmlReader reader) { if (null == reader) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } if (this.Configuration == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274)); } if (this.Configuration.IssuerTokenResolver == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275)); } if (this.Configuration.ServiceTokenResolver == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276)); } XmlDictionaryReader plaintextReader = XmlDictionaryReader.CreateDictionaryReader(reader); Saml2Assertion assertion = new Saml2Assertion(new Saml2NameIdentifier("__TemporaryIssuer__")); // If it's an EncryptedAssertion, we need to retrieve the plaintext // and repoint our reader if (reader.IsStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace)) { EncryptingCredentials encryptingCredentials = null; plaintextReader = CreatePlaintextReaderFromEncryptedData( plaintextReader, Configuration.ServiceTokenResolver, this.KeyInfoSerializer, assertion.ExternalEncryptedKeys, out encryptingCredentials); assertion.EncryptingCredentials = encryptingCredentials; } // Throw if wrong element if (!plaintextReader.IsStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace)) { plaintextReader.ReadStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace); } // disallow empty if (plaintextReader.IsEmptyElement) { #pragma warning suppress 56504 // bogus - thinks plaintextReader.LocalName, plaintextReader.NamespaceURI need validation throw DiagnosticUtility.ThrowHelperXml(plaintextReader, SR.GetString(SR.ID3061, plaintextReader.LocalName, plaintextReader.NamespaceURI)); } // Construct a wrapped serializer so that the EnvelopedSignatureReader's // attempt to read the <ds:KeyInfo> will hit our ReadKeyInfo virtual. WrappedSerializer wrappedSerializer = new WrappedSerializer(this, assertion); // SAML supports enveloped signature, so we need to wrap our reader. // We do not dispose this reader, since as a delegating reader it would // dispose the inner reader, which we don't properly own. EnvelopedSignatureReader realReader = new EnvelopedSignatureReader(plaintextReader, wrappedSerializer, this.Configuration.IssuerTokenResolver, false, false, false); try { // Process @attributes string value; // @xsi:type XmlUtil.ValidateXsiType(realReader, Saml2Constants.Types.AssertionType, Saml2Constants.Namespace); // @Version - required - must be "2.0" string version = realReader.GetAttribute(Saml2Constants.Attributes.Version); if (string.IsNullOrEmpty(version)) { throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Version, Saml2Constants.Elements.Assertion)); } if (!StringComparer.Ordinal.Equals(assertion.Version, version)) { throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID4100, version)); } // @ID - required value = realReader.GetAttribute(Saml2Constants.Attributes.ID); if (string.IsNullOrEmpty(value)) { throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.ID, Saml2Constants.Elements.Assertion)); } assertion.Id = new Saml2Id(value); // @IssueInstant - required value = realReader.GetAttribute(Saml2Constants.Attributes.IssueInstant); if (string.IsNullOrEmpty(value)) { throw DiagnosticUtility.ThrowHelperXml(realReader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.IssueInstant, Saml2Constants.Elements.Assertion)); } assertion.IssueInstant = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted); // Process <elements> realReader.Read(); // <Issuer> 1 assertion.Issuer = this.ReadIssuer(realReader); // <ds:Signature> 0-1 realReader.TryReadSignature(); // <Subject> 0-1 if (realReader.IsStartElement(Saml2Constants.Elements.Subject, Saml2Constants.Namespace)) { assertion.Subject = this.ReadSubject(realReader); } // <Conditions> 0-1 if (realReader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace)) { assertion.Conditions = this.ReadConditions(realReader); } // <Advice> 0-1 if (realReader.IsStartElement(Saml2Constants.Elements.Advice, Saml2Constants.Namespace)) { assertion.Advice = this.ReadAdvice(realReader); } // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO while (realReader.IsStartElement()) { Saml2Statement statement; if (realReader.IsStartElement(Saml2Constants.Elements.Statement, Saml2Constants.Namespace)) { statement = this.ReadStatement(realReader); } else if (realReader.IsStartElement(Saml2Constants.Elements.AttributeStatement, Saml2Constants.Namespace)) { statement = this.ReadAttributeStatement(realReader); } else if (realReader.IsStartElement(Saml2Constants.Elements.AuthnStatement, Saml2Constants.Namespace)) { statement = this.ReadAuthenticationStatement(realReader); } else if (realReader.IsStartElement(Saml2Constants.Elements.AuthzDecisionStatement, Saml2Constants.Namespace)) { statement = this.ReadAuthorizationDecisionStatement(realReader); } else { break; } assertion.Statements.Add(statement); } realReader.ReadEndElement(); if (null == assertion.Subject) { // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585] if (0 == assertion.Statements.Count) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4106)); } // Furthermore, the built-in statement types all require the presence of a subject. // [Saml2Core, lines 1050, 1168, 1280] foreach (Saml2Statement statement in assertion.Statements) { if (statement is Saml2AuthenticationStatement || statement is Saml2AttributeStatement || statement is Saml2AuthorizationDecisionStatement) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4119)); } } } // Reading the end element will complete the signature; // capture the signing creds assertion.SigningCredentials = realReader.SigningCredentials; // Save the captured on-the-wire data, which can then be used // to re-emit this assertion, preserving the same signature. assertion.CaptureSourceData(realReader); return assertion; } catch (Exception e) { if (System.Runtime.Fx.IsFatal(e)) throw; Exception wrapped = TryWrapReadException(realReader, e); if (null == wrapped) { throw; } else { throw wrapped; } } }
/// <summary> /// Read saml:Assertion element from the given reader. /// </summary> /// <param name="reader">XmlReader to deserialize the Assertion from.</param> /// <returns>SamlAssertion</returns> /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception> /// <exception cref="XmlException">The XmlReader is not positioned at a saml:Assertion element or the Assertion /// contains unknown child elements.</exception> protected virtual SamlAssertion ReadAssertion(XmlReader reader) { if (reader == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } if (this.Configuration == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274)); } if (this.Configuration.IssuerTokenResolver == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4275)); } SamlAssertion assertion = new SamlAssertion(); EnvelopedSignatureReader wrappedReader = new EnvelopedSignatureReader(reader, new WrappedSerializer(this, assertion), this.Configuration.IssuerTokenResolver, false, true, false); if (!wrappedReader.IsStartElement(SamlConstants.ElementNames.Assertion, SamlConstants.Namespace)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4065, SamlConstants.ElementNames.Assertion, SamlConstants.Namespace, wrappedReader.LocalName, wrappedReader.NamespaceURI))); } string attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.MajorVersion, null); if (string.IsNullOrEmpty(attributeValue)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.MajorVersion))); } int majorVersion = XmlConvert.ToInt32(attributeValue); attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.MinorVersion, null); if (string.IsNullOrEmpty(attributeValue)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.MinorVersion))); } int minorVersion = XmlConvert.ToInt32(attributeValue); if ((majorVersion != SamlConstants.MajorVersionValue) || (minorVersion != SamlConstants.MinorVersionValue)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4076, majorVersion, minorVersion, SamlConstants.MajorVersionValue, SamlConstants.MinorVersionValue))); } attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.AssertionId, null); if (string.IsNullOrEmpty(attributeValue)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.AssertionId))); } if (!XmlUtil.IsValidXmlIDValue(attributeValue)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4077, attributeValue))); } assertion.AssertionId = attributeValue; attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.Issuer, null); if (string.IsNullOrEmpty(attributeValue)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4075, SamlConstants.AttributeNames.Issuer))); } assertion.Issuer = attributeValue; attributeValue = wrappedReader.GetAttribute(SamlConstants.AttributeNames.IssueInstant, null); if (!string.IsNullOrEmpty(attributeValue)) { assertion.IssueInstant = DateTime.ParseExact( attributeValue, DateTimeFormats.Accepted, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime(); } wrappedReader.MoveToContent(); wrappedReader.Read(); if (wrappedReader.IsStartElement(SamlConstants.ElementNames.Conditions, SamlConstants.Namespace)) { assertion.Conditions = ReadConditions(wrappedReader); } if (wrappedReader.IsStartElement(SamlConstants.ElementNames.Advice, SamlConstants.Namespace)) { assertion.Advice = ReadAdvice(wrappedReader); } while (wrappedReader.IsStartElement()) { assertion.Statements.Add(ReadStatement(wrappedReader)); } if (assertion.Statements.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4078))); } wrappedReader.MoveToContent(); wrappedReader.ReadEndElement(); // Reading the end element will complete the signature; // capture the signing creds assertion.SigningCredentials = wrappedReader.SigningCredentials; // Save the captured on-the-wire data, which can then be used // to re-emit this assertion, preserving the same signature. assertion.CaptureSourceData(wrappedReader); return assertion; }