File: System\ServiceModel\Security\SecurityProtocol.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.
 
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Runtime;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security.Tokens;
using System.Threading.Tasks;
 
namespace System.ServiceModel.Security
{
    // See SecurityProtocolFactory for contracts on subclasses etc
 
    // SecureOutgoingMessage and VerifyIncomingMessage take message as
    // ref parameters (instead of taking a message and returning a
    // message) to reduce the likelihood that a caller will forget to
    // do the rest of the processing with the modified message object.
    // Especially, on the sender-side, not sending the modified
    // message will result in sending it with an unencrypted body.
    // Correspondingly, the async versions have out parameters instead
    // of simple return values.
    internal abstract class SecurityProtocol : ISecurityCommunicationObject
    {
        private static ReadOnlyCollection<SupportingTokenProviderSpecification> s_emptyTokenProviders;
        private Dictionary<string, Collection<SupportingTokenProviderSpecification>> _mergedSupportingTokenProvidersMap;
        private ChannelParameterCollection _channelParameters;
 
        protected SecurityProtocol(SecurityProtocolFactory factory, EndpointAddress target, Uri via)
        {
            SecurityProtocolFactory = factory;
            Target = target;
            Via = via;
            CommunicationObject = new WrapperSecurityCommunicationObject(this);
        }
 
        protected WrapperSecurityCommunicationObject CommunicationObject { get; }
 
        public SecurityProtocolFactory SecurityProtocolFactory { get; }
 
        public EndpointAddress Target { get; }
 
        public Uri Via { get; }
 
        public ICollection<SupportingTokenProviderSpecification> ChannelSupportingTokenProviderSpecification { get; private set; }
 
        public Dictionary<string, ICollection<SupportingTokenProviderSpecification>> ScopedSupportingTokenProviderSpecification { get; private set; }
 
        private static ReadOnlyCollection<SupportingTokenProviderSpecification> EmptyTokenProviders
        {
            get
            {
                if (s_emptyTokenProviders == null)
                {
                    s_emptyTokenProviders = new ReadOnlyCollection<SupportingTokenProviderSpecification>(new List<SupportingTokenProviderSpecification>());
                }
                return s_emptyTokenProviders;
            }
        }
 
        public ChannelParameterCollection ChannelParameters
        {
            get
            {
                return _channelParameters;
            }
            set
            {
                CommunicationObject.ThrowIfDisposedOrImmutable();
                _channelParameters = value;
            }
        }
 
        // ISecurityCommunicationObject members
        public TimeSpan DefaultOpenTimeout
        {
            get { return ServiceDefaults.OpenTimeout; }
        }
 
        public TimeSpan DefaultCloseTimeout
        {
            get { return ServiceDefaults.CloseTimeout; }
        }
 
        public void OnClosed() { }
 
        public void OnClosing() { }
 
        public void OnFaulted() { }
 
        public void OnOpened() { }
 
        public void OnOpening() { }
 
        internal IList<SupportingTokenProviderSpecification> GetSupportingTokenProviders(string action)
        {
            if (_mergedSupportingTokenProvidersMap != null && _mergedSupportingTokenProvidersMap.Count > 0)
            {
                if (action != null && _mergedSupportingTokenProvidersMap.ContainsKey(action))
                {
                    return _mergedSupportingTokenProvidersMap[action];
                }
                else if (_mergedSupportingTokenProvidersMap.ContainsKey(MessageHeaders.WildcardAction))
                {
                    return _mergedSupportingTokenProvidersMap[MessageHeaders.WildcardAction];
                }
            }
            // return null if the token providers list is empty - this gets a perf benefit since calling Count is expensive for an empty
            // ReadOnlyCollection
            return (ChannelSupportingTokenProviderSpecification == EmptyTokenProviders) ? null : (IList<SupportingTokenProviderSpecification>)ChannelSupportingTokenProviderSpecification;
        }
 
