File: System\ServiceModel\Security\WSUtilitySpecificationVersion.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
namespace System.ServiceModel.Security
{
    using System.ServiceModel;
    using System.IO;
    using System.Text;
    using System.Xml;
 
    using CanonicalFormWriter = IdentityModel.CanonicalFormWriter;
    using SignatureResourcePool = IdentityModel.SignatureResourcePool;
    using HashStream = IdentityModel.HashStream;
 
    internal abstract class WSUtilitySpecificationVersion
    {
        internal static string[] AcceptedDateTimeFormats { get; } = new string[]
        {
            "yyyy-MM-ddTHH:mm:ss.fffffffZ",
            "yyyy-MM-ddTHH:mm:ss.ffffffZ",
            "yyyy-MM-ddTHH:mm:ss.fffffZ",
            "yyyy-MM-ddTHH:mm:ss.ffffZ",
            "yyyy-MM-ddTHH:mm:ss.fffZ",
            "yyyy-MM-ddTHH:mm:ss.ffZ",
            "yyyy-MM-ddTHH:mm:ss.fZ",
            "yyyy-MM-ddTHH:mm:ssZ"
        };
 
        internal WSUtilitySpecificationVersion(XmlDictionaryString namespaceUri)
        {
            NamespaceUri = namespaceUri;
        }
 
        public static WSUtilitySpecificationVersion Default
        {
            get { return OneDotZero; }
        }
 
        internal XmlDictionaryString NamespaceUri { get; }
 
        public static WSUtilitySpecificationVersion OneDotZero
        {
            get { return WSUtilitySpecificationVersionOneDotZero.Instance; }
        }
 
        internal abstract bool IsReaderAtTimestamp(XmlDictionaryReader reader);
 
        internal abstract SecurityTimestamp ReadTimestamp(XmlDictionaryReader reader, string digestAlgorithm, SignatureResourcePool resourcePool);
 
        internal abstract void WriteTimestamp(XmlDictionaryWriter writer, SecurityTimestamp timestamp);
 
        internal abstract void WriteTimestampCanonicalForm(Stream stream, SecurityTimestamp timestamp, byte[] buffer);
 
        private sealed class WSUtilitySpecificationVersionOneDotZero : WSUtilitySpecificationVersion
        {
            private WSUtilitySpecificationVersionOneDotZero()
                : base(XD.UtilityDictionary.Namespace)
            {
            }
 
            public static WSUtilitySpecificationVersionOneDotZero Instance { get; } = new WSUtilitySpecificationVersionOneDotZero();
 
            internal override bool IsReaderAtTimestamp(XmlDictionaryReader reader)
            {
                return reader.IsStartElement(XD.UtilityDictionary.Timestamp, XD.UtilityDictionary.Namespace);
            }
 
            internal override SecurityTimestamp ReadTimestamp(XmlDictionaryReader reader, string digestAlgorithm, SignatureResourcePool resourcePool)
            {
                bool canonicalize = digestAlgorithm != null && reader.CanCanonicalize;
                HashStream hashStream = null;
 
                reader.MoveToStartElement(XD.UtilityDictionary.Timestamp, XD.UtilityDictionary.Namespace);
                if (canonicalize)
                {
                    hashStream = resourcePool.TakeHashStream(digestAlgorithm);
                    reader.StartCanonicalization(hashStream, false, null);
                }
                string id = reader.GetAttribute(XD.UtilityDictionary.IdAttribute, XD.UtilityDictionary.Namespace);
                reader.ReadStartElement();
 
                reader.ReadStartElement(XD.UtilityDictionary.CreatedElement, XD.UtilityDictionary.Namespace);
                DateTime creationTimeUtc = reader.ReadContentAsDateTime().ToUniversalTime();
                reader.ReadEndElement();
 
                DateTime expiryTimeUtc;
                if (reader.IsStartElement(XD.UtilityDictionary.ExpiresElement, XD.UtilityDictionary.Namespace))
                {
                    reader.ReadStartElement();
                    expiryTimeUtc = reader.ReadContentAsDateTime().ToUniversalTime();
                    reader.ReadEndElement();
                }
                else
                {
                    expiryTimeUtc = SecurityUtils.MaxUtcDateTime;
                }
 
                reader.ReadEndElement();
 
                byte[] digest;
                if (canonicalize)
                {
                    reader.EndCanonicalization();
                    digest = hashStream.FlushHashAndGetValue();
                }
                else
                {
                    digest = null;
                }
                return new SecurityTimestamp(creationTimeUtc, expiryTimeUtc, id, digestAlgorithm, digest);
            }
 
