File: System\IdentityModel\Metadata\MetadataSerializer.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.IdentityModel.Metadata
{
    using System.Collections.Generic;
    using System.Globalization;
    using System.IdentityModel.Configuration;
    using System.IdentityModel.Diagnostics;
    using System.IdentityModel.Protocols.WSFederation;
    using System.IdentityModel.Protocols.WSTrust;
    using System.IdentityModel.Selectors;
    using System.IdentityModel.Tokens;
    using System.IO;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel.Security;
    using System.Text;
    using System.Xml;
    using System.Xml.Schema;
 
    /// <summary>
    /// Provides support for Metadata Serialization
    /// </summary>
    public class MetadataSerializer
    {
#pragma warning disable 1591
        public const string LanguagePrefix = "xml";
        public const string LanguageLocalName = "lang";
        public const string LanguageAttribute = LanguagePrefix + ":" + LanguageLocalName;
        public const string LanguageNamespaceUri = "http://www.w3.org/XML/1998/namespace";
#pragma warning restore 1591
 
        const string _uriReference = "_metadata";
        List<string> _trustedIssuers = new List<string>();
        SecurityTokenSerializer _tokenSerializer;
 
        /// <summary>
        /// Initializes an instance of <see cref="MetadataSerializer"/>
        /// </summary>
        public MetadataSerializer()
            : this(new KeyInfoSerializer(true))
        {
        }
 
 
        /// <summary>
        /// Initializes an instance of <see cref="MetadataSerializer"/>
        /// </summary>
        /// <param name="tokenSerializer">Security Token Serializer</param>
        /// <exception cref="ArgumentNullException">The parameter tokenSerializer is null.</exception>
        public MetadataSerializer(SecurityTokenSerializer tokenSerializer)
        {
            if (tokenSerializer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenSerializer");
            }
 
            _tokenSerializer = tokenSerializer;
            TrustedStoreLocation = IdentityConfiguration.DefaultTrustedStoreLocation;
            CertificateValidationMode = IdentityConfiguration.DefaultCertificateValidationMode;
            RevocationMode = IdentityConfiguration.DefaultRevocationMode;
        }
 
        /// <summary>
        /// Creates an application service descriptor.
        /// </summary>
        /// <returns>An application service descriptor.</returns>
        protected virtual ApplicationServiceDescriptor CreateApplicationServiceInstance()
        {
            return new ApplicationServiceDescriptor();
        }
 
        /// <summary>
        /// Creates a contact person.
        /// </summary>
        /// <returns>A contact person.</returns>
        protected virtual ContactPerson CreateContactPersonInstance()
        {
            return new ContactPerson();
        }
 
        /// <summary>
        /// Creates an endpoint.
        /// </summary>
        /// <returns>An endpoint.</returns>
        protected virtual ProtocolEndpoint CreateProtocolEndpointInstance()
        {
            return new ProtocolEndpoint();
        }
 
        /// <summary>
        /// Creates entities descriptor.
        /// </summary>
        /// <returns>The entities descriptor. </returns>
        protected virtual EntitiesDescriptor CreateEntitiesDescriptorInstance()
        {
            return new EntitiesDescriptor();
        }
 
        /// <summary>
        /// Creates an entity descriptor.
        /// </summary>
        /// <returns>The entity descriptor.</returns>
        protected virtual EntityDescriptor CreateEntityDescriptorInstance()
        {
            return new EntityDescriptor();
        }
 
        /// <summary>
        /// Creates an idpsso descriptor.
        /// </summary>
        /// <returns>An idpsso descriptor.</returns>
        protected virtual IdentityProviderSingleSignOnDescriptor CreateIdentityProviderSingleSignOnDescriptorInstance()
        {
            return new IdentityProviderSingleSignOnDescriptor();
        }
 
        /// <summary>
        /// Creates an indexed enpoint.
        /// </summary>
        /// <returns>An indexed endpoint.</returns>
        protected virtual IndexedProtocolEndpoint CreateIndexedProtocolEndpointInstance()
        {
            return new IndexedProtocolEndpoint();
        }
 
        /// <summary>
        /// Creates a key descriptor.
        /// </summary>
        /// <returns>The key descriptor.</returns>
        protected virtual KeyDescriptor CreateKeyDescriptorInstance()
        {
            return new KeyDescriptor();
        }
 
        /// <summary>
        /// Creates a localized name.
        /// </summary>
        /// <returns>The localized name.</returns>
        protected virtual LocalizedName CreateLocalizedNameInstance()
        {
            return new LocalizedName();
        }
 
        /// <summary>
        /// Creates a localized uri.
        /// </summary>
        /// <returns>A localized uri.</returns>
        protected virtual LocalizedUri CreateLocalizedUriInstance()
        {
            return new LocalizedUri();
        }
 
        /// <summary>
        /// Creates an organization.
        /// </summary>
        /// <returns>An organization.</returns>
        protected virtual Organization CreateOrganizationInstance()
        {
            return new Organization();
        }
 
        /// <summary>
        /// Creates a security token service descriptor.
        /// </summary>
        /// <returns>A security token service descriptor.</returns>
        protected virtual SecurityTokenServiceDescriptor CreateSecurityTokenServiceDescriptorInstance()
        {
            return new SecurityTokenServiceDescriptor();
        }
 
        /// <summary>
        /// Creates an Spsso descriptor.
        /// </summary>
        /// <returns>An Spsso descriptor.</returns>
        protected virtual ServiceProviderSingleSignOnDescriptor CreateServiceProviderSingleSignOnDescriptorInstance()
        {
            return new ServiceProviderSingleSignOnDescriptor();
        }
 
        /// <summary>
        /// Returns the respective contact type from a string contact type.
        /// </summary>
        /// <param name="conactType">The string type.</param>
        /// <param name="found">If found a match.</param>
        /// <returns>The contact type.</returns>
        private static ContactType GetContactPersonType(string conactType, out bool found)
        {
            if (conactType == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("conactType");
            }
            found = true;
            if (StringComparer.Ordinal.Equals(conactType, "unspecified"))
            {
                return ContactType.Unspecified;
            }
            else if (StringComparer.Ordinal.Equals(conactType, "administrative"))
            {
                return ContactType.Administrative;
            }
            else if (StringComparer.Ordinal.Equals(conactType, "billing"))
            {
                return ContactType.Billing;
            }
            else if (StringComparer.Ordinal.Equals(conactType, "other"))
            {
                return ContactType.Other;
            }
            else if (StringComparer.Ordinal.Equals(conactType, "support"))
            {
                return ContactType.Support;
            }
            else if (StringComparer.Ordinal.Equals(conactType, "technical"))
            {
                return ContactType.Technical;
            }
            found = false;
            return ContactType.Unspecified;
 
        }
 
        private static KeyType GetKeyDescriptorType(string keyType)
        {
            if (keyType == null)
            {
                return KeyType.Unspecified;
            }
            else if (StringComparer.Ordinal.Equals(keyType, "encryption"))
            {
                return KeyType.Encryption;
            }
            else if (StringComparer.Ordinal.Equals(keyType, "signing"))
            {
                return KeyType.Signing;
            }
 
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.Use, keyType)));
        }
 
        /// <summary>
        /// Reads application service descriptor.
        /// </summary>
        /// <param name="reader">Xml reader.</param>
        /// <returns>An application service descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual ApplicationServiceDescriptor ReadApplicationServiceDescriptor(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            ApplicationServiceDescriptor appService = CreateApplicationServiceInstance();
            ReadWebServiceDescriptorAttributes(reader, appService);
            ReadCustomAttributes<ApplicationServiceDescriptor>(reader, appService);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(FederationMetadataConstants.Elements.ApplicationServiceEndpoint, FederationMetadataConstants.Namespace))
                    {
                        isEmpty = reader.IsEmptyElement;
                        reader.ReadStartElement();
                        if (!isEmpty && reader.IsStartElement())
                        {
                            EndpointReference address = EndpointReference.ReadFrom(reader);
                            appService.Endpoints.Add(address);
                            reader.ReadEndElement();
                        }
                    }
                    else if (reader.IsStartElement(FederationMetadataConstants.Elements.PassiveRequestorEndpoint, FederationMetadataConstants.Namespace))
                    {
                        isEmpty = reader.IsEmptyElement;
                        reader.ReadStartElement();
                        if (!isEmpty && reader.IsStartElement())
                        {
                            EndpointReference address = EndpointReference.ReadFrom(reader);
                            appService.PassiveRequestorEndpoints.Add(address);
                            reader.ReadEndElement();
                        }
                    }
                    else if (ReadWebServiceDescriptorElement(reader, appService))
                    {
                        // Do nothing
                    }
                    else if (ReadCustomElement<ApplicationServiceDescriptor>(reader, appService))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
 
                // SecurityTokenService
                reader.ReadEndElement();
            }
 
            return appService;
        }
 
        /// <summary>
        /// Reads a contact person.
        /// </summary>
        /// <param name="reader">Xml reader.</param>
        /// <returns>A contact person.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual ContactPerson ReadContactPerson(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            ContactPerson person = CreateContactPersonInstance();
 
            string contactType = reader.GetAttribute(Saml2MetadataConstants.Attributes.ContactType, null);
            bool foundKey = false;
 
            person.Type = GetContactPersonType(contactType, out foundKey);
            if (!foundKey)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3201, typeof(ContactType), contactType)));
            }
 
            ReadCustomAttributes<ContactPerson>(reader, person);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement(); // <ContactPerson>
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(Saml2MetadataConstants.Elements.Company, Saml2MetadataConstants.Namespace))
                    {
                        person.Company = reader.ReadElementContentAsString(Saml2MetadataConstants.Elements.Company, Saml2MetadataConstants.Namespace);
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.GivenName, Saml2MetadataConstants.Namespace))
                    {
                        person.GivenName = reader.ReadElementContentAsString(Saml2MetadataConstants.Elements.GivenName, Saml2MetadataConstants.Namespace);
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.Surname, Saml2MetadataConstants.Namespace))
                    {
                        person.Surname = reader.ReadElementContentAsString(Saml2MetadataConstants.Elements.Surname, Saml2MetadataConstants.Namespace);
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.EmailAddress, Saml2MetadataConstants.Namespace))
                    {
                        string emailId = reader.ReadElementContentAsString(Saml2MetadataConstants.Elements.EmailAddress, Saml2MetadataConstants.Namespace);
                        if (!String.IsNullOrEmpty(emailId))
                        {
                            person.EmailAddresses.Add(emailId);
                        }
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.TelephoneNumber, Saml2MetadataConstants.Namespace))
                    {
                        string phone = reader.ReadElementContentAsString(Saml2MetadataConstants.Elements.TelephoneNumber, Saml2MetadataConstants.Namespace);
                        if (!String.IsNullOrEmpty(phone))
                        {
                            person.TelephoneNumbers.Add(phone);
                        }
                    }
                    else if (ReadCustomElement<ContactPerson>(reader, person))
                    {
                        // Do nothing
                    }
                    else
                    {
                        reader.Skip();
                    }
 
                }
                reader.ReadEndElement(); // </ContactPerson>
            }
 
            // No mandatory elements to be validated.
            return person;
        }
 
        /// <summary>
        /// Extensibility point for reading custom attributes.
        /// </summary>
        /// <typeparam name="T">The type of element.</typeparam>
        /// <param name="reader">Xml reader.</param>
        /// <param name="target">An object of type T.</param>
        protected virtual void ReadCustomAttributes<T>(XmlReader reader, T target)
        {
            // Extensibility point only. Do Nothing.
        }
 
        /// <summary>
        /// Extensibility point for reading custom elements. By default this returns false.
        /// </summary>
        /// <typeparam name="T">The type of element.</typeparam>
        /// <param name="reader">Xml reader.</param>
        /// <param name="target">An object of type T.</param>
        /// <returns>True if an element of type T is read, else false.</returns>
        protected virtual bool ReadCustomElement<T>(XmlReader reader, T target)
        {
            // Extensibility point only. Do Nothing.
            return false;
        }
 
        /// <summary>
        /// Extensibility point for reading custom RoleDescriptors.
        /// </summary>
        /// <param name="xsiType">The xsi type</param>
        /// <param name="reader">Xml reader</param>
        /// <param name="entityDescriptor">The entity descriptor for adding the Role Descriptors</param>
        protected virtual void ReadCustomRoleDescriptor(string xsiType, XmlReader reader, EntityDescriptor entityDescriptor)
        {
            //
            // Extensibility point: Based on the xsiType, overriden implementations have the ability to read the RoleDescriptor 
            // attributes from a (##other) namespace and add the Role Descriptors to the entityDescriptor
            //
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            TraceUtility.TraceString(System.Diagnostics.TraceEventType.Warning, SR.GetString(SR.ID3274, xsiType));
            reader.Skip();
        }
 
        /// <summary>
        /// Returns the <see cref="DisplayClaim"/> from the <paramref name="reader"/>.
        /// </summary>
        /// <param name="reader">XML reader.</param>
        /// <returns>The display claim.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        /// <exception cref="MetadataSerializationException">Thrown if the XML is not well-formed.</exception>
        protected virtual DisplayClaim ReadDisplayClaim(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            //
            // This is out of scope for extensibility.
            //
            string claimType = reader.GetAttribute(WSFederationMetadataConstants.Attributes.Uri, null);
            if (!UriUtil.CanCreateValidUri(claimType, UriKind.Absolute))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, WSAuthorizationConstants.Elements.ClaimType, claimType)));
            }
            DisplayClaim claim = new DisplayClaim(claimType);
 
            bool isOptional = true;
            string optionalString = reader.GetAttribute(WSFederationMetadataConstants.Attributes.Optional);
            if (!String.IsNullOrEmpty(optionalString))
            {
                try
                {
                    isOptional = XmlConvert.ToBoolean(optionalString.ToLowerInvariant());
                }
                catch (FormatException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, WSFederationMetadataConstants.Attributes.Optional, optionalString)));
                }
            }
            claim.Optional = isOptional;
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(WSAuthorizationConstants.Elements.DisplayName, WSAuthorizationConstants.Namespace))
                    {
                        claim.DisplayTag = reader.ReadElementContentAsString(WSAuthorizationConstants.Elements.DisplayName, WSAuthorizationConstants.Namespace);
                    }
                    else if (reader.IsStartElement(WSAuthorizationConstants.Elements.Description, WSAuthorizationConstants.Namespace))
                    {
                        claim.Description = reader.ReadElementContentAsString(WSAuthorizationConstants.Elements.Description, WSAuthorizationConstants.Namespace);
                    }
                    else
                    {
                        // Move on
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
            return claim;
        }
 
        /// <summary>
        /// Reads entities descriptor.
        /// </summary>
        /// <param name="reader">Xml reader.</param>
        /// <param name="tokenResolver">The security token resolver.</param>
        /// <returns>The entities descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        /// <exception cref="MetadataSerializationException">Thrown if the XML is not well-formed.</exception>
        protected virtual EntitiesDescriptor ReadEntitiesDescriptor(XmlReader reader, SecurityTokenResolver tokenResolver)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            EntitiesDescriptor resultEntityGroup = CreateEntitiesDescriptorInstance();
 
            //
            // There may be embedded signed XML elements. So we need to plumb the SecurityTokenSerializer and tokenResolver
            //
            EnvelopedSignatureReader envelopeReader = new EnvelopedSignatureReader(reader, SecurityTokenSerializer, tokenResolver, false, false, true);
 
            string name = envelopeReader.GetAttribute(Saml2MetadataConstants.Attributes.EntityGroupName, null);
            if (!String.IsNullOrEmpty(name))
            {
                resultEntityGroup.Name = name;
            }
 
            ReadCustomAttributes<EntitiesDescriptor>(envelopeReader, resultEntityGroup);
 
            bool isEmpty = envelopeReader.IsEmptyElement;
            envelopeReader.ReadStartElement(); // <EntitiesDescriptor>
            if (!isEmpty)
            {
                while (envelopeReader.IsStartElement())
                {
                    if (envelopeReader.IsStartElement(Saml2MetadataConstants.Elements.EntityDescriptor, Saml2MetadataConstants.Namespace))
                    {
                        resultEntityGroup.ChildEntities.Add(ReadEntityDescriptor(envelopeReader, tokenResolver));
                    }
                    else if (envelopeReader.IsStartElement(Saml2MetadataConstants.Elements.EntitiesDescriptor, Saml2MetadataConstants.Namespace))
                    {
                        resultEntityGroup.ChildEntityGroups.Add(ReadEntitiesDescriptor(envelopeReader, tokenResolver));
                    }
                    else if (envelopeReader.TryReadSignature())
                    {
                        // Do nothng
                    }
                    else if (ReadCustomElement<EntitiesDescriptor>(envelopeReader, resultEntityGroup))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        envelopeReader.Skip();
                    }
 
                }
                envelopeReader.ReadEndElement(); // </EntitiesDescriptor>
            }
 
            resultEntityGroup.SigningCredentials = envelopeReader.SigningCredentials;
 
            if (resultEntityGroup.SigningCredentials != null)
            {
                ValidateSigningCredential(resultEntityGroup.SigningCredentials);
            }
 
            if (resultEntityGroup.ChildEntityGroups.Count == 0 && resultEntityGroup.ChildEntities.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3200, Saml2MetadataConstants.Elements.EntityDescriptor)));
            }
            foreach (EntityDescriptor entity in resultEntityGroup.ChildEntities)
            {
                if (!String.IsNullOrEmpty(entity.FederationId))
                {
                    if (!StringComparer.Ordinal.Equals(entity.FederationId, resultEntityGroup.Name))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, WSFederationMetadataConstants.Attributes.FederationId, entity.FederationId)));
                    }
                }
            }
            return resultEntityGroup;
        }
 
        /// <summary>
        /// Gets or sets the validation mode of the X509 certificate that is used to sign the metadata document.
        /// </summary>
        public X509CertificateValidationMode CertificateValidationMode
        {
            get;
            set;
        }
 
        /// <summary>
        /// Gets or sets the revocation mode of the X509 certificate that is used to sign the metadata document.
        /// </summary>
        public X509RevocationMode RevocationMode
        {
            get;
            set;
        }
 
        /// <summary>
        /// Gets or sets the trusted store location of the X509 certificate that is used to sign the metadata document.
        /// </summary>
        public StoreLocation TrustedStoreLocation
        {
            get;
            set;
        }
 
        /// <summary>
        /// Gets or sets the certificate validator of the X509 certificate that is used to sign the metadata document.
        /// </summary>
        public X509CertificateValidator CertificateValidator
        {
            get;
            set;
        }
 
        /// <summary>
        /// Gets the list of trusted issuer that this serializer instance trusts to sign the metadata docuemnt.
        /// </summary>
        public List<string> TrustedIssuers
        {
            get { return _trustedIssuers;  }
        }
 
        /// <summary>
        /// Validates the signing credential of the metadata document.
        /// </summary>
        /// <param name="signingCredentials">The signing credential used to sign the metadata document.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="signingCredentials"/> is null.</exception>
        protected virtual void ValidateSigningCredential(SigningCredentials signingCredentials)
        {
            if (signingCredentials == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("signingCredentials");
            }
 
            if (CertificateValidationMode != X509CertificateValidationMode.Custom)
            {
                CertificateValidator = X509Util.CreateCertificateValidator(CertificateValidationMode, RevocationMode, TrustedStoreLocation);
            }
            else if (CertificateValidationMode == X509CertificateValidationMode.Custom && CertificateValidator == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4280)));
            }
 
            X509Certificate2 certificate = GetMetadataSigningCertificate(signingCredentials.SigningKeyIdentifier);
 
            ValidateIssuer(certificate);
            CertificateValidator.Validate(certificate);
        }
 
        /// <summary>
        /// Validates the certificate that signed the metadata document against the TrustedIssuers. This method is invoked by the ValidateSigningCredential method.
        /// By default, this method does not perform any validation. Provide your own implementation to perform trusted issuer validation.
        /// </summary>
        /// <param name="signingCertificate">The signing certificate.</param>
        protected virtual void ValidateIssuer(X509Certificate2 signingCertificate)
        {
            // No-op by default.
        }
 
        /// <summary>
        /// Gets the <see cref="X509Certificate2"/> instance created from the <paramref name="ski"/>.
        /// By default, this method only supports <see cref="X509RawDataKeyIdentifierClause"/>. Override this method
        /// to support other key identifier clauses. This method is invoked by the ValidateSigningCredential method.
        /// </summary>
        /// <param name="ski">The security key identifier instance.</param>
        /// <returns>An <see cref="X509Certificate2"/> instance.</returns>
        protected virtual X509Certificate2 GetMetadataSigningCertificate( SecurityKeyIdentifier ski )
        {
            if (ski == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ski");
            }
 
            X509RawDataKeyIdentifierClause clause = null;
            if (ski.TryFind<X509RawDataKeyIdentifierClause>(out clause))
            {
                return new X509Certificate2(clause.GetX509RawData());
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID8029)));
            }
        }
 
        /// <summary>
        /// Reads an entity descriptor.
        /// </summary>
        /// <param name="inputReader">The xml reader.</param>
        /// <param name="tokenResolver">The security token resolver.</param>
        /// <returns>An entity descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter inputReader is null.</exception>
        protected virtual EntityDescriptor ReadEntityDescriptor(XmlReader inputReader, SecurityTokenResolver tokenResolver)
        {
            if (inputReader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inputReader");
            }
 
            EntityDescriptor resultEntity = CreateEntityDescriptorInstance();
 
            //
            // There may be embedded signed XML elements. So we need to plumb the SecurityTokenSerializer and tokenResolver
            // IDFX 
 
            EnvelopedSignatureReader reader = new EnvelopedSignatureReader(inputReader, SecurityTokenSerializer, tokenResolver, false, false, true);
 
            // EntityID is mandatory - relaxed as per FIP 9935
            string entityId = reader.GetAttribute(Saml2MetadataConstants.Attributes.EntityId, null);
            if (!String.IsNullOrEmpty(entityId))
            {
                resultEntity.EntityId = new EntityId(entityId);
            }
 
            // FederationID is optional
            string fedId = reader.GetAttribute(WSFederationMetadataConstants.Attributes.FederationId, WSFederationMetadataConstants.Namespace);
            if (!String.IsNullOrEmpty(fedId))
            {
                resultEntity.FederationId = fedId;
            }
 
            ReadCustomAttributes<EntityDescriptor>(reader, resultEntity);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(Saml2MetadataConstants.Elements.SpssoDescriptor, Saml2MetadataConstants.Namespace))
                    {
                        resultEntity.RoleDescriptors.Add(ReadServiceProviderSingleSignOnDescriptor(reader));
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.IdpssoDescriptor, Saml2MetadataConstants.Namespace))
                    {
                        resultEntity.RoleDescriptors.Add(ReadIdentityProviderSingleSignOnDescriptor(reader));
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.RoleDescriptor, Saml2MetadataConstants.Namespace))
                    {
                        string xsiType = reader.GetAttribute("type", XmlSchema.InstanceNamespace);
 
                        if (!String.IsNullOrEmpty(xsiType))
                        {
                            int index = xsiType.IndexOf(":", 0, StringComparison.Ordinal);
                            if (index < 0)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3207, "xsi:type", Saml2MetadataConstants.Elements.RoleDescriptor, xsiType)));
                            }
                            string prefix = xsiType.Substring(0, index);
                            string ns = reader.LookupNamespace(prefix);
 
                            if (String.IsNullOrEmpty(ns))
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, prefix, ns)));
                            }
                            else if (!StringComparer.Ordinal.Equals(ns, FederationMetadataConstants.Namespace))
                            {
                                ReadCustomRoleDescriptor(xsiType, reader, resultEntity);
                            }
                            else if (StringComparer.Ordinal.Equals(xsiType, prefix + ":" + FederationMetadataConstants.Elements.ApplicationServiceType))
                            {
                                resultEntity.RoleDescriptors.Add(ReadApplicationServiceDescriptor(reader));
                            }
                            else if (StringComparer.Ordinal.Equals(xsiType, prefix + ":" + FederationMetadataConstants.Elements.SecurityTokenServiceType))
                            {
                                resultEntity.RoleDescriptors.Add(ReadSecurityTokenServiceDescriptor(reader));
                            }
                            else
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3207, "xsi:type", Saml2MetadataConstants.Elements.RoleDescriptor, xsiType)));
                            }
                        }
                        else
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID0001, "xsi:type", Saml2MetadataConstants.Elements.RoleDescriptor)));
                        }
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.Organization, Saml2MetadataConstants.Namespace))
                    {
                        resultEntity.Organization = ReadOrganization(reader);
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.ContactPerson, Saml2MetadataConstants.Namespace))
                    {
                        resultEntity.Contacts.Add(ReadContactPerson(reader));
                    }
                    else if (reader.TryReadSignature())
                    {
                        // Do nothing
                    }
                    else if (ReadCustomElement<EntityDescriptor>(reader, resultEntity))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            resultEntity.SigningCredentials = reader.SigningCredentials;
            if (resultEntity.SigningCredentials != null)
            {
                ValidateSigningCredential(resultEntity.SigningCredentials);
            }
 
            // Elements are optional. Mandatory attributes already validated.
 
            return resultEntity;
        }
 
        /// <summary>
        /// Reads an idpsso descriptor.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>An idpsso descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual IdentityProviderSingleSignOnDescriptor ReadIdentityProviderSingleSignOnDescriptor(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            IdentityProviderSingleSignOnDescriptor idpssoDescriptor = CreateIdentityProviderSingleSignOnDescriptorInstance();
            ReadSingleSignOnDescriptorAttributes(reader, idpssoDescriptor);
            ReadCustomAttributes<IdentityProviderSingleSignOnDescriptor>(reader, idpssoDescriptor);
 
            string wantAuthnRequestSignedAttribute = reader.GetAttribute(Saml2MetadataConstants.Attributes.WantAuthenticationRequestsSigned);
            if (!String.IsNullOrEmpty(wantAuthnRequestSignedAttribute))
            {
                try
                {
                    idpssoDescriptor.WantAuthenticationRequestsSigned = XmlConvert.ToBoolean(wantAuthnRequestSignedAttribute.ToLowerInvariant());
                }
                catch (FormatException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(
                        SR.ID3202, Saml2MetadataConstants.Attributes.WantAuthenticationRequestsSigned, wantAuthnRequestSignedAttribute)));
                }
            }
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(Saml2MetadataConstants.Elements.SingleSignOnService, Saml2MetadataConstants.Namespace))
                    {
                        ProtocolEndpoint endpoint = ReadProtocolEndpoint(reader);
 
                        // Relaxed check for endpoint.ResponseLocation as per FIP 9935
                        idpssoDescriptor.SingleSignOnServices.Add(endpoint);
                    }
                    else if (reader.IsStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace))
                    {
                        idpssoDescriptor.SupportedAttributes.Add(ReadAttribute(reader));
                    }
                    else if (ReadSingleSignOnDescriptorElement(reader, idpssoDescriptor))
                    {
                        // Do nothing
                    }
                    else if (ReadCustomElement<IdentityProviderSingleSignOnDescriptor>(reader, idpssoDescriptor))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            // Relaxed check for( idpssoDescriptor.SingleSignOnServices.Count == 0 ) as per FIP 9935
            return idpssoDescriptor;
        }
 
        /// <summary>
        /// Reads an indexed endpoint.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>An indexed endpoint.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual IndexedProtocolEndpoint ReadIndexedProtocolEndpoint(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            IndexedProtocolEndpoint endpoint = CreateIndexedProtocolEndpointInstance();
 
            string binding = reader.GetAttribute(Saml2MetadataConstants.Attributes.Binding, null);
            Uri bindingUri;
            if (!UriUtil.TryCreateValidUri(binding, UriKind.RelativeOrAbsolute, out bindingUri))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.Binding, binding)));
            }
            endpoint.Binding = bindingUri;
 
            string location = reader.GetAttribute(Saml2MetadataConstants.Attributes.Location, null);
            Uri locationUri;
            if (!UriUtil.TryCreateValidUri(location, UriKind.RelativeOrAbsolute, out locationUri))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.Location, location)));
            }
            endpoint.Location = locationUri;
 
            string indexStr = reader.GetAttribute(Saml2MetadataConstants.Attributes.EndpointIndex, null);
            int index;
            if (!Int32.TryParse(indexStr, out index))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.EndpointIndex, indexStr)));
            }
            endpoint.Index = index;
 
            // responseLocation is optional
            string responseLocation = reader.GetAttribute(Saml2MetadataConstants.Attributes.ResponseLocation, null);
            if (!String.IsNullOrEmpty(responseLocation))
            {
                Uri responseUri;
                if (!UriUtil.TryCreateValidUri(responseLocation, UriKind.RelativeOrAbsolute, out responseUri))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.ResponseLocation, responseLocation)));
                }
                endpoint.ResponseLocation = responseUri;
            }
 
            // isDefault is optional
            string isDefaultString = reader.GetAttribute(Saml2MetadataConstants.Attributes.EndpointIsDefault, null);
            if (!String.IsNullOrEmpty(isDefaultString))
            {
                try
                {
                    endpoint.IsDefault = XmlConvert.ToBoolean(isDefaultString.ToLowerInvariant());
                }
                catch (FormatException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.EndpointIsDefault, isDefaultString)));
                }
            }
 
            ReadCustomAttributes<IndexedProtocolEndpoint>(reader, endpoint);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (ReadCustomElement<IndexedProtocolEndpoint>(reader, endpoint))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        // Move on
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            // No elements to validate. Attributes are already validated
            return endpoint;
        }
 
        /// <summary>
        /// Reads a key descriptor.
        /// </summary>
        /// <param name="reader">Xml reader.</param>
        /// <returns>The key descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual KeyDescriptor ReadKeyDescriptor(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            KeyDescriptor resultKey = CreateKeyDescriptorInstance();
 
            // Use is optional
            string use = reader.GetAttribute(Saml2MetadataConstants.Attributes.Use, null);
            if (!String.IsNullOrEmpty(use))
            {
                resultKey.Use = GetKeyDescriptorType(use);
            }
 
            ReadCustomAttributes<KeyDescriptor>(reader, resultKey);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace))
                    {
                        resultKey.KeyInfo = SecurityTokenSerializer.ReadKeyIdentifier(reader);
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.EncryptionMethod, Saml2MetadataConstants.Namespace))
                    {
                        // Read the required algorithm attribute - relaxed as per FIP 9935
                        string algorithm = reader.GetAttribute(Saml2MetadataConstants.Attributes.Algorithm);
                        if (!String.IsNullOrEmpty(algorithm) && UriUtil.CanCreateValidUri(algorithm, UriKind.Absolute))
                        {
                            resultKey.EncryptionMethods.Add(new EncryptionMethod(new Uri(algorithm)));
                        }
 
                        isEmpty = reader.IsEmptyElement;
                        reader.ReadStartElement(Saml2MetadataConstants.Elements.EncryptionMethod, Saml2MetadataConstants.Namespace);
                        if (!isEmpty)
                        {
                            while (reader.IsStartElement())
                            {
                                if (ReadCustomElement<KeyDescriptor>(reader, resultKey))
                                {
                                    // Do nothing for now
                                }
                                else
                                {
                                    reader.Skip();
                                }
                            }
                            reader.ReadEndElement();
                        }
                    }
                    else if (ReadCustomElement<KeyDescriptor>(reader, resultKey))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            if (resultKey.KeyInfo == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3200, XmlSignatureConstants.Elements.KeyInfo)));
            }
            return resultKey;
        }
 
        /// <summary>
        /// Reads a localized name.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>A localized name.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual LocalizedName ReadLocalizedName(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            LocalizedName resultName = CreateLocalizedNameInstance();
 
            string lang = reader.GetAttribute(LanguageLocalName, LanguageNamespaceUri);
            try
            {
                resultName.Language = CultureInfo.GetCultureInfo(lang);
            }
            catch (ArgumentNullException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, LanguageLocalName, "null")));
            }
            catch (ArgumentException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, LanguageLocalName, lang)));
            }
 
            ReadCustomAttributes<LocalizedName>(reader, resultName);
 
            bool isEmpty = reader.IsEmptyElement;
            string elementName = reader.Name;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                resultName.Name = reader.ReadContentAsString();
                while (reader.IsStartElement())
                {
                    if (ReadCustomElement<LocalizedName>(reader, resultName))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        // Move on
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            if (String.IsNullOrEmpty(resultName.Name))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3200, elementName)));
            }
            return resultName;
        }
 
        /// <summary>
        /// Reads a localized uri.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>A localized uri.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual LocalizedUri ReadLocalizedUri(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            LocalizedUri resultUri = CreateLocalizedUriInstance();
 
            string lang = reader.GetAttribute(LanguageLocalName, LanguageNamespaceUri);
            try
            {
                resultUri.Language = CultureInfo.GetCultureInfo(lang);
            }
            catch (ArgumentNullException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, LanguageLocalName, "null")));
            }
            catch (ArgumentException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, LanguageLocalName, lang)));
            }
 
            ReadCustomAttributes<LocalizedUri>(reader, resultUri);
 
            bool isEmpty = reader.IsEmptyElement;
            string elementName = reader.Name;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                string uriContent = reader.ReadContentAsString();
                Uri uri;
                if (!UriUtil.TryCreateValidUri(uriContent, UriKind.RelativeOrAbsolute, out uri))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, elementName, uriContent)));
                }
                resultUri.Uri = uri;
                while (reader.IsStartElement())
                {
                    if (ReadCustomElement<LocalizedUri>(reader, resultUri))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            if (resultUri.Uri == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3200, elementName)));
            }
            return resultUri;
        }
 
        /// <summary>
        /// Reads the given stream to deserialize a FederationMetadata instance.
        /// </summary>
        /// <param name="stream">Stream to be read.</param>
        /// <returns>An FederationMetadata instance.</returns>
        /// <exception cref="ArgumentNullException">The parameter stream is null.</exception>
        public MetadataBase ReadMetadata(Stream stream)
        {
            if (stream == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
            }
            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, XmlDictionaryReaderQuotas.Max);
            return ReadMetadata(reader);
        }
 
        /// <summary>
        /// Read the given XmlReader to deserialize the FederationMetadata instance.
        /// </summary>
        /// <param name="reader">XmlReader to be read.</param>
        /// <returns>An FederationMetadata instance</returns>
        public MetadataBase ReadMetadata(XmlReader reader)
        {
            return ReadMetadata(reader, EmptySecurityTokenResolver.Instance);
        }
 
        /// <summary>
        /// Read the given XmlReader to deserialize the FederationMetadata instance.
        /// </summary>
        /// <param name="reader">XmlReader to be read.</param>
        /// <param name="tokenResolver">Token resolver to resolve the signature token.</param>
        /// <returns>An FederationMetadata instance.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        public MetadataBase ReadMetadata(XmlReader reader, SecurityTokenResolver tokenResolver)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (tokenResolver == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenResolver");
            }
            if (false == (reader is XmlDictionaryReader))
            {
                reader = XmlDictionaryReader.CreateDictionaryReader(reader);
            }
 
            MetadataBase metadata = ReadMetadataCore(reader, tokenResolver);
            return metadata;
        }
 
        /// <summary>
        /// Reads metadata.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <param name="tokenResolver">The security token resolver.</param>
        /// <returns>MetadataBase</returns>
        /// <exception cref="ArgumentNullException">The parameter reader or tokenReolver is null.</exception>
        protected virtual MetadataBase ReadMetadataCore(XmlReader reader, SecurityTokenResolver tokenResolver)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (tokenResolver == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenResolver");
            }
 
            MetadataBase metadataBase;
 
            if (reader.IsStartElement(Saml2MetadataConstants.Elements.EntitiesDescriptor, Saml2MetadataConstants.Namespace))
            {
                metadataBase = ReadEntitiesDescriptor(reader, tokenResolver);
            }
            else if (reader.IsStartElement(Saml2MetadataConstants.Elements.EntityDescriptor, Saml2MetadataConstants.Namespace))
            {
                metadataBase = ReadEntityDescriptor(reader, tokenResolver);
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3260)));
            }
 
            return metadataBase;
        }
 
        /// <summary>
        /// Reads an organization.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>An organization.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual Organization ReadOrganization(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            Organization resultOrg = CreateOrganizationInstance();
 
            ReadCustomAttributes<Organization>(reader, resultOrg);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(Saml2MetadataConstants.Elements.OrganizationName, Saml2MetadataConstants.Namespace))
                    {
                        resultOrg.Names.Add(ReadLocalizedName(reader));
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.OrganizationDisplayName, Saml2MetadataConstants.Namespace))
                    {
                        resultOrg.DisplayNames.Add(ReadLocalizedName(reader));
                    }
                    else if (reader.IsStartElement(Saml2MetadataConstants.Elements.OrganizationUrl, Saml2MetadataConstants.Namespace))
                    {
                        resultOrg.Urls.Add(ReadLocalizedUri(reader));
                    }
                    else if (ReadCustomElement<Organization>(reader, resultOrg))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
                reader.ReadEndElement();
            }
 
            // Relaxed as per FIP 9935
            // if ( resultOrg.DisplayNames.Count < 1 )
            // if ( resultOrg.Names.Count < 1 )
            // if ( resultOrg.Urls.Count < 1 )
 
            return resultOrg;
        }
 
        /// <summary>
        /// Reads an endpoint.
        /// </summary>
        /// <param name="reader">Xml reader.</param>
        /// <returns>An endpoint.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual ProtocolEndpoint ReadProtocolEndpoint(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            ProtocolEndpoint endpoint = CreateProtocolEndpointInstance();
 
            string binding = reader.GetAttribute(Saml2MetadataConstants.Attributes.Binding, null);
            Uri bindingUri;
            if (!UriUtil.TryCreateValidUri(binding, UriKind.RelativeOrAbsolute, out bindingUri))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.Binding, binding)));
            }
            endpoint.Binding = bindingUri;
 
            string location = reader.GetAttribute(Saml2MetadataConstants.Attributes.Location, null);
            Uri locationUri;
            if (!UriUtil.TryCreateValidUri(location, UriKind.RelativeOrAbsolute, out locationUri))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.Location, location)));
            }
            endpoint.Location = locationUri;
 
            string responseLocation = reader.GetAttribute(Saml2MetadataConstants.Attributes.ResponseLocation, null);
            if (!String.IsNullOrEmpty(responseLocation))
            {
                Uri responseUri;
                if (!UriUtil.TryCreateValidUri(responseLocation, UriKind.RelativeOrAbsolute, out responseUri))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.ResponseLocation, responseLocation)));
                }
                endpoint.ResponseLocation = responseUri;
            }
 
            ReadCustomAttributes<ProtocolEndpoint>(reader, endpoint);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement(); // <Endpoint>
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (!ReadCustomElement<ProtocolEndpoint>(reader, endpoint))
                    {
                        // Move on
                        reader.Skip();
                    }
                }
                reader.ReadEndElement(); // </Endpoint>
            }
 
            return endpoint;
        }
 
        /// <summary>
        /// Reads role descriptor attributes.
        /// </summary>
        /// <param name="reader">The xml reader</param>
        /// <param name="roleDescriptor">The role descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter reader/roleDescriptor/roleDescriptor.ProtocolsSupported is null.</exception>
        protected virtual void ReadRoleDescriptorAttributes(XmlReader reader, RoleDescriptor roleDescriptor)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (roleDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor");
            }
            if (roleDescriptor.ProtocolsSupported == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.ProtocolsSupported");
            }
 
            // Optional
            string validUntilString = reader.GetAttribute(Saml2MetadataConstants.Attributes.ValidUntil, null);
            if (!String.IsNullOrEmpty(validUntilString))
            {
                DateTime validUntil;
                if (!DateTime.TryParse(validUntilString, out validUntil))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.ValidUntil, validUntilString)));
                }
                roleDescriptor.ValidUntil = validUntil;
            }
 
            // Optional
            string errorUrlString = reader.GetAttribute(Saml2MetadataConstants.Attributes.ErrorUrl, null);
            if (!string.IsNullOrEmpty(errorUrlString))
            {
                Uri errorUrl;
                if (!UriUtil.TryCreateValidUri(errorUrlString, UriKind.RelativeOrAbsolute, out errorUrl))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.ErrorUrl, errorUrlString)));
                }
                roleDescriptor.ErrorUrl = errorUrl;
            }
 
            // Mandatory
            string protocols = reader.GetAttribute(Saml2MetadataConstants.Attributes.ProtocolsSupported, null);
            if (String.IsNullOrEmpty(protocols))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, Saml2MetadataConstants.Attributes.ProtocolsSupported, protocols)));
            }
            foreach (string protocol in protocols.Split(' '))
            {
                string toAdd = protocol.Trim();
                if (!String.IsNullOrEmpty(toAdd))
                {
                    roleDescriptor.ProtocolsSupported.Add(new Uri(toAdd));
                }
            }
            ReadCustomAttributes<RoleDescriptor>(reader, roleDescriptor);
        }
 
        /// <summary>
        /// Reads role descriptor element.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <param name="roleDescriptor">The role descriptor.</param>
        /// <returns>True if read.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader/roleDescriptor/roleDescriptor.Contacts/rolDescriptor.Keys is null.</exception>
        protected virtual bool ReadRoleDescriptorElement(XmlReader reader, RoleDescriptor roleDescriptor)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (roleDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor");
            }
            if (roleDescriptor.Contacts == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.Contacts");
            }
            if (roleDescriptor.Keys == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.Keys");
            }
 
            if (reader.IsStartElement(Saml2MetadataConstants.Elements.Organization, Saml2MetadataConstants.Namespace))
            {
                roleDescriptor.Organization = ReadOrganization(reader);
                return true;
            }
            else if (reader.IsStartElement(Saml2MetadataConstants.Elements.KeyDescriptor, Saml2MetadataConstants.Namespace))
            {
                roleDescriptor.Keys.Add(ReadKeyDescriptor(reader));
                return true;
            }
            else if (reader.IsStartElement(Saml2MetadataConstants.Elements.ContactPerson, Saml2MetadataConstants.Namespace))
            {
                roleDescriptor.Contacts.Add(ReadContactPerson(reader));
                return true;
            }
            else
            {
                return ReadCustomElement<RoleDescriptor>(reader, roleDescriptor);
            }
 
            // No mandatory elements. No validations
        }
 
        /// <summary>
        /// Reads security token service descriptor.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>A security token service descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        protected virtual SecurityTokenServiceDescriptor ReadSecurityTokenServiceDescriptor(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            SecurityTokenServiceDescriptor securityTokenServiceDescriptor = CreateSecurityTokenServiceDescriptorInstance();
            ReadWebServiceDescriptorAttributes(reader, securityTokenServiceDescriptor);
            ReadCustomAttributes<SecurityTokenServiceDescriptor>(reader, securityTokenServiceDescriptor);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(FederationMetadataConstants.Elements.SecurityTokenServiceEndpoint, FederationMetadataConstants.Namespace))
                    {
                        isEmpty = reader.IsEmptyElement;
                        reader.ReadStartElement();
                        if (!isEmpty && reader.IsStartElement())
                        {
                            EndpointReference address = EndpointReference.ReadFrom(reader);
                            securityTokenServiceDescriptor.SecurityTokenServiceEndpoints.Add(address);
                            reader.ReadEndElement();
                        }
                    }
                    else if (reader.IsStartElement(FederationMetadataConstants.Elements.PassiveRequestorEndpoint, FederationMetadataConstants.Namespace))
                    {
                        isEmpty = reader.IsEmptyElement;
                        reader.ReadStartElement();
                        if (!isEmpty && reader.IsStartElement())
                        {
                            EndpointReference address = EndpointReference.ReadFrom(reader);
                            securityTokenServiceDescriptor.PassiveRequestorEndpoints.Add(address);
                            reader.ReadEndElement();
                        }
                    }
                    else if (ReadWebServiceDescriptorElement(reader, securityTokenServiceDescriptor))
                    {
                        // Do nothing
                    }
                    else if (ReadCustomElement<SecurityTokenServiceDescriptor>(reader, securityTokenServiceDescriptor))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
 
                reader.ReadEndElement(); // SecurityTokenService
            }
 
            // Relaxed as per FIP 9935
            // if ( securityTokenServiceDescriptor.SecurityTokenServiceEndpoints.Count == 0 )
 
            return securityTokenServiceDescriptor;
        }
 
        /// <summary>
        /// Reads spsso descriptor.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <returns>An spsso descriptor.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader is null.</exception>
        /// <exception cref="MetadataSerializationException">The XML was invalid.</exception>
        protected virtual ServiceProviderSingleSignOnDescriptor ReadServiceProviderSingleSignOnDescriptor(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            ServiceProviderSingleSignOnDescriptor spssoDescriptor = CreateServiceProviderSingleSignOnDescriptorInstance();
 
            string authnRequestsSignedAttribute = reader.GetAttribute(Saml2MetadataConstants.Attributes.AuthenticationRequestsSigned);
            if (!String.IsNullOrEmpty(authnRequestsSignedAttribute))
            {
                try
                {
                    spssoDescriptor.AuthenticationRequestsSigned = XmlConvert.ToBoolean(authnRequestsSignedAttribute.ToLowerInvariant());
                }
                catch (FormatException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(
                        SR.ID3202, Saml2MetadataConstants.Attributes.AuthenticationRequestsSigned, authnRequestsSignedAttribute)));
                }
            }
 
            string wantAssertionsSignedAttribute = reader.GetAttribute(Saml2MetadataConstants.Attributes.WantAssertionsSigned);
            if (!String.IsNullOrEmpty(wantAssertionsSignedAttribute))
            {
                try
                {
                    spssoDescriptor.WantAssertionsSigned = XmlConvert.ToBoolean(wantAssertionsSignedAttribute.ToLowerInvariant());
                }
                catch (FormatException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(
                        SR.ID3202, Saml2MetadataConstants.Attributes.WantAssertionsSigned, wantAssertionsSignedAttribute)));
                }
            }
 
            ReadSingleSignOnDescriptorAttributes(reader, spssoDescriptor);
            ReadCustomAttributes<ServiceProviderSingleSignOnDescriptor>(reader, spssoDescriptor);
 
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmpty)
            {
                while (reader.IsStartElement())
                {
                    if (reader.IsStartElement(Saml2MetadataConstants.Elements.AssertionConsumerService, Saml2MetadataConstants.Namespace))
                    {
                        IndexedProtocolEndpoint endpoint = ReadIndexedProtocolEndpoint(reader);
                        spssoDescriptor.AssertionConsumerServices.Add(endpoint.Index, endpoint);
                    }
                    else if (ReadSingleSignOnDescriptorElement(reader, spssoDescriptor))
                    {
                        // Do nothing
                    }
                    else if (ReadCustomElement<ServiceProviderSingleSignOnDescriptor>(reader, spssoDescriptor))
                    {
                        // Do nothing.
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
 
                reader.ReadEndElement(); // SPSSODescriptor
            }
 
            // Relaxed as per FIP 9935
            // if ( spssoDescriptor.AssertionConsumerService.Count == 0 )
 
            return spssoDescriptor;
        }
 
        /// <summary>
        /// Reads sso descriptor attributes.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <param name="roleDescriptor">The sso role descriptor.</param>
        protected virtual void ReadSingleSignOnDescriptorAttributes(XmlReader reader, SingleSignOnDescriptor roleDescriptor)
        {
            ReadRoleDescriptorAttributes(reader, roleDescriptor);
            ReadCustomAttributes<SingleSignOnDescriptor>(reader, roleDescriptor);
        }
 
        /// <summary>
        /// Reads sso descriptor element.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <param name="singleSignOnDescriptor">The sso descriptor.</param>
        /// <returns>True if read.</returns>
        protected virtual bool ReadSingleSignOnDescriptorElement(XmlReader reader, SingleSignOnDescriptor singleSignOnDescriptor)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (singleSignOnDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ssoDescriptor");
            }
 
            if (ReadRoleDescriptorElement(reader, singleSignOnDescriptor))
            {
                return true;
            }
            else if (reader.IsStartElement(Saml2MetadataConstants.Elements.ArtifactResolutionService, Saml2MetadataConstants.Namespace))
            {
                IndexedProtocolEndpoint endpoint = ReadIndexedProtocolEndpoint(reader);
 
                // Relaxed check for endpoint.ResponseLocation != null as per FIP 9935
                singleSignOnDescriptor.ArtifactResolutionServices.Add(endpoint.Index, endpoint);
                return true;
            }
            else if (reader.IsStartElement(Saml2MetadataConstants.Elements.SingleLogoutService, Saml2MetadataConstants.Namespace))
            {
                singleSignOnDescriptor.SingleLogoutServices.Add(ReadProtocolEndpoint(reader));
                return true;
            }
            else if (reader.IsStartElement(Saml2MetadataConstants.Elements.NameIDFormat, Saml2MetadataConstants.Namespace))
            {
                string nameId = reader.ReadElementContentAsString(Saml2MetadataConstants.Elements.NameIDFormat, Saml2MetadataConstants.Namespace);
                if (!UriUtil.CanCreateValidUri(nameId, UriKind.Absolute))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID0014, Saml2MetadataConstants.Elements.NameIDFormat)));
                }
                singleSignOnDescriptor.NameIdentifierFormats.Add(new Uri(nameId));
                return true;
            }
            else
            {
                return ReadCustomElement<SingleSignOnDescriptor>(reader, singleSignOnDescriptor);
            }
        }
 
        /// <summary>
        /// Reads web service descriptor attributes.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <param name="roleDescriptor">The web service descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter reader/roleDescriptor is null.</exception>
        protected virtual void ReadWebServiceDescriptorAttributes(XmlReader reader, WebServiceDescriptor roleDescriptor)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (roleDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor");
            }
 
            ReadRoleDescriptorAttributes(reader, roleDescriptor);
            string displayName = reader.GetAttribute(Saml2MetadataConstants.Attributes.ServiceDisplayName, null);
            if (!String.IsNullOrEmpty(displayName))
            {
                roleDescriptor.ServiceDisplayName = displayName;
            }
            string description = reader.GetAttribute(Saml2MetadataConstants.Attributes.ServiceDescription, null);
            if (!String.IsNullOrEmpty(description))
            {
                roleDescriptor.ServiceDescription = description;
            }
            ReadCustomAttributes<WebServiceDescriptor>(reader, roleDescriptor);
 
            // All optional no validations
        }
 
        /// <summary>
        /// Reads web service descriptor element.
        /// </summary>
        /// <param name="reader">The xml reader.</param>
        /// <param name="roleDescriptor">The web service descriptor.</param>
        /// <returns>True if read.</returns>
        /// <exception cref="ArgumentNullException">The parameter reader/roleDescriptor/roleDescriptor.TargetScopes/roleDescriptor.TargetScopes/roleDescriptor.TokenTypesOffered
        /// is null.</exception>
        public virtual bool ReadWebServiceDescriptorElement(XmlReader reader, WebServiceDescriptor roleDescriptor)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            if (roleDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor");
            }
            if (roleDescriptor.TargetScopes == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.TargetScopes");
            }
            if (roleDescriptor.ClaimTypesOffered == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.TargetScopes");
            }
            if (roleDescriptor.TokenTypesOffered == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.TokenTypesOffered");
            }
 
            if (ReadRoleDescriptorElement(reader, roleDescriptor))
            {
                return true;
            }
            else if (reader.IsStartElement(FederationMetadataConstants.Elements.TargetScopes, FederationMetadataConstants.Namespace))
            {
                bool isEmpty = reader.IsEmptyElement;
                reader.ReadStartElement();
                if (!isEmpty)
                {
                    while (reader.IsStartElement())
                    {
                        roleDescriptor.TargetScopes.Add(EndpointReference.ReadFrom(reader));
                    }
 
                    reader.ReadEndElement();
                }
 
                return true;
            }
            else if (reader.IsStartElement(FederationMetadataConstants.Elements.ClaimTypesOffered, FederationMetadataConstants.Namespace))
            {
                bool isEmpty = reader.IsEmptyElement;
                reader.ReadStartElement();
                if (!isEmpty)
                {
                    while (reader.IsStartElement())
                    {
                        if (reader.IsStartElement(WSAuthorizationConstants.Elements.ClaimType, WSAuthorizationConstants.Namespace))
                        {
                            roleDescriptor.ClaimTypesOffered.Add(ReadDisplayClaim(reader));
                        }
                        else
                        {
                            // Move on
                            reader.Skip();
                        }
                    }
 
                    reader.ReadEndElement();
                }
 
                return true;
            }
            else if (reader.IsStartElement(FederationMetadataConstants.Elements.ClaimTypesRequested, FederationMetadataConstants.Namespace))
            {
                bool isEmpty = reader.IsEmptyElement;
                reader.ReadStartElement();
                if (!isEmpty)
                {
                    while (reader.IsStartElement())
                    {
                        if (reader.IsStartElement(WSAuthorizationConstants.Elements.ClaimType, WSAuthorizationConstants.Namespace))
                        {
                            roleDescriptor.ClaimTypesRequested.Add(ReadDisplayClaim(reader));
                        }
                        else
                        {
                            // Move on
                            reader.Skip();
                        }
                    }
 
                    reader.ReadEndElement();
                }
 
                return true;
            }
            else if (reader.IsStartElement(FederationMetadataConstants.Elements.TokenTypesOffered, FederationMetadataConstants.Namespace))
            {
                bool isEmpty = reader.IsEmptyElement;
                reader.ReadStartElement(FederationMetadataConstants.Elements.TokenTypesOffered, FederationMetadataConstants.Namespace);
 
                if (!isEmpty)
                {
                    while (reader.IsStartElement())
                    {
                        if (reader.IsStartElement(WSFederationMetadataConstants.Elements.TokenType, WSFederationMetadataConstants.Namespace))
                        {
                            string tokenType = reader.GetAttribute(WSFederationMetadataConstants.Attributes.Uri, null);
                            Uri tokenTypeUri;
                            if (!UriUtil.TryCreateValidUri(tokenType, UriKind.Absolute, out tokenTypeUri))
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3202, WSFederationMetadataConstants.Elements.TokenType, tokenType)));
                            }
 
                            roleDescriptor.TokenTypesOffered.Add(tokenTypeUri);
 
                            isEmpty = reader.IsEmptyElement;
                            reader.ReadStartElement(); // TokenType
                            if (!isEmpty)
                            {
                                reader.ReadEndElement(); // TokenType
                            }
                        }
                        else
                        {
                            reader.Skip();
                        }
                    }
 
                    reader.ReadEndElement(); // TokenTypeOffered
                }
                return true;
            }
            else
            {
                return ReadCustomElement<WebServiceDescriptor>(reader, roleDescriptor);
            }
 
            // All optional. No Validations needed
        }
 
        /// <summary>
        /// Gets the SecurityTokenSerializer that this instance is using to serializer 
        /// SecurityTokens.
        /// </summary>
        public SecurityTokenSerializer SecurityTokenSerializer
        {
            get
            {
                return _tokenSerializer;
            }
        }
 
        /// <summary>
        /// Writes an application service descriptor.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="appService">The application service descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/appService/appService.Endpoint/aappService.PassiveRequestorEndpoints is null.</exception>
        protected virtual void WriteApplicationServiceDescriptor(XmlWriter writer, ApplicationServiceDescriptor appService)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (appService == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("appService");
            }
 
            if (appService.Endpoints == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("appService.Endpoints");
            }
 
            if (appService.PassiveRequestorEndpoints == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("appService.PassiveRequestorEndpoints");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.RoleDescriptor, Saml2MetadataConstants.Namespace);
            writer.WriteAttributeString("xsi", "type", XmlSchema.InstanceNamespace, FederationMetadataConstants.Prefix + ":" + FederationMetadataConstants.Elements.ApplicationServiceType);
 
            writer.WriteAttributeString("xmlns", FederationMetadataConstants.Prefix, null, FederationMetadataConstants.Namespace);
 
            WriteWebServiceDescriptorAttributes(writer, appService);
            WriteCustomAttributes<ApplicationServiceDescriptor>(writer, appService);
 
            WriteWebServiceDescriptorElements(writer, appService);
 
            // Optional ApplicationServiceEndpoints
            foreach (EndpointReference epr in appService.Endpoints)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.ApplicationServiceEndpoint, FederationMetadataConstants.Namespace);
                epr.WriteTo(writer);
                writer.WriteEndElement();
            }
 
            // Optional PassiveRequestorEndpoints
            foreach (EndpointReference epr in appService.PassiveRequestorEndpoints)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.PassiveRequestorEndpoint, FederationMetadataConstants.Namespace);
                epr.WriteTo(writer);
                writer.WriteEndElement();
            }
 
            WriteCustomElements<ApplicationServiceDescriptor>(writer, appService);
 
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes a contact person.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="contactPerson">The contact person.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/contactPerson/contactPerson.EmaillAddresses/contactPerson.TelephoneNumbers is null.</exception>
        protected virtual void WriteContactPerson(XmlWriter writer, ContactPerson contactPerson)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (contactPerson == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contactPerson");
            }
 
            if (contactPerson.EmailAddresses == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contactPerson.EmailAddresses");
            }
 
            if (contactPerson.TelephoneNumbers == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contactPerson.TelephoneNumbers");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.ContactPerson, Saml2MetadataConstants.Namespace);
            if (contactPerson.Type == ContactType.Unspecified)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.ContactType)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ContactType, null, contactPerson.Type.ToString().ToLowerInvariant());
 
            WriteCustomAttributes<ContactPerson>(writer, contactPerson);
 
            if (!String.IsNullOrEmpty(contactPerson.Company))
            {
                writer.WriteElementString(Saml2MetadataConstants.Elements.Company, Saml2MetadataConstants.Namespace, contactPerson.Company);
            }
 
            if (!String.IsNullOrEmpty(contactPerson.GivenName))
            {
                writer.WriteElementString(Saml2MetadataConstants.Elements.GivenName, Saml2MetadataConstants.Namespace, contactPerson.GivenName);
            }
 
            if (!String.IsNullOrEmpty(contactPerson.Surname))
            {
                writer.WriteElementString(Saml2MetadataConstants.Elements.Surname, Saml2MetadataConstants.Namespace, contactPerson.Surname);
            }
 
            foreach (string email in contactPerson.EmailAddresses)
            {
                writer.WriteElementString(Saml2MetadataConstants.Elements.EmailAddress, Saml2MetadataConstants.Namespace, email);
            }
 
            foreach (string phone in contactPerson.TelephoneNumbers)
            {
                writer.WriteElementString(Saml2MetadataConstants.Elements.TelephoneNumber, Saml2MetadataConstants.Namespace, phone);
            }
 
            WriteCustomElements<ContactPerson>(writer, contactPerson);
 
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Extensible point to write custom attributes.
        /// </summary>
        /// <typeparam name="T">The type of the element whose attribute is being written</typeparam>
        /// <param name="writer">The xml writer.</param>
        /// <param name="source">The source element of type T.</param>
        protected virtual void WriteCustomAttributes<T>(XmlWriter writer, T source)
        {
            // Extensibility point only. Do Nothing.
        }
 
        /// <summary>
        /// Extensible point to write custom elements.
        /// </summary>
        /// <typeparam name="T">The type of element being written.</typeparam>
        /// <param name="writer">The xml writer.</param>
        /// <param name="source">The source element of type T.</param>
        protected virtual void WriteCustomElements<T>(XmlWriter writer, T source)
        {
            // Extensibility point only. Do Nothing.
        }
 
        /// <summary>
        /// Writes an endpoint.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="endpoint">The endpoint.</param>
        /// <param name="element">The xml qualified name element.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/endpoint/element is null.</exception>
        protected virtual void WriteProtocolEndpoint(XmlWriter writer, ProtocolEndpoint endpoint, XmlQualifiedName element)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (endpoint == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
            }
 
            if (element == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element");
            }
 
            writer.WriteStartElement(element.Name, element.Namespace);
            if (endpoint.Binding == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.Binding)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Binding, null, (endpoint.Binding.IsAbsoluteUri ? endpoint.Binding.AbsoluteUri : endpoint.Binding.ToString()));
 
            if (endpoint.Location == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.Location)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Location, null, (endpoint.Location.IsAbsoluteUri ? endpoint.Location.AbsoluteUri : endpoint.Location.ToString()));
 
            if (endpoint.ResponseLocation != null)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ResponseLocation, null, (endpoint.ResponseLocation.IsAbsoluteUri ? endpoint.ResponseLocation.AbsoluteUri : endpoint.ResponseLocation.ToString()));
            }
 
            WriteCustomAttributes<ProtocolEndpoint>(writer, endpoint);
 
            WriteCustomElements<ProtocolEndpoint>(writer, endpoint);
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes entities descriptor.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> to use.</param>
        /// <param name="claim">The <see cref="DisplayClaim"/> to write.</param>
        protected virtual void WriteDisplayClaim(XmlWriter writer, DisplayClaim claim)
        {
            // This is not extensible since it is defined in a different spec.
            writer.WriteStartElement(WSAuthorizationConstants.Prefix, WSAuthorizationConstants.Elements.ClaimType, WSAuthorizationConstants.Namespace);
 
            // ClaimType is mandatory
            if (String.IsNullOrEmpty(claim.ClaimType))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, WSAuthorizationConstants.Elements.ClaimType)));
            }
 
            if (!UriUtil.CanCreateValidUri(claim.ClaimType, UriKind.Absolute))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID0014, claim.ClaimType)));
            }
 
            writer.WriteAttributeString(WSFederationMetadataConstants.Attributes.Uri, claim.ClaimType);
 
            if (claim.WriteOptionalAttribute)
            {
                writer.WriteAttributeString(WSFederationMetadataConstants.Attributes.Optional, XmlConvert.ToString(claim.Optional));
            }
 
            if (!String.IsNullOrEmpty(claim.DisplayTag))
            {
                writer.WriteElementString(WSAuthorizationConstants.Prefix, WSAuthorizationConstants.Elements.DisplayName, WSAuthorizationConstants.Namespace, claim.DisplayTag);
            }
 
            if (!String.IsNullOrEmpty(claim.Description))
            {
                writer.WriteElementString(WSAuthorizationConstants.Prefix, WSAuthorizationConstants.Elements.Description, WSAuthorizationConstants.Namespace, claim.Description);
            }
 
            writer.WriteEndElement(); // ClaimType
        }
 
        /// <summary>
        /// Writes entities descriptor.
        /// </summary>
        /// <param name="inputWriter">The <see cref="XmlWriter"/> to use.</param>
        /// <param name="entitiesDescriptor">The entities descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter inputWriter/entitiesDescriptor/entitiesDescriptor.ChildEntities/entitiesDescriptor.ChildEntityGroups
        /// is null.</exception>
        protected virtual void WriteEntitiesDescriptor(XmlWriter inputWriter, EntitiesDescriptor entitiesDescriptor)
        {
            if (inputWriter == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inputWriter");
            }
 
            if (entitiesDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entitiesDescriptor");
            }
 
            if (entitiesDescriptor.ChildEntities == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entitiesDescriptor.ChildEntities");
            }
 
            if (entitiesDescriptor.ChildEntityGroups == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entitiesDescriptor.ChildEntityGroups");
            }
 
            string entityReference = "_" + Guid.NewGuid().ToString();
            XmlWriter writer = inputWriter;
            EnvelopedSignatureWriter signedWriter = null;
            if (entitiesDescriptor.SigningCredentials != null)
            {
                signedWriter = new EnvelopedSignatureWriter(inputWriter, entitiesDescriptor.SigningCredentials, entityReference, SecurityTokenSerializer);
                writer = signedWriter;
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.EntitiesDescriptor, Saml2MetadataConstants.Namespace);
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Id, null, entityReference);
 
            if (entitiesDescriptor.ChildEntities.Count == 0 && entitiesDescriptor.ChildEntityGroups.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.EntitiesDescriptor)));
            }
 
            // Ensure FederationID in all children are valid.
            foreach (EntityDescriptor entity in entitiesDescriptor.ChildEntities)
            {
                if (!String.IsNullOrEmpty(entity.FederationId))
                {
                    if (!StringComparer.Ordinal.Equals(entity.FederationId, entitiesDescriptor.Name))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, WSFederationMetadataConstants.Attributes.FederationId)));
                    }
                }
            }
 
            if (!String.IsNullOrEmpty(entitiesDescriptor.Name))
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.EntityGroupName, null, entitiesDescriptor.Name);
            }
 
            WriteCustomAttributes<EntitiesDescriptor>(writer, entitiesDescriptor);
 
            // WriteSamlMetadataBaseElements?
 
            if (null != signedWriter)
            {
                // Write the signature at the top of the sequence
                signedWriter.WriteSignature();
            }
 
            foreach (EntityDescriptor entity in entitiesDescriptor.ChildEntities)
            {
                WriteEntityDescriptor(writer, entity);
            }
 
            foreach (EntitiesDescriptor entityGroup in entitiesDescriptor.ChildEntityGroups)
            {
                WriteEntitiesDescriptor(writer, entityGroup);
            }
 
            WriteCustomElements<EntitiesDescriptor>(writer, entitiesDescriptor);
 
            writer.WriteEndElement(); // EntitiesDescriptor
        }
 
        /// <summary>
        /// Writes an entity descriptor.
        /// </summary>
        /// <param name="inputWriter">The xml writer.</param>
        /// <param name="entityDescriptor">The entity descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter inputWriter/entityDescriptor/entityDescriptor.Contacts/entityDescriptor.RoleDescriptors is null.</exception>
        protected virtual void WriteEntityDescriptor(XmlWriter inputWriter, EntityDescriptor entityDescriptor)
        {
            if (inputWriter == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inputWriter");
            }
 
            if (entityDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entityDescriptor");
            }
 
            if (entityDescriptor.Contacts == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entityDescriptor.Contacts");
            }
 
            if (entityDescriptor.RoleDescriptors == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entityDescriptor.RoleDescriptors");
            }
 
            string entityReference = "_" + Guid.NewGuid().ToString();
            XmlWriter writer = inputWriter;
            EnvelopedSignatureWriter signedWriter = null;
            if (entityDescriptor.SigningCredentials != null)
            {
                signedWriter = new EnvelopedSignatureWriter(inputWriter, entityDescriptor.SigningCredentials, entityReference, SecurityTokenSerializer);
                writer = signedWriter;
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.EntityDescriptor, Saml2MetadataConstants.Namespace);
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Id, null, entityReference);
 
            if (entityDescriptor.EntityId == null || entityDescriptor.EntityId.Id == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.EntityId)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.EntityId, null, entityDescriptor.EntityId.Id);
 
            if (!String.IsNullOrEmpty(entityDescriptor.FederationId))
            {
                writer.WriteAttributeString(WSFederationMetadataConstants.Attributes.FederationId, WSFederationMetadataConstants.Namespace, entityDescriptor.FederationId);
            }
 
            WriteCustomAttributes<EntityDescriptor>(writer, entityDescriptor);
 
            if (null != signedWriter)
            {
                // Write the signature at the top of the sequence
                signedWriter.WriteSignature();
            }
 
            if (entityDescriptor.RoleDescriptors.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.RoleDescriptor)));
            }
 
            foreach (RoleDescriptor roleDescriptor in entityDescriptor.RoleDescriptors)
            {
                ServiceProviderSingleSignOnDescriptor spDesc = roleDescriptor as ServiceProviderSingleSignOnDescriptor;
                if (spDesc != null)
                {
                    WriteServiceProviderSingleSignOnDescriptor(writer, spDesc);
                }
 
                IdentityProviderSingleSignOnDescriptor idpDesc = roleDescriptor as IdentityProviderSingleSignOnDescriptor;
                if (idpDesc != null)
                {
                    WriteIdentityProviderSingleSignOnDescriptor(writer, idpDesc);
                }
 
                ApplicationServiceDescriptor appService = roleDescriptor as ApplicationServiceDescriptor;
                if (appService != null)
                {
                    WriteApplicationServiceDescriptor(writer, appService);
                }
 
                SecurityTokenServiceDescriptor stsService = roleDescriptor as SecurityTokenServiceDescriptor;
                if (stsService != null)
                {
                    WriteSecurityTokenServiceDescriptor(writer, stsService);
                }
            }
 
            if (entityDescriptor.Organization != null)
            {
                WriteOrganization(writer, entityDescriptor.Organization);
            }
 
            foreach (ContactPerson person in entityDescriptor.Contacts)
            {
                WriteContactPerson(writer, person);
            }
 
            WriteCustomElements<EntityDescriptor>(writer, entityDescriptor);
 
            writer.WriteEndElement(); // EntityDescriptor
        }
 
        /// <summary>
        /// Writes an idpsso descriptor.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="identityProviderSingleSignOnDescriptor">The idpsso descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/idpssoDescriptor/idpssoDescriptor.SupportedAttributes/idpssoDescriptor.SingleSignOnServices
        /// is null.</exception>
        protected virtual void WriteIdentityProviderSingleSignOnDescriptor(XmlWriter writer, IdentityProviderSingleSignOnDescriptor identityProviderSingleSignOnDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (identityProviderSingleSignOnDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("idpssoDescriptor");
            }
 
            if (identityProviderSingleSignOnDescriptor.SupportedAttributes == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("idpssoDescriptor.SupportedAttributes");
            }
 
            if (identityProviderSingleSignOnDescriptor.SingleSignOnServices == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("idpssoDescriptor.SingleSignOnServices");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.IdpssoDescriptor, Saml2MetadataConstants.Namespace);
            if (identityProviderSingleSignOnDescriptor.WantAuthenticationRequestsSigned)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.WantAuthenticationRequestsSigned, null,
                    XmlConvert.ToString(identityProviderSingleSignOnDescriptor.WantAuthenticationRequestsSigned));
            }
 
            WriteSingleSignOnDescriptorAttributes(writer, identityProviderSingleSignOnDescriptor);
            WriteCustomAttributes<IdentityProviderSingleSignOnDescriptor>(writer, identityProviderSingleSignOnDescriptor);
 
            WriteSingleSignOnDescriptorElements(writer, identityProviderSingleSignOnDescriptor);
 
            // Mandatory SingleSignonServiceEndpoint
            if (identityProviderSingleSignOnDescriptor.SingleSignOnServices.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.SingleSignOnService)));
            }
 
            foreach (ProtocolEndpoint endpoint in identityProviderSingleSignOnDescriptor.SingleSignOnServices)
            {
                if (endpoint.ResponseLocation != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3249, Saml2MetadataConstants.Attributes.ResponseLocation)));
                }
 
                XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.SingleSignOnService, Saml2MetadataConstants.Namespace);
                WriteProtocolEndpoint(writer, endpoint, element);
            }
 
            // Optional SupportedAttributes
            foreach (Saml2Attribute attribute in identityProviderSingleSignOnDescriptor.SupportedAttributes)
            {
                WriteAttribute(writer, attribute);
            }
 
            WriteCustomElements<IdentityProviderSingleSignOnDescriptor>(writer, identityProviderSingleSignOnDescriptor);
 
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes an indexed endpoint.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="indexedEP">The indexed endpoint.</param>
        /// <param name="element">The xml qualified element.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/indexedEP/element is null.</exception>
        protected virtual void WriteIndexedProtocolEndpoint(XmlWriter writer, IndexedProtocolEndpoint indexedEP, XmlQualifiedName element)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (indexedEP == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("indexedEP");
            }
 
            if (element == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element");
            }
 
            writer.WriteStartElement(element.Name, element.Namespace);
            if (indexedEP.Binding == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.Binding)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Binding, null, (indexedEP.Binding.IsAbsoluteUri ? indexedEP.Binding.AbsoluteUri : indexedEP.Binding.ToString()));
 
            if (indexedEP.Location == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.Location)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Location, null, (indexedEP.Location.IsAbsoluteUri ? indexedEP.Location.AbsoluteUri : indexedEP.Location.ToString()));
 
            if (indexedEP.Index < 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.EndpointIndex)));
            }
 
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.EndpointIndex, null, indexedEP.Index.ToString(CultureInfo.InvariantCulture));
 
            if (indexedEP.ResponseLocation != null)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ResponseLocation, null, (indexedEP.ResponseLocation.IsAbsoluteUri ? indexedEP.ResponseLocation.AbsoluteUri : indexedEP.ResponseLocation.ToString()));
            }
 
            if (indexedEP.IsDefault.HasValue)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.EndpointIsDefault, null, XmlConvert.ToString(indexedEP.IsDefault.Value));
            }
 
            WriteCustomAttributes<IndexedProtocolEndpoint>(writer, indexedEP);
            WriteCustomElements<IndexedProtocolEndpoint>(writer, indexedEP);
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes a key descriptor.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="keyDescriptor">The key descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/keyDescriptor is null.</exception>
        protected virtual void WriteKeyDescriptor(XmlWriter writer, KeyDescriptor keyDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (keyDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyDescriptor");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.KeyDescriptor, Saml2MetadataConstants.Namespace);
            if (keyDescriptor.Use == KeyType.Encryption || keyDescriptor.Use == KeyType.Signing)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Use, null, keyDescriptor.Use.ToString().ToLowerInvariant());
            }
 
            WriteCustomAttributes<KeyDescriptor>(writer, keyDescriptor);
 
            if (keyDescriptor.KeyInfo == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, XmlSignatureConstants.Elements.KeyInfo)));
            }
 
            SecurityTokenSerializer.WriteKeyIdentifier(writer, keyDescriptor.KeyInfo);
 
            // Write the encryption method element.
            if (keyDescriptor.EncryptionMethods != null && keyDescriptor.EncryptionMethods.Count > 0)
            {
                foreach (EncryptionMethod encryptionMethod in keyDescriptor.EncryptionMethods)
                {
                    if (encryptionMethod.Algorithm == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.Algorithm)));
                    }
 
                    if (!encryptionMethod.Algorithm.IsAbsoluteUri)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID0014, Saml2MetadataConstants.Attributes.Algorithm)));
                    }
 
                    writer.WriteStartElement(Saml2MetadataConstants.Elements.EncryptionMethod, Saml2MetadataConstants.Namespace);
                    writer.WriteAttributeString(Saml2MetadataConstants.Attributes.Algorithm, null, encryptionMethod.Algorithm.AbsoluteUri);
                    writer.WriteEndElement();
                }
            }
 
            WriteCustomElements<KeyDescriptor>(writer, keyDescriptor);
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes a localized name.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="name">The localized name.</param>
        /// <param name="element">The xml qualified name.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/name/element/name.Name is null.</exception>
        protected virtual void WriteLocalizedName(XmlWriter writer, LocalizedName name, XmlQualifiedName element)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (name == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
            }
 
            if (element == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element");
            }
 
            if (name.Name == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name.Name");
            }
 
            writer.WriteStartElement(element.Name, element.Namespace);
            if (name.Language == null || String.IsNullOrEmpty(name.Name))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, LanguageLocalName)));
            }
 
            writer.WriteAttributeString(LanguagePrefix, LanguageLocalName, LanguageNamespaceUri, name.Language.Name);
            WriteCustomAttributes<LocalizedName>(writer, name);
            writer.WriteString(name.Name);
            WriteCustomElements<LocalizedName>(writer, name);
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes localized uri
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="uri">The localized uri.</param>
        /// <param name="element">The xml qualified name.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/uri/element is null.</exception>
        protected virtual void WriteLocalizedUri(XmlWriter writer, LocalizedUri uri, XmlQualifiedName element)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (uri == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri");
            }
 
            if (element == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element");
            }
 
            writer.WriteStartElement(element.Name, element.Namespace);
            if (uri.Language == null || uri.Uri == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, LanguageLocalName)));
            }
 
            writer.WriteAttributeString(LanguagePrefix, LanguageLocalName, LanguageNamespaceUri, uri.Language.Name);
            WriteCustomAttributes<LocalizedUri>(writer, uri);
            writer.WriteString(uri.Uri.IsAbsoluteUri ? uri.Uri.AbsoluteUri : uri.Uri.ToString());
            WriteCustomElements<LocalizedUri>(writer, uri);
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes the federation metadata to the given stream.
        /// </summary>
        /// <param name="stream">Stream to write the Federation Metadata.</param>
        /// <param name="metadata">Metadata to write.</param>
        /// <exception cref="ArgumentNullException">The input argument is null.</exception>
        public void WriteMetadata(Stream stream, MetadataBase metadata)
        {
            if (stream == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
            }
 
            if (metadata == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("metadata");
            }
 
            using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false))
            {
                WriteMetadata(writer, metadata);
            }
        }
 
        /// <summary>
        /// Writes the federation metadata to the given writer.
        /// </summary>
        /// <param name="writer">Writer to which to write the federation Metadata</param>
        /// <param name="metadata">Metadata to write.</param>
        /// <exception cref="ArgumentNullException">The input argument is null.</exception>
        public void WriteMetadata(XmlWriter writer, MetadataBase metadata)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (metadata == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("metadata");
            }
 
            WriteMetadataCore(writer, metadata);
        }
 
        /// <summary>
        /// Writes the metadata.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="metadataBase">The saml metadat base.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/metadataBase is null.</exception>
        protected virtual void WriteMetadataCore(XmlWriter writer, MetadataBase metadataBase)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (metadataBase == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("metadataBase");
            }
 
            EntitiesDescriptor entitiesDescriptor = metadataBase as EntitiesDescriptor;
            if (entitiesDescriptor != null)
            {
                WriteEntitiesDescriptor(writer, entitiesDescriptor);
            }
            else
            {
                EntityDescriptor entityDescriptor = metadataBase as EntityDescriptor;
                if (entityDescriptor == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.EntitiesDescriptor)));
                }
 
                WriteEntityDescriptor(writer, entityDescriptor);
            }
        }
 
        /// <summary>
        /// Writes an organization.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="organization">The organization.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/organization is null.</exception>
        protected virtual void WriteOrganization(XmlWriter writer, Organization organization)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (organization == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("organization");
            }
 
            if (organization.DisplayNames == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("organization.DisplayNames");
            }
 
            if (organization.Names == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("organization.Names");
            }
 
            if (organization.Urls == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("organization.Urls");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.Organization, Saml2MetadataConstants.Namespace);
 
            if (organization.Names.Count < 1)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.OrganizationName)));
            }
 
            foreach (LocalizedName name in organization.Names)
            {
                XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.OrganizationName, Saml2MetadataConstants.Namespace);
                WriteLocalizedName(writer, name, element);
            }
 
            if (organization.DisplayNames.Count < 1)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.OrganizationDisplayName)));
            }
 
            foreach (LocalizedName displayName in organization.DisplayNames)
            {
                XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.OrganizationDisplayName, Saml2MetadataConstants.Namespace);
                WriteLocalizedName(writer, displayName, element);
            }
 
            if (organization.Urls.Count < 1)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.OrganizationUrl)));
            }
 
            foreach (LocalizedUri uri in organization.Urls)
            {
                XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.OrganizationUrl, Saml2MetadataConstants.Namespace);
                WriteLocalizedUri(writer, uri, element);
            }
 
            WriteCustomAttributes<Organization>(writer, organization);
            WriteCustomElements<Organization>(writer, organization);
            writer.WriteEndElement(); // Organization
        }
 
        /// <summary>
        /// Writes role descriptor attibutes.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="roleDescriptor">The role descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/roleDescriptor/roleDescriptor.ProtocolsSupporeted is null.</exception>
        protected virtual void WriteRoleDescriptorAttributes(XmlWriter writer, RoleDescriptor roleDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (roleDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor");
            }
 
            if (roleDescriptor.ProtocolsSupported == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.ProtocolsSupported");
            }
 
            // Optional
            if (roleDescriptor.ValidUntil != null && roleDescriptor.ValidUntil != DateTime.MaxValue)
            {
                // Write the date in a sortable form.
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ValidUntil, null, roleDescriptor.ValidUntil.ToString("s", CultureInfo.InvariantCulture));
            }
 
            // Optional
            if (roleDescriptor.ErrorUrl != null)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ErrorUrl, null, (roleDescriptor.ErrorUrl.IsAbsoluteUri ? roleDescriptor.ErrorUrl.AbsoluteUri : roleDescriptor.ErrorUrl.ToString()));
            }
 
            // Mandatory
            if (roleDescriptor.ProtocolsSupported.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Attributes.ProtocolsSupported)));
            }
 
            StringBuilder sb = new StringBuilder();
            foreach (Uri protocol in roleDescriptor.ProtocolsSupported)
            {
                sb.AppendFormat("{0} ", (protocol.IsAbsoluteUri ? protocol.AbsoluteUri : protocol.ToString()));
            }
 
            string protocolsString = sb.ToString();
            writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ProtocolsSupported, null, protocolsString.Trim());
 
            WriteCustomAttributes<RoleDescriptor>(writer, roleDescriptor);
        }
 
        /// <summary>
        /// Writes the role descriptor element.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="roleDescriptor">The role descriptor.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/roleDescriptor/roleDescriptor.Contacts/roleDescriptor.Keys is null.</exception>
        protected virtual void WriteRoleDescriptorElements(XmlWriter writer, RoleDescriptor roleDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (roleDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor");
            }
 
            if (roleDescriptor.Contacts == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.Contacts");
            }
 
            if (roleDescriptor.Keys == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("roleDescriptor.Keys");
            }
 
            // Optional
            if (roleDescriptor.Organization != null)
            {
                WriteOrganization(writer, roleDescriptor.Organization);
            }
 
            // Optional
            foreach (KeyDescriptor key in roleDescriptor.Keys)
            {
                WriteKeyDescriptor(writer, key);
            }
 
            // Optional
            foreach (ContactPerson contact in roleDescriptor.Contacts)
            {
                WriteContactPerson(writer, contact);
            }
 
            WriteCustomElements<RoleDescriptor>(writer, roleDescriptor);
        }
 
        /// <summary>
        /// Writes a security token service descriptor.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="securityTokenServiceDescriptor">The <see cref="SecurityTokenServiceDescriptor"/>.</param>
        /// <exception cref="ArgumentNullException">The parameter writer/securityTokenServiceDescriptor/securityTokenServiceDescriptor.Endpoint/
        /// securityTokenServiceDescriptor.PassiveRequestorEndpoints is null.</exception>
        protected virtual void WriteSecurityTokenServiceDescriptor(XmlWriter writer, SecurityTokenServiceDescriptor securityTokenServiceDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (securityTokenServiceDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenServiceDescriptor");
            }
 
            if (securityTokenServiceDescriptor.SecurityTokenServiceEndpoints == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenServiceDescriptor.Endpoints");
            }
 
            if (securityTokenServiceDescriptor.PassiveRequestorEndpoints == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenServiceDescriptor.PassiveRequestorEndpoints");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.RoleDescriptor, Saml2MetadataConstants.Namespace);
            writer.WriteAttributeString("xsi", "type", XmlSchema.InstanceNamespace, FederationMetadataConstants.Prefix + ":" + FederationMetadataConstants.Elements.SecurityTokenServiceType);
 
            writer.WriteAttributeString("xmlns", FederationMetadataConstants.Prefix, null, FederationMetadataConstants.Namespace);
 
            WriteWebServiceDescriptorAttributes(writer, securityTokenServiceDescriptor);
            WriteCustomAttributes<SecurityTokenServiceDescriptor>(writer, securityTokenServiceDescriptor);
 
            WriteWebServiceDescriptorElements(writer, securityTokenServiceDescriptor);
 
            if (securityTokenServiceDescriptor.SecurityTokenServiceEndpoints.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, FederationMetadataConstants.Elements.SecurityTokenServiceEndpoint)));
            }
 
            foreach (EndpointReference epr in securityTokenServiceDescriptor.SecurityTokenServiceEndpoints)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.SecurityTokenServiceEndpoint, FederationMetadataConstants.Namespace);
                epr.WriteTo(writer);
                writer.WriteEndElement();
            }
 
            foreach (EndpointReference epr in securityTokenServiceDescriptor.PassiveRequestorEndpoints)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.PassiveRequestorEndpoint, FederationMetadataConstants.Namespace);
                epr.WriteTo(writer);
                writer.WriteEndElement();
            }
 
            WriteCustomElements<SecurityTokenServiceDescriptor>(writer, securityTokenServiceDescriptor);
 
            writer.WriteEndElement();
        }
 
        /// <summary>
        /// Writes an spsso descriptor.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="serviceProviderSingleSignOnDescriptor">The spsso descriptor.</param>
        /// <exception cref="ArgumentNullException">The input parameter is null.</exception>
        protected virtual void WriteServiceProviderSingleSignOnDescriptor(XmlWriter writer, ServiceProviderSingleSignOnDescriptor serviceProviderSingleSignOnDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (serviceProviderSingleSignOnDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("spssoDescriptor");
            }
 
            if (serviceProviderSingleSignOnDescriptor.AssertionConsumerServices == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("spssoDescriptor.AssertionConsumerService");
            }
 
            writer.WriteStartElement(Saml2MetadataConstants.Elements.SpssoDescriptor, Saml2MetadataConstants.Namespace);
            if (serviceProviderSingleSignOnDescriptor.AuthenticationRequestsSigned)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.AuthenticationRequestsSigned, null,
                    XmlConvert.ToString(serviceProviderSingleSignOnDescriptor.AuthenticationRequestsSigned));
            }
 
            if (serviceProviderSingleSignOnDescriptor.WantAssertionsSigned)
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.WantAssertionsSigned, null,
                    XmlConvert.ToString(serviceProviderSingleSignOnDescriptor.WantAssertionsSigned));
            }
 
            WriteSingleSignOnDescriptorAttributes(writer, serviceProviderSingleSignOnDescriptor);
            WriteCustomAttributes<ServiceProviderSingleSignOnDescriptor>(writer, serviceProviderSingleSignOnDescriptor);
 
            WriteSingleSignOnDescriptorElements(writer, serviceProviderSingleSignOnDescriptor);
            if (serviceProviderSingleSignOnDescriptor.AssertionConsumerServices.Count == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, Saml2MetadataConstants.Elements.AssertionConsumerService)));
            }
 
            foreach (IndexedProtocolEndpoint ep in serviceProviderSingleSignOnDescriptor.AssertionConsumerServices.Values)
            {
                XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.AssertionConsumerService, Saml2MetadataConstants.Namespace);
                WriteIndexedProtocolEndpoint(writer, ep, element);
            }
 
            WriteCustomElements<ServiceProviderSingleSignOnDescriptor>(writer, serviceProviderSingleSignOnDescriptor);
            writer.WriteEndElement(); // SPSSODescriptor
        }
 
        /// <summary>
        /// Writes the sso descriptor attributers.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="singleSignOnDescriptor">The sso descriptor.</param>
        protected virtual void WriteSingleSignOnDescriptorAttributes(XmlWriter writer, SingleSignOnDescriptor singleSignOnDescriptor)
        {
            WriteRoleDescriptorAttributes(writer, singleSignOnDescriptor);
            WriteCustomAttributes<SingleSignOnDescriptor>(writer, singleSignOnDescriptor);
        }
 
        /// <summary>
        /// Writes the sso descriptor element.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="singleSignOnDescriptor">The sso descriptor.</param>
        protected virtual void WriteSingleSignOnDescriptorElements(XmlWriter writer, SingleSignOnDescriptor singleSignOnDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (singleSignOnDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ssoDescriptor");
            }
 
            WriteRoleDescriptorElements(writer, singleSignOnDescriptor);
 
            if (singleSignOnDescriptor.ArtifactResolutionServices != null && singleSignOnDescriptor.ArtifactResolutionServices.Count > 0)
            {
                // Write the artifact resolution services
                foreach (IndexedProtocolEndpoint ep in singleSignOnDescriptor.ArtifactResolutionServices.Values)
                {
                    if (ep.ResponseLocation != null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3249, Saml2MetadataConstants.Attributes.ResponseLocation)));
                    }
 
                    XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.ArtifactResolutionService, Saml2MetadataConstants.Namespace);
                    WriteIndexedProtocolEndpoint(writer, ep, element);
                }
            }
 
            if (singleSignOnDescriptor.SingleLogoutServices != null && singleSignOnDescriptor.SingleLogoutServices.Count > 0)
            {
                // Write the single logout service endpoints.
                foreach (ProtocolEndpoint endpoint in singleSignOnDescriptor.SingleLogoutServices)
                {
                    XmlQualifiedName element = new XmlQualifiedName(Saml2MetadataConstants.Elements.SingleLogoutService, Saml2MetadataConstants.Namespace);
                    WriteProtocolEndpoint(writer, endpoint, element);
                }
            }
 
            if (singleSignOnDescriptor.NameIdentifierFormats != null && singleSignOnDescriptor.NameIdentifierFormats.Count > 0)
            {
                // Write the name id formats
                foreach (Uri nameId in singleSignOnDescriptor.NameIdentifierFormats)
                {
                    if (!nameId.IsAbsoluteUri)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID0014, Saml2MetadataConstants.Elements.NameIDFormat)));
                    }
 
                    writer.WriteStartElement(Saml2MetadataConstants.Elements.NameIDFormat, Saml2MetadataConstants.Namespace);
                    writer.WriteString(nameId.AbsoluteUri);
                    writer.WriteEndElement();
                }
            }
 
            WriteCustomElements<SingleSignOnDescriptor>(writer, singleSignOnDescriptor);
        }
 
        /// <summary>
        /// Write a web service description's attributes.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="wsDescriptor">The web service desriptor.</param>
        /// <exception cref="ArgumentNullException">The input parameter is null.</exception>
        protected virtual void WriteWebServiceDescriptorAttributes(XmlWriter writer, WebServiceDescriptor wsDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (wsDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("wsDescriptor");
            }
 
            WriteRoleDescriptorAttributes(writer, wsDescriptor);
 
            if (!String.IsNullOrEmpty(wsDescriptor.ServiceDisplayName))
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ServiceDisplayName, null, wsDescriptor.ServiceDisplayName);
            }
 
            if (!String.IsNullOrEmpty(wsDescriptor.ServiceDescription))
            {
                writer.WriteAttributeString(Saml2MetadataConstants.Attributes.ServiceDescription, null, wsDescriptor.ServiceDescription);
            }
 
            WriteCustomAttributes<WebServiceDescriptor>(writer, wsDescriptor);
        }
 
        /// <summary>
        /// Write a web service description element.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="wsDescriptor">The web service desriptor.</param>
        /// <exception cref="ArgumentNullException">The input parameter is null.</exception>
        protected virtual void WriteWebServiceDescriptorElements(XmlWriter writer, WebServiceDescriptor wsDescriptor)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (wsDescriptor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("wsDescriptor");
            }
 
            if (wsDescriptor.TargetScopes == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("wsDescriptor.TargetScopes");
            }
 
            if (wsDescriptor.ClaimTypesOffered == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("wsDescriptor.ClaimTypesOffered");
            }
 
            if (wsDescriptor.TokenTypesOffered == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("wsDescriptor.TokenTypesOffered");
            }
 
            WriteRoleDescriptorElements(writer, wsDescriptor);
 
            if (wsDescriptor.TokenTypesOffered.Count > 0)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.TokenTypesOffered, FederationMetadataConstants.Namespace);
                foreach (Uri tokenType in wsDescriptor.TokenTypesOffered)
                {
                    writer.WriteStartElement(WSFederationMetadataConstants.Elements.TokenType, WSFederationMetadataConstants.Namespace);
                    if (!tokenType.IsAbsoluteUri)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MetadataSerializationException(SR.GetString(SR.ID3203, WSAuthorizationConstants.Elements.ClaimType)));
                    }
 
                    writer.WriteAttributeString(WSFederationMetadataConstants.Attributes.Uri, tokenType.AbsoluteUri);
                    writer.WriteEndElement();
                }
 
                writer.WriteEndElement();
            }
 
            if (wsDescriptor.ClaimTypesOffered.Count > 0)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.ClaimTypesOffered, FederationMetadataConstants.Namespace);
                foreach (DisplayClaim claim in wsDescriptor.ClaimTypesOffered)
                {
                    WriteDisplayClaim(writer, claim);
                }
 
                writer.WriteEndElement();
            }
 
            if (wsDescriptor.ClaimTypesRequested.Count > 0)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.ClaimTypesRequested, FederationMetadataConstants.Namespace);
                foreach (DisplayClaim claim in wsDescriptor.ClaimTypesRequested)
                {
                    WriteDisplayClaim(writer, claim);
                }
 
                writer.WriteEndElement();
            }
 
            if (wsDescriptor.TargetScopes.Count > 0)
            {
                writer.WriteStartElement(FederationMetadataConstants.Elements.TargetScopes, FederationMetadataConstants.Namespace);
                foreach (EndpointReference address in wsDescriptor.TargetScopes)
                {
                    address.WriteTo(writer);
                }
 
                writer.WriteEndElement();
            }
 
            WriteCustomElements<WebServiceDescriptor>(writer, wsDescriptor);
        }
 
        /// <summary>
        /// Reads the &lt;saml:Attribute> element.
        /// </summary>
        /// <remarks>
        /// The default implementation requires that the content of the 
        /// Attribute element be a simple string. To handle complex content
        /// or content of declared simple types other than xs:string, override
        /// this method.
        /// </remarks>
        /// <param name="reader">The xml reader.</param>
        /// <returns>A Saml2 attribute.</returns>
        /// <exception cref="ArgumentNullException">The input parameter is null.</exception>
        protected virtual Saml2Attribute ReadAttribute(XmlReader reader)
        {
            if (null == reader)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
 
            // throw if wrong element
            if (!reader.IsStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace))
            {
                reader.ReadStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
            }
 
            try
            {
                Saml2Attribute attribute;
                bool isEmpty = reader.IsEmptyElement;
 
                // @attributes
                string value;
 
                // @xsi:type 
                XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.AttributeType, Saml2Constants.Namespace);
 
                // @Name - required
                value = reader.GetAttribute(Saml2Constants.Attributes.Name);
                if (String.IsNullOrEmpty(value))
                {
                    throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0001, Saml2Constants.Attributes.Name, Saml2Constants.Elements.Attribute));
                }
 
                attribute = new Saml2Attribute(value);
 
                // @NameFormat - optional
                value = reader.GetAttribute(Saml2Constants.Attributes.NameFormat);
                if (!String.IsNullOrEmpty(value))
                {
                    if (!UriUtil.CanCreateValidUri(value, UriKind.Absolute))
                    {
                        throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID0011, Saml2Constants.Attributes.Namespace, Saml2Constants.Elements.Action));
                    }
 
                    attribute.NameFormat = new Uri(value);
                }
 
                // @FriendlyName - optional
                attribute.FriendlyName = reader.GetAttribute(Saml2Constants.Attributes.FriendlyName);
 
                // content
                reader.Read();
                if (!isEmpty)
                {
                    while (reader.IsStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace))
                    {
                        bool isEmptyValue = reader.IsEmptyElement;
                        bool isNil = XmlUtil.IsNil(reader);
 
                        // For now, the value must be a string
                        XmlUtil.ValidateXsiType(reader, "string", XmlSchema.Namespace);
 
                        if (isNil)
                        {
                            reader.Read();
                            if (!isEmptyValue)
                            {
                                reader.ReadEndElement();
                            }
 
                            attribute.Values.Add(null);
                        }
                        else if (isEmptyValue)
                        {
                            reader.Read();
                            attribute.Values.Add("");
                        }
                        else
                        {
                            attribute.Values.Add(reader.ReadElementString());
                        }
                    }
 
                    reader.ReadEndElement();
                }
 
                return attribute;
            }
            catch (Exception e)
            {
                if (System.Runtime.Fx.IsFatal(e))
                    throw;
 
                Exception wrapped = TryWrapReadException(reader, e);
                if (null == wrapped)
                {
                    throw;
                }
                else
                {
                    throw wrapped;
                }
            }
        }
 
        /// <summary>
        /// Writes the &lt;saml:Attribute> element.
        /// </summary>
        /// <param name="writer">The xml writer.</param>
        /// <param name="data">The Saml2 attibute.</param>
        /// <exception cref="ArgumentNullException">The input parameter is null.</exception>
        protected virtual void WriteAttribute(XmlWriter writer, Saml2Attribute data)
        {
            if (null == writer)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
 
            if (null == data)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data");
            }
 
            // <Attribute>
            writer.WriteStartElement(Saml2Constants.Elements.Attribute, Saml2Constants.Namespace);
 
            // @Name - required
            writer.WriteAttributeString(Saml2Constants.Attributes.Name, data.Name);
 
            // @NameFormat - optional
            if (null != data.NameFormat)
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.NameFormat, data.NameFormat.AbsoluteUri);
            }
 
            // @FriendlyName - optional
            if (null != data.FriendlyName)
            {
                writer.WriteAttributeString(Saml2Constants.Attributes.FriendlyName, data.FriendlyName);
            }
 
            // <AttributeValue> 0-OO (nillable)
            foreach (string value in data.Values)
            {
                writer.WriteStartElement(Saml2Constants.Elements.AttributeValue, Saml2Constants.Namespace);
 
                if (null == value)
                {
                    writer.WriteAttributeString("nil", XmlSchema.InstanceNamespace, XmlConvert.ToString(true));
                }
                else if (value.Length > 0)
                {
                    writer.WriteString(value);
                }
 
                writer.WriteEndElement();
            }
 
            // </Attribute>
            writer.WriteEndElement();
        }
 
        // Wraps common data validation exceptions with an XmlException 
        // associated with the failing reader
        private static Exception TryWrapReadException(XmlReader reader, Exception inner)
        {
            if (inner is FormatException
                || inner is ArgumentException
                || inner is InvalidOperationException
                || inner is OverflowException)
            {
                return DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4125), inner);
            }
 
            return null;
        }
    }
}