        protected InitiatorServiceModelSecurityTokenRequirement CreateInitiatorSecurityTokenRequirement()
        {
            InitiatorServiceModelSecurityTokenRequirement requirement = new InitiatorServiceModelSecurityTokenRequirement();
            requirement.TargetAddress = Target;
            requirement.Via = Via;
            requirement.SecurityBindingElement = SecurityProtocolFactory.SecurityBindingElement;
            requirement.SecurityAlgorithmSuite = SecurityProtocolFactory.OutgoingAlgorithmSuite;
            requirement.MessageSecurityVersion = SecurityProtocolFactory.MessageSecurityVersion.SecurityTokenVersion;
            if (_channelParameters != null)
            {
                requirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty] = _channelParameters;
            }
            return requirement;
        }
 
        private InitiatorServiceModelSecurityTokenRequirement CreateInitiatorSecurityTokenRequirement(SecurityTokenParameters parameters, SecurityTokenAttachmentMode attachmentMode)
        {
            InitiatorServiceModelSecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement();
            parameters.InitializeSecurityTokenRequirement(requirement);
            requirement.KeyUsage = SecurityKeyUsage.Signature;
            requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Output;
            requirement.Properties[ServiceModelSecurityTokenRequirement.SupportingTokenAttachmentModeProperty] = attachmentMode;
            return requirement;
        }
 
