/// <summary>
 /// Returns true if the reader is pointing to an EncryptedData element.
 /// </summary>
 /// <param name="reader">The reader positioned at a security token.</param>
 /// <returns>true if the reader is positioned at EncryptedData else false.</returns>
 /// <remarks>Does not move the reader when returning false.</remarks>
 public override bool CanReadToken(XmlReader reader)
 {
     return(EncryptedDataElement.CanReadFrom(reader));
 }
        /// <summary>
        /// Reads the encrypted security token.
        /// </summary>
        /// <param name="reader">The reader from which to read the token.</param>
        /// <returns>An instance of <see cref="SecurityToken"/>.</returns>
        /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
        /// <exception cref="InvalidOperationException">One of the properties 'Configuration' or 'Configuration.ServiceTokenResolver' is null. This property is required for obtaining keys for decryption.</exception>
        /// <exception cref="SecurityTokenException">A <see cref="SecurityKeyIdentifier"/> is not found inside the xml pointed to by the reader.</exception>
        /// <exception cref="EncryptedTokenDecryptionFailedException">The <see cref="SecurityKeyIdentifier"/> found inside the xml cannot be resolved by Configuration.ServiceTokenResolver to a <see cref="SecurityKey"/>.</exception>
        /// <exception cref="SecurityTokenException">The <see cref="SecurityKeyIdentifier"/> is not a <see cref="SymmetricSecurityKey"/>.</exception>
        /// <exception cref="InvalidOperationException">The ContainingCollection (<see cref="SecurityTokenHandlerCollection"/>) is unable to find a  <see cref="SecurityTokenHandler"/> that is able to read the decrypted xml and return a <see cref="SecurityToken"/>.</exception>
        public override SecurityToken ReadToken(XmlReader reader)
        {
            if (null == reader)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }

            if (this.Configuration == null)
            {
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
            }

            if (this.Configuration.ServiceTokenResolver == null)
            {
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
            }

            //
            // Read the encrypted data element
            //
            EncryptedDataElement encryptedData = new EncryptedDataElement(KeyInfoSerializer);

            encryptedData.ReadXml(XmlDictionaryReader.CreateDictionaryReader(reader));

            //
            // All the clauses in a keyinfo must identify the same key, so we
            // can try each clause in turn and stop when one resolves.
            //
            SecurityKey decryptionKey = null;

            foreach (SecurityKeyIdentifierClause clause in encryptedData.KeyIdentifier)
            {
                this.Configuration.ServiceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey);

                if (null != decryptionKey)
                {
                    break;
                }
            }

            //
            // Try to use the SKI to create the key instead.
            //
            if (decryptionKey == null && encryptedData.KeyIdentifier.CanCreateKey)
            {
                decryptionKey = encryptedData.KeyIdentifier.CreateKey();
            }

            //
            // Fail if none of the clauses resolved or ski itself cannot create key.
            //
            if (null == decryptionKey)
            {
                EncryptedKeyIdentifierClause encryptedKeyClause;
                if (encryptedData.KeyIdentifier.TryFind <EncryptedKeyIdentifierClause>(out encryptedKeyClause))
                {
                    //
                    // System.IdentityModel.Tokens.EncryptedKeyIdentifierClause.ToString() does not print out
                    // very good information except the cipher data in this case. We have worked around that
                    // by using the token serializer to serialize the key identifier clause again.
                    //
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                              new EncryptedTokenDecryptionFailedException(
                                  SR.GetString(SR.ID4036, XmlUtil.SerializeSecurityKeyIdentifier(encryptedData.KeyIdentifier, base.ContainingCollection.KeyInfoSerializer))));
                }
                else
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                              new EncryptedTokenDecryptionFailedException(SR.GetString(SR.ID4036, encryptedData.KeyIdentifier.ToString())));
                }
            }

            //
            // Need a symmetric key
            //
            SymmetricSecurityKey symmetricKey = decryptionKey as SymmetricSecurityKey;

            if (null == symmetricKey)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                          new SecurityTokenException(SR.GetString(SR.ID4023)));
            }

            //
            // Do the actual decryption
            //
            byte[] plainText;

            using (SymmetricAlgorithm decrypter = symmetricKey.GetSymmetricAlgorithm(encryptedData.Algorithm))
            {
                plainText = encryptedData.Decrypt(decrypter);
            }

            DebugEncryptedTokenClearText(plainText, Encoding.UTF8);

            //
            // Read and return the plaintext token
            //
            using (XmlReader innerTokenReader = XmlDictionaryReader.CreateTextReader(plainText, XmlDictionaryReaderQuotas.Max))
            {
                if (this.ContainingCollection != null && this.ContainingCollection.CanReadToken(innerTokenReader))
                {
                    return(this.ContainingCollection.ReadToken(innerTokenReader));
                }
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4014, innerTokenReader.LocalName, innerTokenReader.NamespaceURI));
            }
        }
        /// <summary>
        /// Writes a <see cref="EncryptedSecurityToken"/> using the xmlWriter.
        /// </summary>
        /// <param name="writer">The XmlWriter to which the encrypted token is written.</param>
        /// <param name="token">The <see cref="SecurityToken"/> which must be an instance of <see cref="EncryptedSecurityToken"/>.</param>
        /// <exception cref="ArgumentNullException">The input prameter 'writer' is null.</exception>
        /// <exception cref="ArgumentNullException">The input prameter 'token' is null.</exception>
        /// <exception cref="ArgumentException">The <see cref="SecurityToken"/> is not an instance of <see cref="EncryptedSecurityToken"/>.</exception>
        /// <exception cref="InvalidOperationException">The property 'Configuration' is null. This property is required for obtaining keys for encryption.</exception>
        /// <exception cref="InvalidOperationException">The ContaingCollection was unable to find a <see cref="SecurityTokenHandler"/> that is able to write
        /// the <see cref="SecurityToken"/> returned by 'EncryptedSecurityToken.Token'.</exception>
        /// <exception cref="SecurityTokenException">The property 'EncryptinCredentials.SecurityKey is not a <see cref="SymmetricSecurityKey"/></exception>
        public override void WriteToken(XmlWriter writer, SecurityToken token)
        {
            if (null == writer)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }

            if (null == token)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
            }

            EncryptedSecurityToken encryptedToken = token as EncryptedSecurityToken;

            if (null == encryptedToken)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID4024));
            }

            if (this.ContainingCollection == null)
            {
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4279));
            }

            //
            // This implementation simply wraps the token in xenc:EncryptedData
            //
            EncryptedDataElement encryptedData = new EncryptedDataElement(KeyInfoSerializer);

            using (MemoryStream plaintextStream = new MemoryStream())
            {
                //
                // Buffer the plaintext
                //
                using (XmlDictionaryWriter plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false))
                {
                    SecurityTokenHandler securityTokenHandler = this.ContainingCollection[encryptedToken.Token.GetType()];
                    if (securityTokenHandler != null)
                    {
                        securityTokenHandler.WriteToken(plaintextWriter, encryptedToken.Token);
                    }
                    else
                    {
                        throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4224, encryptedToken.Token.GetType()));
                    }
                }

                //
                // Set up the EncryptedData element
                //
                EncryptingCredentials encryptingCredentials = encryptedToken.EncryptingCredentials;
                encryptedData.Type          = XmlEncryptionConstants.EncryptedDataTypes.Element;
                encryptedData.KeyIdentifier = encryptingCredentials.SecurityKeyIdentifier;
                encryptedData.Algorithm     = encryptingCredentials.Algorithm;

                //
                // Get the encryption key, which must be symmetric
                //
                SymmetricSecurityKey encryptingKey = encryptingCredentials.SecurityKey as SymmetricSecurityKey;
                if (encryptingKey == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID3064)));
                }

                //
                // Do the actual encryption
                //
                using (SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(encryptingCredentials.Algorithm))
                {
                    byte[] plainTextBytes = plaintextStream.GetBuffer();
                    DebugEncryptedTokenClearText(plainTextBytes, Encoding.UTF8);
                    encryptedData.Encrypt(symmetricAlgorithm, plainTextBytes, 0, (int)plaintextStream.Length);
                }
            }

            //
            // Write the EncryptedData element
            //
            encryptedData.WriteXml(writer, KeyInfoSerializer);
        }
        /// <summary>
        /// Writes a <see cref="EncryptedSecurityToken"/> using the xmlWriter.
        /// </summary>
        /// <param name="writer">The XmlWriter to which the encrypted token is written.</param>
        /// <param name="token">The <see cref="SecurityToken"/> which must be an instance of <see cref="EncryptedSecurityToken"/>.</param>
        /// <exception cref="ArgumentNullException">The input prameter 'writer' is null.</exception>
        /// <exception cref="ArgumentNullException">The input prameter 'token' is null.</exception>
        /// <exception cref="ArgumentException">The <see cref="SecurityToken"/> is not an instance of <see cref="EncryptedSecurityToken"/>.</exception>
        /// <exception cref="InvalidOperationException">The property 'Configuration' is null. This property is required for obtaining keys for encryption.</exception>
        /// <exception cref="InvalidOperationException">The ContaingCollection was unable to find a <see cref="SecurityTokenHandler"/> that is able to write
        /// the <see cref="SecurityToken"/> returned by 'EncryptedSecurityToken.Token'.</exception>
        /// <exception cref="SecurityTokenException">The property 'EncryptinCredentials.SecurityKey is not a <see cref="SymmetricSecurityKey"/></exception>
        public override void WriteToken(XmlWriter writer, SecurityToken token)
        {
            if (null == writer)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }

            if (null == token)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
            }

            EncryptedSecurityToken encryptedToken = token as EncryptedSecurityToken;
            if (null == encryptedToken)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID4024));
            }

            if (this.ContainingCollection == null)
            {
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4279));
            }

            //
            // This implementation simply wraps the token in xenc:EncryptedData
            //
            EncryptedDataElement encryptedData = new EncryptedDataElement(KeyInfoSerializer);
            using (MemoryStream plaintextStream = new MemoryStream())
            {
                //
                // Buffer the plaintext
                //
                using (XmlDictionaryWriter plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false))
                {
                    SecurityTokenHandler securityTokenHandler = this.ContainingCollection[encryptedToken.Token.GetType()];
                    if (securityTokenHandler != null)
                    {
                        securityTokenHandler.WriteToken(plaintextWriter, encryptedToken.Token);
                    }
                    else
                    {
                        throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4224, encryptedToken.Token.GetType()));
                    }
                }

                //
                // Set up the EncryptedData element
                //
                EncryptingCredentials encryptingCredentials = encryptedToken.EncryptingCredentials;
                encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
                encryptedData.KeyIdentifier = encryptingCredentials.SecurityKeyIdentifier;
                encryptedData.Algorithm = encryptingCredentials.Algorithm;

                //
                // Get the encryption key, which must be symmetric
                //
                SymmetricSecurityKey encryptingKey = encryptingCredentials.SecurityKey as SymmetricSecurityKey;
                if (encryptingKey == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID3064)));
                }

                //
                // Do the actual encryption
                //
                using (SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(encryptingCredentials.Algorithm))
                {
                    byte[] plainTextBytes = plaintextStream.GetBuffer();
                    DebugEncryptedTokenClearText(plainTextBytes, Encoding.UTF8);
                    encryptedData.Encrypt(symmetricAlgorithm, plainTextBytes, 0, (int)plaintextStream.Length);
                }
            }

            //
            // Write the EncryptedData element
            //
            encryptedData.WriteXml(writer, KeyInfoSerializer);
        }
        /// <summary>
        /// Reads the encrypted security token.
        /// </summary>
        /// <param name="reader">The reader from which to read the token.</param>
        /// <returns>An instance of <see cref="SecurityToken"/>.</returns>
        /// <exception cref="ArgumentNullException">Input parameter 'reader' is null.</exception>
        /// <exception cref="InvalidOperationException">One of the properties 'Configuration' or 'Configuration.ServiceTokenResolver' is null. This property is required for obtaining keys for decryption.</exception>
        /// <exception cref="SecurityTokenException">A <see cref="SecurityKeyIdentifier"/> is not found inside the xml pointed to by the reader.</exception>
        /// <exception cref="EncryptedTokenDecryptionFailedException">The <see cref="SecurityKeyIdentifier"/> found inside the xml cannot be resolved by Configuration.ServiceTokenResolver to a <see cref="SecurityKey"/>.</exception>
        /// <exception cref="SecurityTokenException">The <see cref="SecurityKeyIdentifier"/> is not a <see cref="SymmetricSecurityKey"/>.</exception>
        /// <exception cref="InvalidOperationException">The ContainingCollection (<see cref="SecurityTokenHandlerCollection"/>) is unable to find a  <see cref="SecurityTokenHandler"/> that is able to read the decrypted xml and return a <see cref="SecurityToken"/>.</exception>
        public override SecurityToken ReadToken(XmlReader reader)
        {
            if (null == reader)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }

            if (this.Configuration == null)
            {
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
            }

            if (this.Configuration.ServiceTokenResolver == null)
            {
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4276));
            }

            //
            // Read the encrypted data element
            //
            EncryptedDataElement encryptedData = new EncryptedDataElement(KeyInfoSerializer);
            encryptedData.ReadXml(XmlDictionaryReader.CreateDictionaryReader(reader));

            //
            // All the clauses in a keyinfo must identify the same key, so we 
            // can try each clause in turn and stop when one resolves.
            //
            SecurityKey decryptionKey = null;
            foreach (SecurityKeyIdentifierClause clause in encryptedData.KeyIdentifier)
            {
                this.Configuration.ServiceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey);

                if (null != decryptionKey)
                {
                    break;
                }
            }

            //
            // Try to use the SKI to create the key instead.
            //
            if (decryptionKey == null && encryptedData.KeyIdentifier.CanCreateKey)
            {
                decryptionKey = encryptedData.KeyIdentifier.CreateKey();
            }

            //
            // Fail if none of the clauses resolved or ski itself cannot create key.
            //
            if (null == decryptionKey)
            {
                EncryptedKeyIdentifierClause encryptedKeyClause;
                if (encryptedData.KeyIdentifier.TryFind<EncryptedKeyIdentifierClause>(out encryptedKeyClause))
                {
                    //
                    // System.IdentityModel.Tokens.EncryptedKeyIdentifierClause.ToString() does not print out 
                    // very good information except the cipher data in this case. We have worked around that
                    // by using the token serializer to serialize the key identifier clause again.
                    //
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new EncryptedTokenDecryptionFailedException(
                            SR.GetString(SR.ID4036, XmlUtil.SerializeSecurityKeyIdentifier(encryptedData.KeyIdentifier, base.ContainingCollection.KeyInfoSerializer))));
                }
                else
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new EncryptedTokenDecryptionFailedException(SR.GetString(SR.ID4036, encryptedData.KeyIdentifier.ToString())));
                }
            }

            //
            // Need a symmetric key
            //
            SymmetricSecurityKey symmetricKey = decryptionKey as SymmetricSecurityKey;
            if (null == symmetricKey)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new SecurityTokenException(SR.GetString(SR.ID4023)));
            }

            //
            // Do the actual decryption
            //
            byte[] plainText;

            using (SymmetricAlgorithm decrypter = symmetricKey.GetSymmetricAlgorithm(encryptedData.Algorithm))
            {
                plainText = encryptedData.Decrypt(decrypter);
            }

            DebugEncryptedTokenClearText(plainText, Encoding.UTF8);

            //
            // Read and return the plaintext token
            //
            using (XmlReader innerTokenReader = XmlDictionaryReader.CreateTextReader(plainText, XmlDictionaryReaderQuotas.Max))
            {
                if (this.ContainingCollection != null && this.ContainingCollection.CanReadToken(innerTokenReader))
                {
                    return this.ContainingCollection.ReadToken(innerTokenReader);
                }
                throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4014, innerTokenReader.LocalName, innerTokenReader.NamespaceURI));
            }
        }
        internal static XmlDictionaryReader CreatePlaintextReaderFromEncryptedData(
                        XmlDictionaryReader reader,
                        SecurityTokenResolver serviceTokenResolver,
                        SecurityTokenSerializer keyInfoSerializer,
                        Collection<EncryptedKeyIdentifierClause> clauses,
                        out EncryptingCredentials encryptingCredentials)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }

            reader.MoveToContent();
            if (reader.IsEmptyElement)
            {
#pragma warning suppress 56504 // bogus - thinks reader.LocalName, reader.NamespaceURI need validation
                throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID3061, reader.LocalName, reader.NamespaceURI));
            }

            encryptingCredentials = null;

            XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.EncryptedElementType, Saml2Constants.Namespace);

            reader.ReadStartElement();
            EncryptedDataElement encryptedData = new EncryptedDataElement(keyInfoSerializer);

            // <xenc:EncryptedData> 1
            encryptedData.ReadXml(reader);

            // <xenc:EncryptedKey> 0-oo
            reader.MoveToContent();
            while (reader.IsStartElement(XmlEncryptionConstants.Elements.EncryptedKey, XmlEncryptionConstants.Namespace))
            {
                SecurityKeyIdentifierClause skic;
                if (keyInfoSerializer.CanReadKeyIdentifierClause(reader))
                {
                    skic = keyInfoSerializer.ReadKeyIdentifierClause(reader);
                }
                else
                {
                    EncryptedKeyElement encryptedKey = new EncryptedKeyElement(keyInfoSerializer);
                    encryptedKey.ReadXml(reader);
                    skic = encryptedKey.GetClause();
                }

                EncryptedKeyIdentifierClause encryptedKeyClause = skic as EncryptedKeyIdentifierClause;
                if (null == encryptedKeyClause)
                {
                    throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4172));
                }

                clauses.Add(encryptedKeyClause);
            }

            reader.ReadEndElement();

            // Try to resolve the decryption key from both the embedded 
            // KeyInfo and any external clauses
            SecurityKey decryptionKey = null;
            SecurityKeyIdentifierClause matchingClause = null;

            foreach (SecurityKeyIdentifierClause clause in encryptedData.KeyIdentifier)
            {
                if (serviceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey))
                {
                    matchingClause = clause;
                    break;
                }
            }

            if (null == decryptionKey)
            {
                foreach (SecurityKeyIdentifierClause clause in clauses)
                {
                    if (serviceTokenResolver.TryResolveSecurityKey(clause, out decryptionKey))
                    {
                        matchingClause = clause;
                        break;
                    }
                }
            }

            if (null == decryptionKey)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new EncryptedTokenDecryptionFailedException());
            }

            // Need a symmetric key
            SymmetricSecurityKey symmetricKey = decryptionKey as SymmetricSecurityKey;
            if (null == symmetricKey)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new SecurityTokenException(SR.GetString(SR.ID4023)));
            }

            // Do the actual decryption
            SymmetricAlgorithm decryptor = symmetricKey.GetSymmetricAlgorithm(encryptedData.Algorithm);
            byte[] plainText = encryptedData.Decrypt(decryptor);

            // Save off the encrypting credentials for roundtrip
            encryptingCredentials = new ReceivedEncryptingCredentials(decryptionKey, new SecurityKeyIdentifier(matchingClause), encryptedData.Algorithm);

            return XmlDictionaryReader.CreateTextReader(plainText, reader.Quotas);
        }
        /// <summary>
        /// Writes the &lt;saml:NameID> element.
        /// </summary>
        /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2NameIdentifier"/>.</param>
        /// <param name="data">The <see cref="Saml2NameIdentifier"/> to serialize.</param>
        /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'data' is null.</exception>
        /// <exception cref="CryptographicException">Saml2NameIdentifier encrypting credentials must have a Symmetric Key specified.</exception>
        protected virtual void WriteNameId(XmlWriter writer, Saml2NameIdentifier data)
        {
            if (null == writer)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }

            if (null == data)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
            }

            // If there are encrypting credentials, then we need to encrypt the name identifier
            if (data.EncryptingCredentials != null)
            {
                EncryptingCredentials encryptingCredentials = data.EncryptingCredentials;

                // Get the encryption key, which must be symmetric
                SymmetricSecurityKey encryptingKey = encryptingCredentials.SecurityKey as SymmetricSecurityKey;
                if (encryptingKey == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3284)));
                }

                MemoryStream plaintextStream = null;
                try
                {
                    // Serialize an encrypted name ID
                    plaintextStream = new MemoryStream();

                    using (XmlWriter plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false))
                    {
                        plaintextWriter.WriteStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
                        this.WriteNameIdType(plaintextWriter, data);
                        plaintextWriter.WriteEndElement();
                    }

                    EncryptedDataElement encryptedData = new EncryptedDataElement();
                    encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
                    encryptedData.Algorithm = encryptingCredentials.Algorithm;
                    encryptedData.KeyIdentifier = encryptingCredentials.SecurityKeyIdentifier;

                    // Perform encryption
                    SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(encryptingCredentials.Algorithm);
                    encryptedData.Encrypt(symmetricAlgorithm, plaintextStream.GetBuffer(), 0, (int)plaintextStream.Length);
                    ((IDisposable)plaintextStream).Dispose();

                    writer.WriteStartElement(Saml2Constants.Elements.EncryptedID, Saml2Constants.Namespace);
                    encryptedData.WriteXml(writer, this.KeyInfoSerializer);

                    foreach (EncryptedKeyIdentifierClause clause in data.ExternalEncryptedKeys)
                    {
                        this.KeyInfoSerializer.WriteKeyIdentifierClause(writer, clause);
                    }

                    writer.WriteEndElement();
                }
                finally
                {
                    if (plaintextStream != null)
                    {
                        plaintextStream.Dispose();
                        plaintextStream = null;
                    }
                }
            }
            else
            {
                writer.WriteStartElement(Saml2Constants.Elements.NameID, Saml2Constants.Namespace);
                this.WriteNameIdType(writer, data);
                writer.WriteEndElement();
            }
        }
        /// <summary>
        /// Serializes the provided SamlAssertion to the XmlWriter.
        /// </summary>
        /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Assertion"/>.</param>
        /// <param name="data">The <see cref="Saml2Assertion"/> to serialize.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="writer"/> or <paramref name="data"/> parameters are null.</exception>
        /// <exception cref="InvalidOperationException"> The <paramref name="data"/>  has both <see cref="EncryptingCredentials"/> and <see cref="ReceivedEncryptingCredentials"/> properties null.</exception>
        /// <exception cref="InvalidOperationException">The <paramref name="data"/> must have a <see cref="Saml2Subject"/> if no <see cref="Saml2Statement"/> are present.</exception>
        /// <exception cref="InvalidOperationException">The SAML2 authentication, attribute, and authorization decision <see cref="Saml2Statement"/> require a <see cref="Saml2Subject"/>.</exception>
        /// <exception cref="CryptographicException">Token encrypting credentials must have a Symmetric Key specified.</exception>
        protected virtual void WriteAssertion(XmlWriter writer, Saml2Assertion data)
        {
            if (null == writer)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }

            if (null == data)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
            }

            XmlWriter originalWriter = writer;
            MemoryStream plaintextStream = null;
            XmlDictionaryWriter plaintextWriter = null;

            // If an EncryptingCredentials is present then check if this is not of type ReceivedEncryptinCredentials.
            // ReceivedEncryptingCredentials mean that it was credentials that were hydrated from a token received
            // on the wire. We should not directly use this while re-serializing a token.
            if ((null != data.EncryptingCredentials) && !(data.EncryptingCredentials is ReceivedEncryptingCredentials))
            {
                plaintextStream = new MemoryStream();
                writer = plaintextWriter = XmlDictionaryWriter.CreateTextWriter(plaintextStream, Encoding.UTF8, false);
            }
            else if (data.ExternalEncryptedKeys == null || data.ExternalEncryptedKeys.Count > 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4173)));
            }

            // If we've saved off the token stream, re-emit it.
            if (data.CanWriteSourceData)
            {
                data.WriteSourceData(writer);
            }
            else
            {
                // Wrap the writer if necessary for a signature
                // We do not dispose this writer, since as a delegating writer it would
                // dispose the inner writer, which we don't properly own.
                EnvelopedSignatureWriter signatureWriter = null;
                if (null != data.SigningCredentials)
                {
#pragma warning suppress 56506
                    writer = signatureWriter = new EnvelopedSignatureWriter(writer, data.SigningCredentials, data.Id.Value, new WrappedSerializer(this, data));
                }

                if (null == data.Subject)
                {
                    // An assertion with no statements MUST contain a <Subject> element. [Saml2Core, line 585]
                    if (data.Statements == null || 0 == data.Statements.Count)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(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 data.Statements)
                    {
                        if (statement is Saml2AuthenticationStatement
                            || statement is Saml2AttributeStatement
                            || statement is Saml2AuthorizationDecisionStatement)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                new InvalidOperationException(SR.GetString(SR.ID4119)));
                        }
                    }
                }

                // <Assertion>
                writer.WriteStartElement(Saml2Constants.Elements.Assertion, Saml2Constants.Namespace);

                // @ID - required
                writer.WriteAttributeString(Saml2Constants.Attributes.ID, data.Id.Value);

                // @IssueInstant - required
                writer.WriteAttributeString(Saml2Constants.Attributes.IssueInstant, XmlConvert.ToString(data.IssueInstant.ToUniversalTime(), DateTimeFormats.Generated));

                // @Version - required
                writer.WriteAttributeString(Saml2Constants.Attributes.Version, data.Version);

                // <Issuer> 1
                this.WriteIssuer(writer, data.Issuer);

                // <ds:Signature> 0-1
                if (null != signatureWriter)
                {
                    signatureWriter.WriteSignature();
                }

                // <Subject> 0-1
                if (null != data.Subject)
                {
                    this.WriteSubject(writer, data.Subject);
                }

                // <Conditions> 0-1
                if (null != data.Conditions)
                {
                    this.WriteConditions(writer, data.Conditions);
                }

                // <Advice> 0-1
                if (null != data.Advice)
                {
                    this.WriteAdvice(writer, data.Advice);
                }

                // <Statement|AuthnStatement|AuthzDecisionStatement|AttributeStatement>, 0-OO
                foreach (Saml2Statement statement in data.Statements)
                {
                    this.WriteStatement(writer, statement);
                }

                writer.WriteEndElement();
            }

            // Finish off the encryption
            if (null != plaintextWriter)
            {
                ((IDisposable)plaintextWriter).Dispose();
                plaintextWriter = null;

                EncryptedDataElement encryptedData = new EncryptedDataElement();
                encryptedData.Type = XmlEncryptionConstants.EncryptedDataTypes.Element;
                encryptedData.Algorithm = data.EncryptingCredentials.Algorithm;
                encryptedData.KeyIdentifier = data.EncryptingCredentials.SecurityKeyIdentifier;

                // Get the encryption key, which must be symmetric
                SymmetricSecurityKey encryptingKey = data.EncryptingCredentials.SecurityKey as SymmetricSecurityKey;
                if (encryptingKey == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3064)));
                }

                // Do the actual encryption
                SymmetricAlgorithm symmetricAlgorithm = encryptingKey.GetSymmetricAlgorithm(data.EncryptingCredentials.Algorithm);
                encryptedData.Encrypt(symmetricAlgorithm, plaintextStream.GetBuffer(), 0, (int)plaintextStream.Length);
                ((IDisposable)plaintextStream).Dispose();

                originalWriter.WriteStartElement(Saml2Constants.Elements.EncryptedAssertion, Saml2Constants.Namespace);
                encryptedData.WriteXml(originalWriter, this.KeyInfoSerializer);
                foreach (EncryptedKeyIdentifierClause clause in data.ExternalEncryptedKeys)
                {
                    this.KeyInfoSerializer.WriteKeyIdentifierClause(originalWriter, clause);
                }

                originalWriter.WriteEndElement();
            }
        }