File: System\ServiceModel\Security\SecurityHeaderTokenResolver.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.IdentityModel.Tokens;
using System.ServiceModel;
using System.IdentityModel.Selectors;
using System.ServiceModel.Security.Tokens;
using System.IO;
using System.Globalization;
using System.Security.Cryptography.X509Certificates;
 
namespace System.ServiceModel.Security
{
    internal sealed class SecurityHeaderTokenResolver : SecurityTokenResolver, IdentityModel.IWrappedTokenKeyResolver
    {
        private const int InitialTokenArraySize = 10;
        private int _tokenCount;
        private SecurityTokenEntry[] _tokens;
        private ReceiveSecurityHeader _securityHeader;
 
        public SecurityHeaderTokenResolver()
            : this(null)
        {
        }
 
        public SecurityHeaderTokenResolver(ReceiveSecurityHeader securityHeader)
        {
            _tokens = new SecurityTokenEntry[InitialTokenArraySize];
            _securityHeader = securityHeader;
        }
 
        public SecurityToken ExpectedWrapper { get; set; }
 
        public SecurityTokenParameters ExpectedWrapperTokenParameters { get; set; }
 
        public void Add(SecurityToken token)
        {
            Add(token, SecurityTokenReferenceStyle.Internal, null);
        }
 
        public void Add(SecurityToken token, SecurityTokenReferenceStyle allowedReferenceStyle, SecurityTokenParameters tokenParameters)
        {
            if (token == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token));
            }
 