        private void AddSupportingTokenProviders(SupportingTokenParameters supportingTokenParameters, bool isOptional, IList<SupportingTokenProviderSpecification> providerSpecList)
        {
            for (int i = 0; i < supportingTokenParameters.Endorsing.Count; ++i)
            {
                SecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.Endorsing[i], SecurityTokenAttachmentMode.Endorsing);
                try
                {
                    if (isOptional)
                    {
                        requirement.IsOptionalToken = true;
                    }
                    SecurityTokenProvider provider = SecurityProtocolFactory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
                    if (provider == null)
                    {
                        continue;
                    }
                    SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.Endorsing, supportingTokenParameters.Endorsing[i]);
                    providerSpecList.Add(providerSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
 
            for (int i = 0; i < supportingTokenParameters.SignedEndorsing.Count; ++i)
            {
                SecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.SignedEndorsing[i], SecurityTokenAttachmentMode.SignedEndorsing);
                try
                {
                    if (isOptional)
                    {
                        requirement.IsOptionalToken = true;
                    }
                    SecurityTokenProvider provider = SecurityProtocolFactory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
                    if (provider == null)
                    {
                        continue;
                    }
                    SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.SignedEndorsing, supportingTokenParameters.SignedEndorsing[i]);
                    providerSpecList.Add(providerSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
 
            for (int i = 0; i < supportingTokenParameters.SignedEncrypted.Count; ++i)
            {
                SecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.SignedEncrypted[i], SecurityTokenAttachmentMode.SignedEncrypted);
                try
                {
                    if (isOptional)
                    {
                        requirement.IsOptionalToken = true;
                    }
                    SecurityTokenProvider provider = SecurityProtocolFactory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
                    if (provider == null)
                    {
                        continue;
                    }
                    SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.SignedEncrypted, supportingTokenParameters.SignedEncrypted[i]);
                    providerSpecList.Add(providerSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
 
            for (int i = 0; i < supportingTokenParameters.Signed.Count; ++i)
            {
                SecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement(supportingTokenParameters.Signed[i], SecurityTokenAttachmentMode.Signed);
                try
                {
                    if (isOptional)
                    {
                        requirement.IsOptionalToken = true;
                    }
                    SecurityTokenProvider provider = SecurityProtocolFactory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
                    if (provider == null)
                    {
                        continue;
                    }
                    SupportingTokenProviderSpecification providerSpec = new SupportingTokenProviderSpecification(provider, SecurityTokenAttachmentMode.Signed, supportingTokenParameters.Signed[i]);
                    providerSpecList.Add(providerSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
        }
 
        private async Task MergeSupportingTokenProvidersAsync(TimeSpan timeout)
        {
            if (ScopedSupportingTokenProviderSpecification.Count == 0)
            {
                _mergedSupportingTokenProvidersMap = null;
            }
            else
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                SecurityProtocolFactory.ExpectSupportingTokens = true;
                _mergedSupportingTokenProvidersMap = new Dictionary<string, Collection<SupportingTokenProviderSpecification>>();
                foreach (string action in ScopedSupportingTokenProviderSpecification.Keys)
                {
                    ICollection<SupportingTokenProviderSpecification> scopedProviders = ScopedSupportingTokenProviderSpecification[action];
                    if (scopedProviders == null || scopedProviders.Count == 0)
                    {
                        continue;
                    }
                    Collection<SupportingTokenProviderSpecification> mergedProviders = new Collection<SupportingTokenProviderSpecification>();
                    foreach (SupportingTokenProviderSpecification spec in ChannelSupportingTokenProviderSpecification)
                    {
                        mergedProviders.Add(spec);
                    }
                    foreach (SupportingTokenProviderSpecification spec in scopedProviders)
                    {
                        await SecurityUtils.OpenTokenProviderIfRequiredAsync(spec.TokenProvider, timeoutHelper.RemainingTime());
                        if (spec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing || spec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.SignedEndorsing)
                        {
                            if (spec.TokenParameters.RequireDerivedKeys && !spec.TokenParameters.HasAsymmetricKey)
                            {
                                SecurityProtocolFactory.ExpectKeyDerivation = true;
                            }
                        }
                        mergedProviders.Add(spec);
                    }
                    _mergedSupportingTokenProvidersMap.Add(action, mergedProviders);
                }
            }
        }
 
        public Task OpenAsync(TimeSpan timeout)
        {
            return ((IAsyncCommunicationObject)CommunicationObject).OpenAsync(timeout);
        }
 
        public virtual async Task OnOpenAsync(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            if (SecurityProtocolFactory.ActAsInitiator)
            {
                ChannelSupportingTokenProviderSpecification = new Collection<SupportingTokenProviderSpecification>();
                ScopedSupportingTokenProviderSpecification = new Dictionary<string, ICollection<SupportingTokenProviderSpecification>>();
 
                AddSupportingTokenProviders(SecurityProtocolFactory.SecurityBindingElement.EndpointSupportingTokenParameters, false, (IList<SupportingTokenProviderSpecification>)ChannelSupportingTokenProviderSpecification);
                AddSupportingTokenProviders(SecurityProtocolFactory.SecurityBindingElement.OptionalEndpointSupportingTokenParameters, true, (IList<SupportingTokenProviderSpecification>)ChannelSupportingTokenProviderSpecification);
                foreach (string action in SecurityProtocolFactory.SecurityBindingElement.OperationSupportingTokenParameters.Keys)
                {
                    Collection<SupportingTokenProviderSpecification> providerSpecList = new Collection<SupportingTokenProviderSpecification>();
                    AddSupportingTokenProviders(SecurityProtocolFactory.SecurityBindingElement.OperationSupportingTokenParameters[action], false, providerSpecList);
                    ScopedSupportingTokenProviderSpecification.Add(action, providerSpecList);
                }
 
                foreach (string action in SecurityProtocolFactory.SecurityBindingElement.OptionalOperationSupportingTokenParameters.Keys)
                {
                    Collection<SupportingTokenProviderSpecification> providerSpecList;
                    ICollection<SupportingTokenProviderSpecification> existingList;
                    if (ScopedSupportingTokenProviderSpecification.TryGetValue(action, out existingList))
                    {
                        providerSpecList = ((Collection<SupportingTokenProviderSpecification>)existingList);
                    }
                    else
                    {
                        providerSpecList = new Collection<SupportingTokenProviderSpecification>();
                        ScopedSupportingTokenProviderSpecification.Add(action, providerSpecList);
                    }
 
                    AddSupportingTokenProviders(SecurityProtocolFactory.SecurityBindingElement.OptionalOperationSupportingTokenParameters[action], true, providerSpecList);
                }
 
                if (!ChannelSupportingTokenProviderSpecification.IsReadOnly)
                {
                    if (ChannelSupportingTokenProviderSpecification.Count == 0)
                    {
                        ChannelSupportingTokenProviderSpecification = EmptyTokenProviders;
                    }
                    else
                    {
                        SecurityProtocolFactory.ExpectSupportingTokens = true;
                        foreach (SupportingTokenProviderSpecification tokenProviderSpec in ChannelSupportingTokenProviderSpecification)
                        {
                            await SecurityUtils.OpenTokenProviderIfRequiredAsync(tokenProviderSpec.TokenProvider, timeoutHelper.RemainingTime());
                            if (tokenProviderSpec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing || tokenProviderSpec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.SignedEndorsing)
                            {
                                if (tokenProviderSpec.TokenParameters.RequireDerivedKeys && !tokenProviderSpec.TokenParameters.HasAsymmetricKey)
                                {
                                    SecurityProtocolFactory.ExpectKeyDerivation = true;
                                }
                            }
                        }
                        ChannelSupportingTokenProviderSpecification =
                            new ReadOnlyCollection<SupportingTokenProviderSpecification>((Collection<SupportingTokenProviderSpecification>)ChannelSupportingTokenProviderSpecification);
                    }
                }
                // create a merged map of the per operation supporting tokens
                await MergeSupportingTokenProvidersAsync(timeoutHelper.RemainingTime());
            }
        }
 
        public Task CloseAsync(bool aborted, TimeSpan timeout)
        {
            if (aborted)
            {
                CommunicationObject.Abort();
                return Task.CompletedTask;
            }
            else
            {
                return ((IAsyncCommunicationObject)CommunicationObject).CloseAsync(timeout);
            }
        }
 
        public virtual void OnAbort()
        {
            if (SecurityProtocolFactory.ActAsInitiator)
            {
                foreach (SupportingTokenProviderSpecification spec in ChannelSupportingTokenProviderSpecification)
                {
                    SecurityUtils.AbortTokenProviderIfRequired(spec.TokenProvider);
                }
                foreach (string action in ScopedSupportingTokenProviderSpecification.Keys)
                {
                    ICollection<SupportingTokenProviderSpecification> supportingProviders = ScopedSupportingTokenProviderSpecification[action];
                    foreach (SupportingTokenProviderSpecification spec in supportingProviders)
                    {
                        SecurityUtils.AbortTokenProviderIfRequired(spec.TokenProvider);
                    }
                }
            }
        }
 
        public virtual async Task OnCloseAsync(TimeSpan timeout)
        {
            if (SecurityProtocolFactory.ActAsInitiator)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                foreach (SupportingTokenProviderSpecification spec in ChannelSupportingTokenProviderSpecification)
                {
                    await SecurityUtils.CloseTokenProviderIfRequiredAsync(spec.TokenProvider, timeoutHelper.RemainingTime());
                }
 
                foreach (string action in ScopedSupportingTokenProviderSpecification.Keys)
                {
                    ICollection<SupportingTokenProviderSpecification> supportingProviders = ScopedSupportingTokenProviderSpecification[action];
                    foreach (SupportingTokenProviderSpecification spec in supportingProviders)
                    {
                        await SecurityUtils.CloseTokenProviderIfRequiredAsync(spec.TokenProvider, timeoutHelper.RemainingTime());
                    }
                }
            }
        }
 
        private static void SetSecurityHeaderId(SendSecurityHeader securityHeader, Message message)
        {
            SecurityMessageProperty messageProperty = message.Properties.Security;
            if (messageProperty != null)
            {
                securityHeader.IdPrefix = messageProperty.SenderIdPrefix;
            }
        }
 
        private void AddSupportingTokenSpecification(SecurityMessageProperty security, IList<SecurityToken> tokens, SecurityTokenAttachmentMode attachmentMode, IDictionary<SecurityToken, ReadOnlyCollection<IAuthorizationPolicy>> tokenPoliciesMapping)
        {
            if (tokens == null || tokens.Count == 0)
            {
                return;
            }
 
            for (int i = 0; i < tokens.Count; ++i)
            {
                security.IncomingSupportingTokens.Add(new SupportingTokenSpecification(tokens[i], tokenPoliciesMapping[tokens[i]], attachmentMode));
            }
        }
 
        protected void AddSupportingTokenSpecification(SecurityMessageProperty security, IList<SecurityToken> basicTokens, IList<SecurityToken> endorsingTokens, IList<SecurityToken> signedEndorsingTokens, IList<SecurityToken> signedTokens, IDictionary<SecurityToken, ReadOnlyCollection<IAuthorizationPolicy>> tokenPoliciesMapping)
        {
            AddSupportingTokenSpecification(security, basicTokens, SecurityTokenAttachmentMode.SignedEncrypted, tokenPoliciesMapping);
            AddSupportingTokenSpecification(security, endorsingTokens, SecurityTokenAttachmentMode.Endorsing, tokenPoliciesMapping);
            AddSupportingTokenSpecification(security, signedEndorsingTokens, SecurityTokenAttachmentMode.SignedEndorsing, tokenPoliciesMapping);
            AddSupportingTokenSpecification(security, signedTokens, SecurityTokenAttachmentMode.Signed, tokenPoliciesMapping);
        }
 
        protected SendSecurityHeader CreateSendSecurityHeader(Message message, string actor, SecurityProtocolFactory factory)
        {
            return CreateSendSecurityHeader(message, actor, factory, true);
        }
 
        protected SendSecurityHeader CreateSendSecurityHeaderForTransportProtocol(Message message, string actor, SecurityProtocolFactory factory)
        {
            return CreateSendSecurityHeader(message, actor, factory, false);
        }
 
        private SendSecurityHeader CreateSendSecurityHeader(Message message, string actor, SecurityProtocolFactory factory, bool requireMessageProtection)
        {
            MessageDirection transferDirection = factory.ActAsInitiator ? MessageDirection.Input : MessageDirection.Output;
            SendSecurityHeader sendSecurityHeader = factory.StandardsManager.CreateSendSecurityHeader(
                message,
                actor, true, false,
                factory.OutgoingAlgorithmSuite, transferDirection);
            sendSecurityHeader.Layout = factory.SecurityHeaderLayout;
            sendSecurityHeader.RequireMessageProtection = requireMessageProtection;
            SetSecurityHeaderId(sendSecurityHeader, message);
            if (factory.AddTimestamp)
            {
                sendSecurityHeader.AddTimestamp(factory.TimestampValidityDuration);
            }
 
            sendSecurityHeader.StreamBufferManager = factory.StreamBufferManager;
            return sendSecurityHeader;
        }
 
        internal void AddMessageSupportingTokens(Message message, ref IList<SupportingTokenSpecification> supportingTokens)
        {
            SecurityMessageProperty supportingTokensProperty = message.Properties.Security;
            if (supportingTokensProperty != null && supportingTokensProperty.HasOutgoingSupportingTokens)
            {
                if (supportingTokens == null)
                {
                    supportingTokens = new Collection<SupportingTokenSpecification>();
                }
 
                for (int i = 0; i < supportingTokensProperty.OutgoingSupportingTokens.Count; ++i)
                {
                    SupportingTokenSpecification spec = supportingTokensProperty.OutgoingSupportingTokens[i];
                    if (spec.SecurityTokenParameters == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRP.SenderSideSupportingTokensMustSpecifySecurityTokenParameters));
                    }
                    supportingTokens.Add(spec);
                }
            }
        }
 
        internal async Task<IList<SupportingTokenSpecification>> TryGetSupportingTokensAsync(SecurityProtocolFactory factory, EndpointAddress target, Uri via, Message message, TimeSpan timeout)
        {
            IList<SupportingTokenSpecification> supportingTokens = null;
            if (!factory.ActAsInitiator)
            {
                return null;
            }
 
            if (message == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message));
            }
 
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            IList<SupportingTokenProviderSpecification> supportingTokenProviders = GetSupportingTokenProviders(message.Headers.Action);
            if (supportingTokenProviders != null && supportingTokenProviders.Count > 0)
            {
                supportingTokens = new Collection<SupportingTokenSpecification>();
                for (int i = 0; i < supportingTokenProviders.Count; ++i)
                {
                    SupportingTokenProviderSpecification spec = supportingTokenProviders[i];
                    SecurityToken supportingToken;
                    supportingToken = await spec.TokenProvider.GetTokenAsync(timeoutHelper.RemainingTime());
 
                    supportingTokens.Add(new SupportingTokenSpecification(supportingToken, EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance, spec.SecurityTokenAttachmentMode, spec.TokenParameters));
                }
            }
 
            // add any runtime supporting tokens
            AddMessageSupportingTokens(message, ref supportingTokens);
            return supportingTokens;
        }
 
