public static void ForEachChild(this XmlDictionaryReader reader, Func <XmlDictionaryReader, bool> tryRead, out Signature signature)
 {
     using (var signatureReader = new EnvelopedSignatureReader(reader))
     {
         if (!signatureReader.IsEmptyElement && reader.Read())
         {
             while (signatureReader.IsStartElement())
             {
                 if (!tryRead(signatureReader))
                 {
                     signatureReader.ReadOuterXml();
                 }
             }
         }
         signatureReader.Read();
         signature = signatureReader.Signature;
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Reads a &lt;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 &lt;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;
        }