            if ((allowedReferenceStyle == SecurityTokenReferenceStyle.External) && (tokenParameters == null))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SRP.ResolvingExternalTokensRequireSecurityTokenParameters);
            }
 
            EnsureCapacityToAddToken();
            _tokens[_tokenCount++] = new SecurityTokenEntry(token, tokenParameters, allowedReferenceStyle);
        }
 
        private void EnsureCapacityToAddToken()
        {
            if (_tokenCount == _tokens.Length)
            {
                SecurityTokenEntry[] newTokens = new SecurityTokenEntry[_tokens.Length * 2];
                Array.Copy(_tokens, 0, newTokens, 0, _tokenCount);
                _tokens = newTokens;
            }
        }
 
        public bool CheckExternalWrapperMatch(SecurityKeyIdentifier keyIdentifier)
        {
            if (ExpectedWrapper == null || ExpectedWrapperTokenParameters == null)
            {
                return false;
            }
 
            for (int i = 0; i < keyIdentifier.Count; i++)
            {
                if (ExpectedWrapperTokenParameters.MatchesKeyIdentifierClause(ExpectedWrapper, keyIdentifier[i], SecurityTokenReferenceStyle.External))
                {
                    return true;
                }
            }
            return false;
        }
 
        internal SecurityToken ResolveToken(SecurityKeyIdentifier keyIdentifier, bool matchOnlyExternalTokens, bool resolveIntrinsicKeyClause)
        {
            if (keyIdentifier == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifier));
            }
            for (int i = 0; i < keyIdentifier.Count; i++)
            {
                SecurityToken token = ResolveToken(keyIdentifier[i], matchOnlyExternalTokens, resolveIntrinsicKeyClause);
                if (token != null)
                {
                    return token;
                }
            }
            return null;
        }
 
        private SecurityKey ResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, bool createIntrinsicKeys)
        {
            if (keyIdentifierClause == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(keyIdentifierClause)));
            }
 
            SecurityKey securityKey;
            for (int i = 0; i < _tokenCount; i++)
            {
                securityKey = _tokens[i].Token.ResolveKeyIdentifierClause(keyIdentifierClause);
                if (securityKey != null)
                {
                    return securityKey;
                }
            }
 
            if (createIntrinsicKeys)
            {
                if (SecurityUtils.TryCreateKeyFromIntrinsicKeyClause(keyIdentifierClause, this, out securityKey))
                {
                    return securityKey;
                }
            }
 
            return null;
        }
 
        private bool MatchDirectReference(SecurityToken token, SecurityKeyIdentifierClause keyClause)
        {
            LocalIdKeyIdentifierClause localClause = keyClause as LocalIdKeyIdentifierClause;
            if (localClause == null)
            {
                return false;
            }
 
            return token.MatchesKeyIdentifierClause(localClause);
        }
 
        internal SecurityToken ResolveToken(SecurityKeyIdentifierClause keyIdentifierClause, bool matchOnlyExternal, bool resolveIntrinsicKeyClause)
        {
            if (keyIdentifierClause == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifierClause));
            }
 
            SecurityToken resolvedToken = null;
            for (int i = 0; i < _tokenCount; i++)
            {
                if (matchOnlyExternal && _tokens[i].AllowedReferenceStyle != SecurityTokenReferenceStyle.External)
                {
                    continue;
                }
 
                SecurityToken token = _tokens[i].Token;
                if (_tokens[i].TokenParameters != null && _tokens[i].TokenParameters.MatchesKeyIdentifierClause(token, keyIdentifierClause, _tokens[i].AllowedReferenceStyle))
                {
                    resolvedToken = token;
                    break;
                }
                else if (_tokens[i].TokenParameters == null)
                {
                    // match it according to the allowed reference style
                    if (_tokens[i].AllowedReferenceStyle == SecurityTokenReferenceStyle.Internal && MatchDirectReference(token, keyIdentifierClause))
                    {
                        resolvedToken = token;
                        break;
                    }
                }
            }
 
            if ((resolvedToken == null) && (keyIdentifierClause is X509RawDataKeyIdentifierClause) && (!matchOnlyExternal) && (resolveIntrinsicKeyClause))
            {
                resolvedToken = new X509SecurityToken(new X509Certificate2(((X509RawDataKeyIdentifierClause)keyIdentifierClause).GetX509RawData()));
            }
 
            byte[] derivationNonce = keyIdentifierClause.GetDerivationNonce();
            if ((resolvedToken != null) && (derivationNonce != null))
            {
                // A Implicit Derived Key is specified. Create a derived key off of the resolve token.
                if (SecurityUtils.GetSecurityKey<SymmetricSecurityKey>(resolvedToken) == null)
                {
                    // The resolved token contains no Symmetric Security key and thus we cannot create 
                    // a derived key off of it.
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRP.Format(SRP.UnableToDeriveKeyFromKeyInfoClause, keyIdentifierClause, resolvedToken)));
                }
 
                int derivationLength = (keyIdentifierClause.DerivationLength == 0) ? DerivedKeySecurityToken.DefaultDerivedKeyLength : keyIdentifierClause.DerivationLength;
                if (derivationLength > _securityHeader.MaxDerivedKeyLength)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRP.Format(SRP.DerivedKeyLengthSpecifiedInImplicitDerivedKeyClauseTooLong, keyIdentifierClause.ToString(), derivationLength, _securityHeader.MaxDerivedKeyLength)));
                }
 
                bool alreadyDerived = false;
                for (int i = 0; i < _tokenCount; ++i)
                {
                    DerivedKeySecurityToken derivedKeyToken = _tokens[i].Token as DerivedKeySecurityToken;
                    if (derivedKeyToken != null)
                    {
                        if ((derivedKeyToken.Length == derivationLength) &&
                            (SecurityUtils.IsEqual(derivedKeyToken.Nonce, derivationNonce)) &&
                            (derivedKeyToken.TokenToDerive.MatchesKeyIdentifierClause(keyIdentifierClause)))
                        {
                            // This is a implcit derived key for which we have already derived the token.
                            resolvedToken = _tokens[i].Token;
                            alreadyDerived = true;
                            break;
                        }
                    }
                }
 
                if (!alreadyDerived)
                {
                    string psha1Algorithm = SecurityUtils.GetKeyDerivationAlgorithm(_securityHeader.StandardsManager.MessageSecurityVersion.SecureConversationVersion);
 
                    resolvedToken = new DerivedKeySecurityToken(-1, 0, derivationLength, null, derivationNonce, resolvedToken, keyIdentifierClause, psha1Algorithm, SecurityUtils.GenerateId());
                    ((DerivedKeySecurityToken)resolvedToken).InitializeDerivedKey(derivationLength);
                    Add(resolvedToken, SecurityTokenReferenceStyle.Internal, null);
                    _securityHeader.EnsureDerivedKeyLimitNotReached();
                }
            }
 
            return resolvedToken;
        }
 
        public override string ToString()
        {
            using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
            {
                writer.WriteLine("SecurityTokenResolver");
                writer.WriteLine("    (");
                writer.WriteLine("    TokenCount = {0},", _tokenCount);
                for (int i = 0; i < _tokenCount; i++)
                {
                    writer.WriteLine("    TokenEntry[{0}] = (AllowedReferenceStyle={1}, Token={2}, Parameters={3})",
                        i, _tokens[i].AllowedReferenceStyle, _tokens[i].Token.GetType(), _tokens[i].TokenParameters);
                }
                writer.WriteLine("    )");
                return writer.ToString();
            }
        }
 
        protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)
        {
            token = ResolveToken(keyIdentifier, false, true);
            return token != null;
        }
 
        internal bool TryResolveToken(SecurityKeyIdentifier keyIdentifier, bool matchOnlyExternalTokens, bool resolveIntrinsicKeyClause, out SecurityToken token)
        {
            token = ResolveToken(keyIdentifier, matchOnlyExternalTokens, resolveIntrinsicKeyClause);
            return token != null;
        }
 
        protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)
        {
            token = ResolveToken(keyIdentifierClause, false, true);
            return token != null;
        }
 
        internal bool TryResolveToken(SecurityKeyIdentifierClause keyIdentifierClause, bool matchOnlyExternalTokens, bool resolveIntrinsicKeyClause, out SecurityToken token)
        {
            token = ResolveToken(keyIdentifierClause, matchOnlyExternalTokens, resolveIntrinsicKeyClause);
            return token != null;
        }
 
        internal bool TryResolveSecurityKey(SecurityKeyIdentifierClause keyIdentifierClause, bool createIntrinsicKeys, out SecurityKey key)
        {
            if (keyIdentifierClause == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifierClause));
            }
            key = ResolveSecurityKeyCore(keyIdentifierClause, createIntrinsicKeys);
            return key != null;
        }
 
        protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)
        {
            key = ResolveSecurityKeyCore(keyIdentifierClause, true);
            return key != null;
        }
 
        private struct SecurityTokenEntry
        {
            private SecurityTokenReferenceStyle _allowedReferenceStyle;
 
            public SecurityTokenEntry(SecurityToken token, SecurityTokenParameters tokenParameters, SecurityTokenReferenceStyle allowedReferenceStyle)
            {
                Token = token;
                TokenParameters = tokenParameters;
                _allowedReferenceStyle = allowedReferenceStyle;
            }
 
            public SecurityToken Token { get; }
 
            public SecurityTokenParameters TokenParameters { get; }
 
            public SecurityTokenReferenceStyle AllowedReferenceStyle
            {
                get { return _allowedReferenceStyle; }
            }
        }
    }
}