        protected ReadOnlyCollection<SecurityTokenResolver> MergeOutOfBandResolvers(IList<SupportingTokenAuthenticatorSpecification> supportingAuthenticators, ReadOnlyCollection<SecurityTokenResolver> primaryResolvers)
        {
            Collection<SecurityTokenResolver> outOfBandResolvers = null;
            if (supportingAuthenticators != null && supportingAuthenticators.Count > 0)
            {
                for (int i = 0; i < supportingAuthenticators.Count; ++i)
                {
                    if (supportingAuthenticators[i].TokenResolver != null)
                    {
                        outOfBandResolvers = outOfBandResolvers ?? new Collection<SecurityTokenResolver>();
                        outOfBandResolvers.Add(supportingAuthenticators[i].TokenResolver);
                    }
                }
            }
 
            if (outOfBandResolvers != null)
            {
                if (primaryResolvers != null)
                {
                    for (int i = 0; i < primaryResolvers.Count; ++i)
                    {
                        outOfBandResolvers.Insert(0, primaryResolvers[i]);
                    }
                }
                return new ReadOnlyCollection<SecurityTokenResolver>(outOfBandResolvers);
            }
            else
            {
                return primaryResolvers ?? EmptyReadOnlyCollection<SecurityTokenResolver>.Instance;
            }
        }
 