            internal override void WriteTimestamp(XmlDictionaryWriter writer, SecurityTimestamp timestamp)
            {
                writer.WriteStartElement(XD.UtilityDictionary.Prefix.Value, XD.UtilityDictionary.Timestamp, XD.UtilityDictionary.Namespace);
                writer.WriteAttributeString(XD.UtilityDictionary.IdAttribute, XD.UtilityDictionary.Namespace, timestamp.Id);
 
                writer.WriteStartElement(XD.UtilityDictionary.CreatedElement, XD.UtilityDictionary.Namespace);
                char[] creationTime = timestamp.GetCreationTimeChars();
                writer.WriteChars(creationTime, 0, creationTime.Length);
                writer.WriteEndElement(); // wsu:Created
 
                writer.WriteStartElement(XD.UtilityDictionary.ExpiresElement, XD.UtilityDictionary.Namespace);
                char[] expiryTime = timestamp.GetExpiryTimeChars();
                writer.WriteChars(expiryTime, 0, expiryTime.Length);
                writer.WriteEndElement(); // wsu:Expires
 
                writer.WriteEndElement();
            }
 
            internal override void WriteTimestampCanonicalForm(Stream stream, SecurityTimestamp timestamp, byte[] workBuffer)
            {
                TimestampCanonicalFormWriter.Instance.WriteCanonicalForm(
                    stream,
                    timestamp.Id, timestamp.GetCreationTimeChars(), timestamp.GetExpiryTimeChars(),
                    workBuffer);
            }
        }
 
        private sealed class TimestampCanonicalFormWriter : CanonicalFormWriter
        {
            private const string Timestamp = UtilityStrings.Prefix + ":" + UtilityStrings.Timestamp;
            private const string Created = UtilityStrings.Prefix + ":" + UtilityStrings.CreatedElement;
            private const string Expires = UtilityStrings.Prefix + ":" + UtilityStrings.ExpiresElement;
            private const string IdAttribute = UtilityStrings.Prefix + ":" + UtilityStrings.IdAttribute;
            private const string Ns = "xmlns:" + UtilityStrings.Prefix + "=\"" + UtilityStrings.Namespace + "\"";
 
            private const string Xml1 = "<" + Timestamp + " " + Ns + " " + IdAttribute + "=\"";
            private const string Xml2 = "\"><" + Created + ">";
            private const string Xml3 = "</" + Created + "><" + Expires + ">";
            private const string Xml4 = "</" + Expires + "></" + Timestamp + ">";
 
            private readonly byte[] _fragment1;
            private readonly byte[] _fragment2;
            private readonly byte[] _fragment3;
            private readonly byte[] _fragment4;
 
            private TimestampCanonicalFormWriter()
            {
                Encoding encoding = CanonicalFormWriter.Utf8WithoutPreamble;
                _fragment1 = encoding.GetBytes(Xml1);
                _fragment2 = encoding.GetBytes(Xml2);
                _fragment3 = encoding.GetBytes(Xml3);
                _fragment4 = encoding.GetBytes(Xml4);
            }
 
            public static TimestampCanonicalFormWriter Instance { get; } = new TimestampCanonicalFormWriter();
 
            public void WriteCanonicalForm(Stream stream, string id, char[] created, char[] expires, byte[] workBuffer)
            {
                stream.Write(_fragment1, 0, _fragment1.Length);
                EncodeAndWrite(stream, workBuffer, id);
                stream.Write(_fragment2, 0, _fragment2.Length);
                EncodeAndWrite(stream, workBuffer, created);
                stream.Write(_fragment3, 0, _fragment3.Length);
                EncodeAndWrite(stream, workBuffer, expires);
                stream.Write(_fragment4, 0, _fragment4.Length);
            }
        }
    }
}