File: System\ServiceModel\Security\WSSecurityOneDotZeroSendSecurityHeader.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//----------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Security
{
    using System.IdentityModel.Tokens;
    using System.IO;
    using System.Runtime;
    using System.Security.Cryptography;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Diagnostics;
    using System.Xml;
    using ExclusiveCanonicalizationTransform = System.IdentityModel.ExclusiveCanonicalizationTransform;
    using HashStream = System.IdentityModel.HashStream;
    using IPrefixGenerator = System.IdentityModel.IPrefixGenerator;
    using ISecurityElement = System.IdentityModel.ISecurityElement;
    using ISignatureValueSecurityElement = System.IdentityModel.ISignatureValueSecurityElement;
    using PreDigestedSignedInfo = System.IdentityModel.PreDigestedSignedInfo;
    using Reference = System.IdentityModel.Reference;
    using SignedInfo = System.IdentityModel.SignedInfo;
    using SignedXml = System.IdentityModel.SignedXml;
    using StandardSignedInfo = System.IdentityModel.StandardSignedInfo;
    using System.ServiceModel.Security.Tokens;
 
    class WSSecurityOneDotZeroSendSecurityHeader : SendSecurityHeader
    {
        HashStream hashStream;
        PreDigestedSignedInfo signedInfo;
        SignedXml signedXml;
        SecurityKey signatureKey;
        MessagePartSpecification effectiveSignatureParts;
 
        SymmetricAlgorithm encryptingSymmetricAlgorithm;
        ReferenceList referenceList;
        SecurityKeyIdentifier encryptionKeyIdentifier;
 
        bool hasSignedEncryptedMessagePart;
 
        // For Transport Secrity we have to sign the 'To' header with the 
        // supporting tokens.
        byte[] toHeaderHash = null;
        string toHeaderId = null;
 
        public WSSecurityOneDotZeroSendSecurityHeader(Message message, string actor, bool mustUnderstand, bool relay,
            SecurityStandardsManager standardsManager,
            SecurityAlgorithmSuite algorithmSuite,
            MessageDirection direction)
            : base(message, actor, mustUnderstand, relay, standardsManager, algorithmSuite, direction)
        {
        }
 
        protected string EncryptionAlgorithm
        {
            get { return this.AlgorithmSuite.DefaultEncryptionAlgorithm; }
        }
 
        protected XmlDictionaryString EncryptionAlgorithmDictionaryString
        {
            get { return this.AlgorithmSuite.DefaultEncryptionAlgorithmDictionaryString; }
        }
 
        protected override bool HasSignedEncryptedMessagePart
        {
            get
            {
                return this.hasSignedEncryptedMessagePart;
            }
        }
 
        void AddEncryptionReference(MessageHeader header, string headerId, IPrefixGenerator prefixGenerator, bool sign,
            out MemoryStream plainTextStream, out string encryptedDataId)
        {
            plainTextStream = new MemoryStream();
            XmlDictionaryWriter encryptingWriter = XmlDictionaryWriter.CreateTextWriter(plainTextStream);
            if (sign)
            {
                AddSignatureReference(header, headerId, prefixGenerator, encryptingWriter);
            }
            else
            {
                header.WriteHeader(encryptingWriter, this.Version);
                encryptingWriter.Flush();
            }
            encryptedDataId = this.GenerateId();
            referenceList.AddReferredId(encryptedDataId);
        }
 
        void AddSignatureReference(SecurityToken token, int position, SecurityTokenAttachmentMode mode)
        {
            SecurityKeyIdentifierClause keyIdentifierClause = null;
            bool strTransformEnabled = this.ShouldUseStrTransformForToken(token, position, mode, out keyIdentifierClause);
            AddTokenSignatureReference(token, keyIdentifierClause, strTransformEnabled);
        }
 
        void AddPrimaryTokenSignatureReference(SecurityToken token, SecurityTokenParameters securityTokenParameters)
        {
            // Currently we only support signing the primary token if the primary token is an issued token and protectTokens knob is set to true.
            // We will get rid of the below check when we support all token types.
            IssuedSecurityTokenParameters istp = securityTokenParameters as IssuedSecurityTokenParameters;
            if (istp == null)
            {
                return;
            }
 
            bool strTransformEnabled = istp != null && istp.UseStrTransform;
            SecurityKeyIdentifierClause keyIdentifierClause = null;
            // Only if the primary token is included in the message that we sign it because WCF at present does not resolve externally referenced tokens. 
            // This means in the server's response 
            if (ShouldSerializeToken(securityTokenParameters, this.MessageDirection))
            {
                if (strTransformEnabled)
                {
                    keyIdentifierClause = securityTokenParameters.CreateKeyIdentifierClause(token, GetTokenReferenceStyle(securityTokenParameters));
                }
                AddTokenSignatureReference(token, keyIdentifierClause, strTransformEnabled);
            }
        }
 
        // Given a token and useStarTransform value this method adds apporopriate reference accordingly.
        // 1. If strTransform is disabled, it adds a reference to the token's id. 
        // 2. Else if strtransform is enabled it adds a reference the security token's keyIdentifier's id.
        void AddTokenSignatureReference(SecurityToken token, SecurityKeyIdentifierClause keyIdentifierClause, bool strTransformEnabled)
        {
            if (!strTransformEnabled && token.Id == null)
            {
                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.ElementToSignMustHaveId)), this.Message);
            }
 
            HashStream hashStream = TakeHashStream();
            XmlDictionaryWriter utf8Writer = TakeUtf8Writer();
            utf8Writer.StartCanonicalization(hashStream, false, null);
            this.StandardsManager.SecurityTokenSerializer.WriteToken(utf8Writer, token);
            utf8Writer.EndCanonicalization();
 
            if (strTransformEnabled)
            {
                if (keyIdentifierClause != null)
                {
                    if (String.IsNullOrEmpty(keyIdentifierClause.Id))
                        keyIdentifierClause.Id = SecurityUniqueId.Create().Value;
                    this.ElementContainer.MapSecurityTokenToStrClause(token, keyIdentifierClause);
                    this.signedInfo.AddReference(keyIdentifierClause.Id, hashStream.FlushHashAndGetValue(), true);
                }
                else
                    throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenManagerCannotCreateTokenReference)), this.Message);
            }
            else
                this.signedInfo.AddReference(token.Id, hashStream.FlushHashAndGetValue());
        }
 
        void AddSignatureReference(SendSecurityHeaderElement[] elements)
        {
            if (elements != null)
            {
                for (int i = 0; i < elements.Length; ++i)
                {
                    SecurityKeyIdentifierClause keyIdentifierClause = null;
                    TokenElement signedEncryptedTokenElement = elements[i].Item as TokenElement;
 
                    // signedEncryptedTokenElement can either be a TokenElement ( in SignThenEncrypt case) or EncryptedData ( in !SignThenEncryptCase)
                    // STR-Transform does not make sense in !SignThenEncrypt case .
                    // note: signedEncryptedTokenElement can also be SignatureConfirmation but we do not care about it here.
                    bool useStrTransform = signedEncryptedTokenElement != null
                                           && SignThenEncrypt
                                           && this.ShouldUseStrTransformForToken(signedEncryptedTokenElement.Token,
                                                                                 i,
                                                                                 SecurityTokenAttachmentMode.SignedEncrypted,
                                                                                 out keyIdentifierClause);
 
                    if (!useStrTransform && elements[i].Id == null)
                    {
                        throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.ElementToSignMustHaveId)), this.Message);
                    }
 
                    HashStream hashStream = TakeHashStream();
                    XmlDictionaryWriter utf8Writer = TakeUtf8Writer();
                    utf8Writer.StartCanonicalization(hashStream, false, null);
                    elements[i].Item.WriteTo(utf8Writer, ServiceModelDictionaryManager.Instance);
                    utf8Writer.EndCanonicalization();
 
                    if (useStrTransform)
                    {
                        if (keyIdentifierClause != null)
                        {
                            if (String.IsNullOrEmpty(keyIdentifierClause.Id))
                                keyIdentifierClause.Id = SecurityUniqueId.Create().Value;
 
                            this.ElementContainer.MapSecurityTokenToStrClause(signedEncryptedTokenElement.Token, keyIdentifierClause);
                            this.signedInfo.AddReference(keyIdentifierClause.Id, hashStream.FlushHashAndGetValue(), true);
                        }
                        else
                            throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenManagerCannotCreateTokenReference)), this.Message);
                    }
                    else
                        this.signedInfo.AddReference(elements[i].Id, hashStream.FlushHashAndGetValue());
                }
            }
        }
 
        void AddSignatureReference(SecurityToken[] tokens, SecurityTokenAttachmentMode mode)
        {
            if (tokens != null)
            {
                for (int i = 0; i < tokens.Length; ++i)
                {
                    AddSignatureReference(tokens[i], i, mode);
                }
            }
        }
 
        string GetSignatureHash(MessageHeader header, string headerId, IPrefixGenerator prefixGenerator, XmlDictionaryWriter writer, out byte[] hash)
        {
            HashStream hashStream = TakeHashStream();
            XmlDictionaryWriter effectiveWriter;
            XmlBuffer canonicalBuffer = null;
 
            if (writer.CanCanonicalize)
            {
                effectiveWriter = writer;
            }
            else
            {
                canonicalBuffer = new XmlBuffer(int.MaxValue);
                effectiveWriter = canonicalBuffer.OpenSection(XmlDictionaryReaderQuotas.Max);
            }
 
            effectiveWriter.StartCanonicalization(hashStream, false, null);
 
            header.WriteStartHeader(effectiveWriter, this.Version);
            if (headerId == null)
            {
                headerId = GenerateId();
                this.StandardsManager.IdManager.WriteIdAttribute(effectiveWriter, headerId);
            }
            header.WriteHeaderContents(effectiveWriter, this.Version);
            effectiveWriter.WriteEndElement();
            effectiveWriter.EndCanonicalization();
            effectiveWriter.Flush();
 
            if (!ReferenceEquals(effectiveWriter, writer))
            {
                Fx.Assert(canonicalBuffer != null, "Canonical buffer cannot be null.");
                canonicalBuffer.CloseSection();
                canonicalBuffer.Close();
                XmlDictionaryReader dicReader = canonicalBuffer.GetReader(0);
                writer.WriteNode(dicReader, false);
                dicReader.Close();
            }
 
            hash = hashStream.FlushHashAndGetValue();
 
            return headerId;
        }
 
        void AddSignatureReference(MessageHeader header, string headerId, IPrefixGenerator prefixGenerator, XmlDictionaryWriter writer)
        {
            byte[] hashValue;
            headerId = GetSignatureHash(header, headerId, prefixGenerator, writer, out hashValue);
            this.signedInfo.AddReference(headerId, hashValue);
        }
 
        void ApplySecurityAndWriteHeader(MessageHeader header, string headerId, XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator)
        {
            if (!this.RequireMessageProtection && this.ShouldSignToHeader)
            {
                if ((header.Name == XD.AddressingDictionary.To.Value) &&
                    (header.Namespace == this.Message.Version.Addressing.Namespace))
                {
                    if (this.toHeaderHash == null)
                    {
                        byte[] headerHash;
                        headerId = GetSignatureHash(header, headerId, prefixGenerator, writer, out headerHash);
                        this.toHeaderHash = headerHash;
                        this.toHeaderId = headerId;
                    }
                    else
                        // More than one 'To' header is specified in the message.
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TransportSecuredMessageHasMoreThanOneToHeader)));
 
                    return;
                }
            }
 
            MessagePartProtectionMode protectionMode = GetProtectionMode(header);
            MemoryStream plainTextStream;
            string encryptedDataId;
            switch (protectionMode)
            {
                case MessagePartProtectionMode.None:
                    header.WriteHeader(writer, this.Version);
                    return;
                case MessagePartProtectionMode.Sign:
                    AddSignatureReference(header, headerId, prefixGenerator, writer);
                    return;
                case MessagePartProtectionMode.SignThenEncrypt:
                    AddEncryptionReference(header, headerId, prefixGenerator, true, out plainTextStream, out encryptedDataId);
                    EncryptAndWriteHeader(header, encryptedDataId, plainTextStream, writer);
                    this.hasSignedEncryptedMessagePart = true;
                    return;
                case MessagePartProtectionMode.Encrypt:
                    AddEncryptionReference(header, headerId, prefixGenerator, false, out plainTextStream, out encryptedDataId);
                    EncryptAndWriteHeader(header, encryptedDataId, plainTextStream, writer);
                    return;
                case MessagePartProtectionMode.EncryptThenSign:
                    AddEncryptionReference(header, headerId, prefixGenerator, false, out plainTextStream, out encryptedDataId);
                    EncryptedHeader encryptedHeader = EncryptHeader(
                        header, this.encryptingSymmetricAlgorithm, this.encryptionKeyIdentifier, this.Version, encryptedDataId, plainTextStream);
                    AddSignatureReference(encryptedHeader, encryptedDataId, prefixGenerator, writer);
                    return;
                default:
                    Fx.Assert("Invalid MessagePartProtectionMode");
                    return;
            }
        }
 
        public override void ApplySecurityAndWriteHeaders(MessageHeaders headers, XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator)
        {
            string[] headerIds;
            if (this.RequireMessageProtection || this.ShouldSignToHeader)
            {
                headerIds = headers.GetHeaderAttributes(UtilityStrings.IdAttribute,
                    this.StandardsManager.IdManager.DefaultIdNamespaceUri);
            }
            else
            {
                headerIds = null;
            }
            for (int i = 0; i < headers.Count; i++)
            {
                MessageHeader header = headers.GetMessageHeader(i);
                if (this.Version.Addressing == AddressingVersion.None && header.Namespace == AddressingVersion.None.Namespace)
                {
                    continue;
                }
 
                if (header != this)
                {
                    ApplySecurityAndWriteHeader(header, headerIds == null ? null : headerIds[i], writer, prefixGenerator);
                }
            }
        }
 
        static bool CanCanonicalizeAndFragment(XmlDictionaryWriter writer)
        {
            if (!writer.CanCanonicalize)
            {
                return false;
            }
            IFragmentCapableXmlDictionaryWriter fragmentingWriter = writer as IFragmentCapableXmlDictionaryWriter;
            return fragmentingWriter != null && fragmentingWriter.CanFragment;
        }
 
        public override void ApplyBodySecurity(XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator)
        {
            SecurityAppliedMessage message = this.SecurityAppliedMessage;
            EncryptedData encryptedData;
            HashStream hashStream;
            switch (message.BodyProtectionMode)
            {
                case MessagePartProtectionMode.None:
                    return;
                case MessagePartProtectionMode.Sign:
                    hashStream = TakeHashStream();
                    if (CanCanonicalizeAndFragment(writer))
                    {
                        message.WriteBodyToSignWithFragments(hashStream, false, null, writer);
                    }
                    else
                    {
                        message.WriteBodyToSign(hashStream);
                    }
                    this.signedInfo.AddReference(message.BodyId, hashStream.FlushHashAndGetValue());
                    return;
                case MessagePartProtectionMode.SignThenEncrypt:
                    hashStream = TakeHashStream();
                    encryptedData = CreateEncryptedDataForBody();
                    if (CanCanonicalizeAndFragment(writer))
                    {
                        message.WriteBodyToSignThenEncryptWithFragments(hashStream, false, null, encryptedData, this.encryptingSymmetricAlgorithm, writer);
                    }
                    else
                    {
                        message.WriteBodyToSignThenEncrypt(hashStream, encryptedData, this.encryptingSymmetricAlgorithm);
                    }
                    this.signedInfo.AddReference(message.BodyId, hashStream.FlushHashAndGetValue());
                    this.referenceList.AddReferredId(encryptedData.Id);
                    this.hasSignedEncryptedMessagePart = true;
                    return;
                case MessagePartProtectionMode.Encrypt:
                    encryptedData = CreateEncryptedDataForBody();
                    message.WriteBodyToEncrypt(encryptedData, this.encryptingSymmetricAlgorithm);
                    this.referenceList.AddReferredId(encryptedData.Id);
                    return;
                case MessagePartProtectionMode.EncryptThenSign:
                    hashStream = TakeHashStream();
                    encryptedData = CreateEncryptedDataForBody();
                    message.WriteBodyToEncryptThenSign(hashStream, encryptedData, this.encryptingSymmetricAlgorithm);
                    this.signedInfo.AddReference(message.BodyId, hashStream.FlushHashAndGetValue());
                    this.referenceList.AddReferredId(encryptedData.Id);
                    return;
                default:
                    Fx.Assert("Invalid MessagePartProtectionMode");
                    return;
            }
        }
 
        protected static MemoryStream CaptureToken(SecurityToken token, SecurityStandardsManager serializer)
        {
            MemoryStream stream = new MemoryStream();
            XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
            serializer.SecurityTokenSerializer.WriteToken(writer, token);
            writer.Flush();
            stream.Seek(0, SeekOrigin.Begin);
            return stream;
        }
 
        protected static MemoryStream CaptureSecurityElement(ISecurityElement element)
        {
            MemoryStream stream = new MemoryStream();
            XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
            element.WriteTo(writer, ServiceModelDictionaryManager.Instance);
            writer.Flush();
            stream.Seek(0, SeekOrigin.Begin);
            return stream;
        }
 
        protected override ISecurityElement CompleteEncryptionCore(
            SendSecurityHeaderElement primarySignature,
            SendSecurityHeaderElement[] basicTokens,
            SendSecurityHeaderElement[] signatureConfirmations,
            SendSecurityHeaderElement[] endorsingSignatures)
        {
            if (this.referenceList == null)
            {
                return null;
            }
 
            if (primarySignature != null && primarySignature.Item != null && primarySignature.MarkedForEncryption)
            {
                EncryptElement(primarySignature);
            }
 
            if (basicTokens != null)
            {
                for (int i = 0; i < basicTokens.Length; ++i)
                {
                    if (basicTokens[i].MarkedForEncryption)
                        EncryptElement(basicTokens[i]);
                }
            }
 
            if (signatureConfirmations != null)
            {
                for (int i = 0; i < signatureConfirmations.Length; ++i)
                {
                    if (signatureConfirmations[i].MarkedForEncryption)
                        EncryptElement(signatureConfirmations[i]);
                }
            }
 
            if (endorsingSignatures != null)
            {
                for (int i = 0; i < endorsingSignatures.Length; ++i)
                {
                    if (endorsingSignatures[i].MarkedForEncryption)
                        EncryptElement(endorsingSignatures[i]);
                }
            }
 
            try
            {
                return this.referenceList.DataReferenceCount > 0 ? this.referenceList : null;
            }
            finally
            {
                this.referenceList = null;
                this.encryptingSymmetricAlgorithm = null;
                this.encryptionKeyIdentifier = null;
            }
        }
 
        protected override ISignatureValueSecurityElement CompletePrimarySignatureCore(
            SendSecurityHeaderElement[] signatureConfirmations,
            SecurityToken[] signedEndorsingTokens,
            SecurityToken[] signedTokens,
            SendSecurityHeaderElement[] basicTokens, bool isPrimarySignature)
        {
            if (this.signedXml == null)
            {
                return null;
            }
 
            SecurityTimestamp timestamp = this.Timestamp;
            if (timestamp != null)
            {
                if (timestamp.Id == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TimestampToSignHasNoId)));
                }
                HashStream hashStream = TakeHashStream();
                this.StandardsManager.WSUtilitySpecificationVersion.WriteTimestampCanonicalForm(
                    hashStream, timestamp, this.signedInfo.ResourcePool.TakeEncodingBuffer());
                signedInfo.AddReference(timestamp.Id, hashStream.FlushHashAndGetValue());
            }
 
            if ((this.ShouldSignToHeader) && (this.signatureKey is AsymmetricSecurityKey) && (this.Version.Addressing != AddressingVersion.None))
            {
                if (this.toHeaderHash != null)
                    signedInfo.AddReference(this.toHeaderId, this.toHeaderHash);
                else
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TransportSecurityRequireToHeader)));
            }
 
            AddSignatureReference(signatureConfirmations);
            if (isPrimarySignature && this.ShouldProtectTokens)
            {
                AddPrimaryTokenSignatureReference(this.ElementContainer.SourceSigningToken, this.SigningTokenParameters);
            }
 
            if (this.RequireMessageProtection)
            {
                AddSignatureReference(signedEndorsingTokens, SecurityTokenAttachmentMode.SignedEndorsing);
                AddSignatureReference(signedTokens, SecurityTokenAttachmentMode.Signed);
                AddSignatureReference(basicTokens);
            }
 
            if (this.signedInfo.ReferenceCount == 0)
            {
                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NoPartsOfMessageMatchedPartsToSign)), this.Message);
            }
            try
            {
                this.signedXml.ComputeSignature(this.signatureKey);
                return this.signedXml;
            }
            finally
            {
                this.hashStream = null;
                this.signedInfo = null;
                this.signedXml = null;
                this.signatureKey = null;
                this.effectiveSignatureParts = null;
            }
        }
 
        EncryptedData CreateEncryptedData()
        {
            EncryptedData encryptedData = new EncryptedData();
            encryptedData.SecurityTokenSerializer = this.StandardsManager.SecurityTokenSerializer;
            encryptedData.KeyIdentifier = this.encryptionKeyIdentifier;
            encryptedData.EncryptionMethod = this.EncryptionAlgorithm;
            encryptedData.EncryptionMethodDictionaryString = this.EncryptionAlgorithmDictionaryString;
            return encryptedData;
        }
 
        EncryptedData CreateEncryptedData(MemoryStream stream, string id, bool typeElement)
        {
            EncryptedData encryptedData = CreateEncryptedData();
            encryptedData.Id = id;
            encryptedData.SetUpEncryption(this.encryptingSymmetricAlgorithm, new ArraySegment<byte>(stream.GetBuffer(), 0, (int) stream.Length));
            if (typeElement)
            {
                encryptedData.Type = EncryptedData.ElementType;
            }
            return encryptedData;
        }
 
        EncryptedData CreateEncryptedDataForBody()
        {
            EncryptedData encryptedData = CreateEncryptedData();
            encryptedData.Type = EncryptedData.ContentType;
            return encryptedData;
        }
 
        void EncryptAndWriteHeader(MessageHeader plainTextHeader, string id, MemoryStream stream, XmlDictionaryWriter writer)
        {
            EncryptedHeader encryptedHeader = EncryptHeader(
                plainTextHeader,
                this.encryptingSymmetricAlgorithm, this.encryptionKeyIdentifier, this.Version,
                id, stream);
            encryptedHeader.WriteHeader(writer, this.Version);
        }
 
        void EncryptElement(SendSecurityHeaderElement element)
        {
            string id = GenerateId();
            ISecurityElement encryptedElement = CreateEncryptedData(CaptureSecurityElement(element.Item), id, true);
            this.referenceList.AddReferredId(id);
            element.Replace(id, encryptedElement);
        }
 
        protected virtual EncryptedHeader EncryptHeader(MessageHeader plainTextHeader, SymmetricAlgorithm algorithm,
                SecurityKeyIdentifier keyIdentifier, MessageVersion version, string id, MemoryStream stream)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                SR.GetString(SR.HeaderEncryptionNotSupportedInWsSecurityJan2004, plainTextHeader.Name, plainTextHeader.Namespace)));
        }
 
        HashStream TakeHashStream()
        {
            HashStream hashStream = null;
            if (this.hashStream == null)
            {
                this.hashStream = hashStream = new HashStream(CryptoHelper.CreateHashAlgorithm(this.AlgorithmSuite.DefaultDigestAlgorithm));
            }
            else
            {
                hashStream = this.hashStream;;
                hashStream.Reset();
            }
            return hashStream;
        }
 
        XmlDictionaryWriter TakeUtf8Writer()
        {
            return this.signedInfo.ResourcePool.TakeUtf8Writer();
        }
 
        MessagePartProtectionMode GetProtectionMode(MessageHeader header)
        {
            if (!this.RequireMessageProtection)
            {
                return MessagePartProtectionMode.None;
            }
            bool sign = this.signedInfo != null && this.effectiveSignatureParts.IsHeaderIncluded(header);
            bool encrypt = this.referenceList != null && this.EncryptionParts.IsHeaderIncluded(header);
            return MessagePartProtectionModeHelper.GetProtectionMode(sign, encrypt, this.SignThenEncrypt);
        }
 
        protected override void StartEncryptionCore(SecurityToken token, SecurityKeyIdentifier keyIdentifier)
        {
            this.encryptingSymmetricAlgorithm = SecurityUtils.GetSymmetricAlgorithm(this.EncryptionAlgorithm, token);
            if (this.encryptingSymmetricAlgorithm == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(
                    SR.GetString(SR.UnableToCreateSymmetricAlgorithmFromToken, this.EncryptionAlgorithm)));
            }
            this.encryptionKeyIdentifier = keyIdentifier;
            this.referenceList = new ReferenceList();
        }
 
        protected override void StartPrimarySignatureCore(SecurityToken token,
            SecurityKeyIdentifier keyIdentifier,
            MessagePartSpecification signatureParts,
            bool generateTargettableSignature)
        {
            SecurityAlgorithmSuite suite = this.AlgorithmSuite;
            string canonicalizationAlgorithm = suite.DefaultCanonicalizationAlgorithm;
            XmlDictionaryString canonicalizationAlgorithmDictionaryString = suite.DefaultCanonicalizationAlgorithmDictionaryString;
            if (canonicalizationAlgorithm != SecurityAlgorithms.ExclusiveC14n)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new MessageSecurityException(SR.GetString(SR.UnsupportedCanonicalizationAlgorithm, suite.DefaultCanonicalizationAlgorithm)));
            }
            string signatureAlgorithm;
            XmlDictionaryString signatureAlgorithmDictionaryString;
            suite.GetSignatureAlgorithmAndKey(token, out signatureAlgorithm, out this.signatureKey, out signatureAlgorithmDictionaryString);
            string digestAlgorithm = suite.DefaultDigestAlgorithm;
            XmlDictionaryString digestAlgorithmDictionaryString = suite.DefaultDigestAlgorithmDictionaryString;
            this.signedInfo = new PreDigestedSignedInfo(ServiceModelDictionaryManager.Instance, canonicalizationAlgorithm, canonicalizationAlgorithmDictionaryString, digestAlgorithm, digestAlgorithmDictionaryString, signatureAlgorithm, signatureAlgorithmDictionaryString);
            this.signedXml = new SignedXml(this.signedInfo, ServiceModelDictionaryManager.Instance, this.StandardsManager.SecurityTokenSerializer);
            if (keyIdentifier != null)
            {
                this.signedXml.Signature.KeyIdentifier = keyIdentifier;
            }
            if (generateTargettableSignature)
            {
                this.signedXml.Id = GenerateId();
            }
            this.effectiveSignatureParts = signatureParts;
            this.hashStream = this.signedInfo.ResourcePool.TakeHashStream(digestAlgorithm);
        }
 
        protected override ISignatureValueSecurityElement CreateSupportingSignature(SecurityToken token, SecurityKeyIdentifier identifier)
        {
            StartPrimarySignatureCore(token, identifier, MessagePartSpecification.NoParts, false);
            return CompletePrimarySignatureCore(null, null, null, null, false);
        }
 
        protected override ISignatureValueSecurityElement CreateSupportingSignature(SecurityToken token, SecurityKeyIdentifier identifier, ISecurityElement elementToSign)
        {
            SecurityAlgorithmSuite algorithmSuite = this.AlgorithmSuite;
            string signatureAlgorithm;
            XmlDictionaryString signatureAlgorithmDictionaryString;
            SecurityKey signatureKey;
            algorithmSuite.GetSignatureAlgorithmAndKey(token, out signatureAlgorithm, out signatureKey, out signatureAlgorithmDictionaryString);
            SignedXml signedXml = new SignedXml(ServiceModelDictionaryManager.Instance, this.StandardsManager.SecurityTokenSerializer);
            SignedInfo signedInfo = signedXml.Signature.SignedInfo;
            signedInfo.CanonicalizationMethod = algorithmSuite.DefaultCanonicalizationAlgorithm;
            signedInfo.CanonicalizationMethodDictionaryString = algorithmSuite.DefaultCanonicalizationAlgorithmDictionaryString;
            signedInfo.SignatureMethod = signatureAlgorithm;
            signedInfo.SignatureMethodDictionaryString = signatureAlgorithmDictionaryString;
 
            if (elementToSign.Id == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ElementToSignMustHaveId)));
            }
            Reference reference = new Reference(ServiceModelDictionaryManager.Instance, "#" + elementToSign.Id, elementToSign);
            reference.DigestMethod = algorithmSuite.DefaultDigestAlgorithm;
            reference.DigestMethodDictionaryString = algorithmSuite.DefaultDigestAlgorithmDictionaryString;
            reference.AddTransform(new ExclusiveCanonicalizationTransform());
            ((StandardSignedInfo)signedInfo).AddReference(reference);
 
            signedXml.ComputeSignature(signatureKey);
            if (identifier != null)
            {
                signedXml.Signature.KeyIdentifier = identifier;
            }
            return signedXml;
        }
 
          protected override void WriteSecurityTokenReferencyEntry(XmlDictionaryWriter writer, SecurityToken securityToken, SecurityTokenParameters securityTokenParameters)
        {
            SecurityKeyIdentifierClause keyIdentifierClause = null;
 
            // Given a token this method writes its corresponding security token reference entry in the security header 
            // 1. If the token parameters is an issuedSecurityTokenParamter 
            // 2. If UseStrTransform is enabled on it.
 
            IssuedSecurityTokenParameters issuedSecurityTokenParameters = securityTokenParameters as IssuedSecurityTokenParameters;
            if (issuedSecurityTokenParameters == null || !issuedSecurityTokenParameters.UseStrTransform)
                return;
 
            if (this.ElementContainer.TryGetIdentifierClauseFromSecurityToken(securityToken, out keyIdentifierClause))
            {
                if (keyIdentifierClause != null && !String.IsNullOrEmpty(keyIdentifierClause.Id))
                {
                    WrappedXmlDictionaryWriter wrappedLocalWriter = new WrappedXmlDictionaryWriter(writer, keyIdentifierClause.Id);
                    this.StandardsManager.SecurityTokenSerializer.WriteKeyIdentifierClause(wrappedLocalWriter, keyIdentifierClause);
                }
                else
                    throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenManagerCannotCreateTokenReference)), this.Message);
            }
        }
    }
 
    class WrappedXmlDictionaryWriter : XmlDictionaryWriter
    {
        XmlDictionaryWriter innerWriter;
        int index;
        bool insertId;
        bool isStrReferenceElement;
        string id;
 
        public WrappedXmlDictionaryWriter(XmlDictionaryWriter writer, string id)
        {
            this.innerWriter = writer;
            this.index = 0;
            this.insertId = false;
            this.isStrReferenceElement = false;
            this.id = id;
        }
 
        public override void WriteStartAttribute(string prefix, string localName, string namespaceUri)
        {
            if (isStrReferenceElement && this.insertId && localName == XD.UtilityDictionary.IdAttribute.Value)
            {
                // This means the serializer is already writing the Id out, so we don't write it again.
                this.insertId = false;
            }
            this.innerWriter.WriteStartAttribute(prefix, localName, namespaceUri);
        }
 
        public override void WriteStartElement(string prefix, string localName, string namespaceUri)
        {
            if (isStrReferenceElement && this.insertId)
            {
                if (id != null)
                {
                    this.innerWriter.WriteAttributeString(XD.UtilityDictionary.Prefix.Value, XD.UtilityDictionary.IdAttribute, XD.UtilityDictionary.Namespace, id);
                }
 
                isStrReferenceElement = false;
                this.insertId = false;
            }
 
            index++;
 
            if (index == 1 && localName == XD.SecurityJan2004Dictionary.SecurityTokenReference.Value)
            {
                this.insertId = true;
                isStrReferenceElement = true;
            }
 
            this.innerWriter.WriteStartElement(prefix, localName, namespaceUri);
        }
 
        // Below methods simply call into innerWritter
        public override void Close()
        {
            this.innerWriter.Close();
        }
 
        public override void Flush()
        {
            this.innerWriter.Flush();
        }
 
        public override string LookupPrefix(string ns)
        {
            return this.innerWriter.LookupPrefix(ns);
        }
 
        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            this.innerWriter.WriteBase64(buffer, index, count);
        }
 
        public override void WriteCData(string text)
        {
            this.innerWriter.WriteCData(text);
        }
 
        public override void WriteCharEntity(char ch)
        {
            this.innerWriter.WriteCharEntity(ch);
        }
 
        public override void WriteChars(char[] buffer, int index, int count)
        {
            this.innerWriter.WriteChars(buffer, index, count);
        }
 
        public override void WriteComment(string text)
        {
            this.innerWriter.WriteComment(text);
        }
 
        public override void WriteDocType(string name, string pubid, string sysid, string subset)
        {
            this.innerWriter.WriteDocType(name, pubid, sysid, subset);
        }
 
        public override void WriteEndAttribute()
        {
            this.innerWriter.WriteEndAttribute();
        }
 
        public override void WriteEndDocument()
        {
            this.innerWriter.WriteEndDocument();
        }
 
        public override void WriteEndElement()
        {
            this.innerWriter.WriteEndElement();
        }
 
        public override void WriteEntityRef(string name)
        {
            this.innerWriter.WriteEntityRef(name);
        }
 
        public override void WriteFullEndElement()
        {
            this.innerWriter.WriteFullEndElement();
        }
 
        public override void WriteProcessingInstruction(string name, string text)
        {
            this.innerWriter.WriteProcessingInstruction(name, text);
        }
 
        public override void WriteRaw(string data)
        {
            this.innerWriter.WriteRaw(data);
        }
 
        public override void WriteRaw(char[] buffer, int index, int count)
        {
            this.innerWriter.WriteRaw(buffer, index, count);
        }
 
        public override void WriteStartDocument(bool standalone)
        {
            this.innerWriter.WriteStartDocument(standalone);
        }
 
        public override void WriteStartDocument()
        {
            this.innerWriter.WriteStartDocument();
        }
 
        public override WriteState WriteState
        {
            get { return this.innerWriter.WriteState; }
        }
 
        public override void WriteString(string text)
        {
            this.innerWriter.WriteString(text);
        }
 
        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            this.innerWriter.WriteSurrogateCharEntity(lowChar, highChar);
        }
 
        public override void WriteWhitespace(string ws)
        {
            this.innerWriter.WriteWhitespace(ws);
        }
    }
}