        protected void AddSupportingTokens(SendSecurityHeader securityHeader, IList<SupportingTokenSpecification> supportingTokens)
        {
            if (supportingTokens != null)
            {
                for (int i = 0; i < supportingTokens.Count; ++i)
                {
                    SecurityToken token = supportingTokens[i].SecurityToken;
                    SecurityTokenParameters tokenParameters = supportingTokens[i].SecurityTokenParameters;
                    switch (supportingTokens[i].SecurityTokenAttachmentMode)
                    {
                        case SecurityTokenAttachmentMode.Signed:
                            securityHeader.AddSignedSupportingToken(token, tokenParameters);
                            break;
                        case SecurityTokenAttachmentMode.Endorsing:
                            securityHeader.AddEndorsingSupportingToken(token, tokenParameters);
                            break;
                        case SecurityTokenAttachmentMode.SignedEncrypted:
                            securityHeader.AddBasicSupportingToken(token, tokenParameters);
                            break;
                        case SecurityTokenAttachmentMode.SignedEndorsing:
                            securityHeader.AddSignedEndorsingSupportingToken(token, tokenParameters);
                            break;
                        default:
                            Fx.Assert("Unknown token attachment mode " + supportingTokens[i].SecurityTokenAttachmentMode.ToString());
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.Format(SRP.UnknownTokenAttachmentMode, supportingTokens[i].SecurityTokenAttachmentMode.ToString())));
                    }
                }
            }
        }
 
        internal static async Task<SecurityToken> GetTokenAsync(SecurityTokenProvider provider, EndpointAddress target, TimeSpan timeout)
        {
            if (provider == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRP.Format(SRP.TokenProviderCannotGetTokensForTarget, target)));
            }
 
            SecurityToken token = null;
 
            try
            {
                token = await provider.GetTokenAsync(timeout);
            }
            catch (SecurityTokenException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRP.Format(SRP.TokenProviderCannotGetTokensForTarget, target), exception));
            }
            catch (SecurityNegotiationException sne)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SRP.Format(SRP.TokenProviderCannotGetTokensForTarget, target), sne));
            }
 
            return token;
        }
 
        public abstract Task<Message> SecureOutgoingMessageAsync(Message message, TimeSpan timeout);
 
        // subclasses that offer correlation should override this version
        public virtual async Task<(SecurityProtocolCorrelationState, Message)> SecureOutgoingMessageAsync(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
        {
            return (null, await SecureOutgoingMessageAsync(message, timeout));
        }
 
        protected virtual void OnOutgoingMessageSecured(Message securedMessage)
        {
        }
 
        protected virtual void OnSecureOutgoingMessageFailure(Message message)
        {
        }
 
        public abstract void VerifyIncomingMessage(ref Message message, TimeSpan timeout);
 
        // subclasses that offer correlation should override this version
        public virtual SecurityProtocolCorrelationState VerifyIncomingMessage(ref Message message, TimeSpan timeout, params SecurityProtocolCorrelationState[] correlationStates)
        {
            VerifyIncomingMessage(ref message, timeout);
            return null;
        }
 
        protected virtual void OnIncomingMessageVerified(Message verifiedMessage)
        {
        }
 
        protected virtual void OnVerifyIncomingMessageFailure(Message message, Exception exception)
        {
        }
    }
}