< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.IdentityModel.Tokens.X509SecurityTokenHandler
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509SecurityTokenHandler.cs
Line coverage
7%
Covered lines: 10
Uncovered lines: 124
Coverable lines: 134
Total lines: 505
Line coverage: 7.4%
Branch coverage
0%
Covered branches: 0
Total branches: 64
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
.ctor()100%11100%
.ctor(...)100%110%
.ctor(...)100%110%
CanReadKeyIdentifierClause(...)0%220%
CanReadToken(...)0%440%
CanWriteKeyIdentifierClause(...)0%440%
ReadKeyIdentifierClause(...)0%220%
ReadToken(...)0%14140%
GetTokenTypeIdentifiers()100%11100%
ValidateToken(...)0%20200%
WriteKeyIdentifierClause(...)0%660%
WriteToken(...)0%880%
KerberosCertificateLogon(...)100%110%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509SecurityTokenHandler.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Collections.ObjectModel;
 7using System.Security.Claims;
 8using System.Security.Cryptography.X509Certificates;
 9using System.Security.Principal;
 10using System.Xml;
 11using CoreWCF.IdentityModel.Selectors;
 12using CoreWCF.Runtime;
 13using CoreWCF.Security;
 14using Claim = System.Security.Claims.Claim;
 15
 16namespace CoreWCF.IdentityModel.Tokens
 17{
 18    /// <summary>
 19    /// SecurityTokenHandler for X509SecurityToken. By default, the
 20    /// handler will do chain-trust validation of the Certificate.
 21    /// </summary>
 22    public class X509SecurityTokenHandler : SecurityTokenHandler
 23    {
 24        private X509CertificateValidator _certificateValidator;
 425        private readonly X509DataSecurityKeyIdentifierClauseSerializer _x509DataKeyIdentifierClauseSerializer = new X509
 26
 27        /// <summary>
 28        /// Creates an instance of <see cref="X509SecurityTokenHandler"/>. MapToWindows is defaulted to false.
 29        /// Uses <see cref="X509CertificateValidator.PeerOrChainTrust"/> as the default certificate validator.
 30        /// </summary>
 31        public X509SecurityTokenHandler()
 432            : this(false, null)
 33        {
 434        }
 35
 36        /// <summary>
 37        /// Creates an instance of <see cref="X509SecurityTokenHandler"/> with an X509 certificate validator.
 38        /// MapToWindows is to false by default.
 39        /// </summary>
 40        /// <param name="certificateValidator">The certificate validator.</param>
 41        public X509SecurityTokenHandler(X509CertificateValidator certificateValidator)
 042            : this(false, certificateValidator)
 43        {
 044        }
 45
 46        /// <summary>
 47        /// Creates an instance of <see cref="X509SecurityTokenHandler"/>. Uses <see cref="X509CertificateValidator.Peer
 48        /// as the default certificate validator.
 49        /// </summary>
 50        /// <param name="mapToWindows">Boolean to indicate if the certificate should be mapped to a
 51        /// windows account. Default is false.</param>
 52        public X509SecurityTokenHandler(bool mapToWindows)
 053            : this(mapToWindows, null)
 54        {
 055        }
 56
 57        /// <summary>
 58        /// Creates an instance of <see cref="X509SecurityTokenHandler"/>.
 59        /// </summary>
 60        /// <param name="mapToWindows">Boolean to indicate if the certificate should be mapped to a windows account.</pa
 61        /// <param name="certificateValidator">The certificate validator.</param>
 462        public X509SecurityTokenHandler(bool mapToWindows, X509CertificateValidator certificateValidator)
 63        {
 464            MapToWindows = mapToWindows;
 465            _certificateValidator = certificateValidator;
 466        }
 67
 68        /// <summary>
 69        /// Gets or sets a value indicating whether if the validating token should be mapped to a
 70        /// Windows account.
 71        /// </summary>
 472        public bool MapToWindows { get; set; }
 73
 74        /// <summary>
 75        /// Gets or sets the X509CeritificateValidator that is used by the current instance.
 76        /// </summary>
 77        public X509CertificateValidator CertificateValidator
 78        {
 79            get
 80            {
 081                if (_certificateValidator == null)
 82                {
 083                    if (Configuration != null)
 84                    {
 085                        return Configuration.CertificateValidator;
 86                    }
 87                    else
 88                    {
 089                        return null;
 90                    }
 91                }
 92                else
 93                {
 094                    return _certificateValidator;
 95                }
 96            }
 97
 98            set
 99            {
 0100                _certificateValidator = value;
 0101            }
 102        }
 103
 104        /// <summary>
 105        /// Gets or sets the X509NTAuthChainTrustValidator that is used by the current instance during certificate valid
 106        /// </summary>
 107        //public X509NTAuthChainTrustValidator X509NTAuthChainTrustValidator
 108        //{
 109        //    get
 110        //    {
 111        //        return this.x509NTAuthChainTrustValidator;
 112        //    }
 113
 114        //    set
 115        //    {
 116        //        this.x509NTAuthChainTrustValidator = value;
 117        //    }
 118        //}
 119
 120        /// <summary>
 121        /// Gets or sets a value indicating whether XmlDsig defined clause types are
 122        /// preferred. Supported XmlDSig defined SecurityKeyIdentifierClause types
 123        /// are,
 124        /// 1. X509IssuerSerial
 125        /// 2. X509SKI
 126        /// 3. X509Certificate
 127        /// </summary>
 0128        public bool WriteXmlDSigDefinedClauseTypes { get; set; }
 129
 130        /// <summary>
 131        /// Gets a boolean indicating if the handler can validate tokens.
 132        /// Returns true by default.
 133        /// </summary>
 134        public override bool CanValidateToken
 135        {
 136            get
 137            {
 0138                return true;
 139            }
 140        }
 141
 142        /// <summary>
 143        /// Gets a boolean indicating if the handler can write tokens.
 144        /// Returns true by default.
 145        /// </summary>
 146        public override bool CanWriteToken
 147        {
 148            get
 149            {
 0150                return true;
 151            }
 152        }
 153
 154        /// <summary>
 155        /// Checks if the given reader is referring to a &lt;ds:X509Data> element.
 156        /// </summary>
 157        /// <param name=nameof(reader)>XmlReader positioned at the SecurityKeyIdentifierClause. </param>
 158        /// <returns>True if the XmlReader is referring to a &lt;ds:X509Data> element.</returns>
 159        /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
 160        public override bool CanReadKeyIdentifierClause(XmlReader reader)
 161        {
 0162            if (reader == null)
 163            {
 0164                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader));
 165            }
 166
 0167            return _x509DataKeyIdentifierClauseSerializer.CanReadKeyIdentifierClause(reader);
 168        }
 169
 170        /// <summary>
 171        /// Checks if the reader points to a X.509 Security Token as defined in WS-Security.
 172        /// </summary>
 173        /// <param name=nameof(reader)>Reader pointing to the token XML.</param>
 174        /// <returns>Returns true if the element is pointing to a X.509 Security Token.</returns>
 175        /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
 176        public override bool CanReadToken(XmlReader reader)
 177        {
 0178            if (reader == null)
 179            {
 0180                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader));
 181            }
 182
 0183            if (reader.IsStartElement(WSSecurity10Constants.Elements.BinarySecurityToken, WSSecurity10Constants.Namespac
 184            {
 0185                string valueTypeUri = reader.GetAttribute(WSSecurity10Constants.Attributes.ValueType, null);
 0186                return StringComparer.Ordinal.Equals(valueTypeUri, WSSecurity10Constants.X509TokenType);
 187            }
 188
 0189            return false;
 190        }
 191
 192        /// <summary>
 193        /// Checks if the given SecurityKeyIdentifierClause can be serialized by this handler. The
 194        /// supported SecurityKeyIdentifierClause are,
 195        /// 1. <see cref="System.IdentityModel.Tokens.X509IssuerSerialKeyIdentifierClause"/>
 196        /// 2. <see cref="System.IdentityModel.Tokens.X509RawDataKeyIdentifierClause"/>
 197        /// 3. <see cref="System.IdentityModel.Tokens.X509SubjectKeyIdentifierClause"/>
 198        /// </summary>
 199        /// <param name=nameof(securityKeyIdentifierClause)>SecurityKeyIdentifierClause to be serialized.</param>
 200        /// <returns>True if the 'securityKeyIdentifierClause' is supported and if WriteXmlDSigDefinedClausTypes
 201        /// is set to true.</returns>
 202        /// <exception cref="ArgumentNullException">The parameter 'securityKeyIdentifierClause' is null.</exception>
 203        public override bool CanWriteKeyIdentifierClause(SecurityKeyIdentifierClause securityKeyIdentifierClause)
 204        {
 0205            if (securityKeyIdentifierClause == null)
 206            {
 0207                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(securityKeyIdentifierClause));
 208            }
 209
 0210            return WriteXmlDSigDefinedClauseTypes && _x509DataKeyIdentifierClauseSerializer.CanWriteKeyIdentifierClause(
 211        }
 212
 213        /// <summary>
 214        /// Gets X509SecurityToken type.
 215        /// </summary>
 216        public override Type TokenType
 217        {
 14218            get { return typeof(X509SecurityToken); }
 219        }
 220
 221        /// <summary>
 222        /// Deserializes a SecurityKeyIdentifierClause referenced by the XmlReader.
 223        /// </summary>
 224        /// <param name=nameof(reader)>XmlReader referencing the SecurityKeyIdentifierClause.</param>
 225        /// <returns>Instance of SecurityKeyIdentifierClause.</returns>
 226        /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
 227        public override SecurityKeyIdentifierClause ReadKeyIdentifierClause(XmlReader reader)
 228        {
 0229            if (reader == null)
 230            {
 0231                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader));
 232            }
 233
 0234            return _x509DataKeyIdentifierClauseSerializer.ReadKeyIdentifierClause(reader);
 235        }
 236
 237        /// <summary>
 238        /// Reads the X.509 Security token referenced by the XmlReader.
 239        /// </summary>
 240        /// <param name=nameof(reader)>XmlReader pointing to a X.509 Security token.</param>
 241        /// <returns>An instance of <see cref="X509SecurityToken"/>.</returns>
 242        /// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
 243        /// <exception cref="XmlException">XmlReader is not pointing to an valid X509SecurityToken as
 244        /// defined in WS-Security X.509 Token Profile. Or the encodingType specified is other than Base64
 245        /// or HexBinary.</exception>
 246        public override SecurityToken ReadToken(XmlReader reader)
 247        {
 0248            if (reader == null)
 249            {
 0250                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader));
 251            }
 252
 0253            XmlDictionaryReader dicReader = XmlDictionaryReader.CreateDictionaryReader(reader);
 0254            if (!dicReader.IsStartElement(WSSecurity10Constants.Elements.BinarySecurityToken, WSSecurity10Constants.Name
 255            {
 0256                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 0257                    new XmlException(
 0258                        SR.Format(
 0259                        SR.ID4065,
 0260                        WSSecurity10Constants.Elements.BinarySecurityToken,
 0261                        WSSecurity10Constants.Namespace,
 0262                        dicReader.LocalName,
 0263                        dicReader.NamespaceURI)));
 264            }
 265
 0266            string valueTypeUri = dicReader.GetAttribute(WSSecurity10Constants.Attributes.ValueType, null);
 267
 0268            if (!StringComparer.Ordinal.Equals(valueTypeUri, WSSecurity10Constants.X509TokenType))
 269            {
 0270                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 0271                    new XmlException(
 0272                        SR.Format(
 0273                        SR.ID4066,
 0274                        WSSecurity10Constants.Elements.BinarySecurityToken,
 0275                        WSSecurity10Constants.Namespace,
 0276                        WSSecurity10Constants.Attributes.ValueType,
 0277                        WSSecurity10Constants.X509TokenType,
 0278                        valueTypeUri)));
 279            }
 280
 0281            string wsuId = dicReader.GetAttribute(WSSecurityUtilityConstants.Attributes.Id, WSSecurityUtilityConstants.N
 0282            string encoding = dicReader.GetAttribute(WSSecurity10Constants.Attributes.EncodingType, null);
 283
 284            byte[] binaryData;
 0285            if (encoding == null || StringComparer.Ordinal.Equals(encoding, WSSecurity10Constants.Base64EncodingType))
 286            {
 0287                binaryData = dicReader.ReadElementContentAsBase64();
 288            }
 0289            else if (StringComparer.Ordinal.Equals(encoding, WSSecurity10Constants.HexBinaryEncodingType))
 290            {
 0291                binaryData = SoapHexBinary.Parse(dicReader.ReadElementContentAsString()).Value;
 292            }
 293            else
 294            {
 0295                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.ID4068)));
 296            }
 297
 0298            return string.IsNullOrEmpty(wsuId) ?
 0299                new X509SecurityToken(new X509Certificate2(binaryData)) :
 0300                new X509SecurityToken(new X509Certificate2(binaryData), wsuId);
 301        }
 302
 303        /// <summary>
 304        /// Gets the X.509 Security Token Type defined in WS-Security X.509 Token profile.
 305        /// </summary>
 306        /// <returns>The token type identifier.</returns>
 307        public override string[] GetTokenTypeIdentifiers()
 308        {
 8309            return new string[] { SecurityTokenTypes.X509Certificate };
 310        }
 311
 312        /// <summary>
 313        /// Validates an <see cref="X509SecurityToken"/>.
 314        /// </summary>
 315        /// <param name="token">The <see cref="X509SecurityToken"/> to validate.</param>
 316        /// <returns>A <see cref="ReadOnlyCollection{T}"/> of <see cref="ClaimsIdentity"/> representing the identities c
 317        /// <exception cref="ArgumentNullException">The parameter 'token' is null.</exception>
 318        /// <exception cref="ArgumentException">The token is not assignable from <see cref="X509SecurityToken"/>.</excep
 319        /// <exception cref="InvalidOperationException">Configuration <see cref="SecurityTokenHandlerConfiguration"/>is 
 320        /// <exception cref="SecurityTokenValidationException">The current <see cref="X509CertificateValidator"/> was un
 321        /// <exception cref="InvalidOperationException">Configuration.IssuerNameRegistry is null.</exception>
 322        /// <exception cref="SecurityTokenException">Configuration.IssuerNameRegistry return null when resolving the iss
 323        public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
 324        {
 0325            if (token == null)
 326            {
 0327                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token));
 328            }
 329
 0330            if (!(token is X509SecurityToken x509Token))
 331            {
 0332                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(token), SR.Format(SR.ID0018, typeof(
 333            }
 334
 0335            if (Configuration == null)
 336            {
 0337                throw new InvalidOperationException(SR.Format(SR.ID4274));
 338            }
 339
 340            try
 341            {
 342                // Validate the token.
 343                try
 344                {
 0345                    CertificateValidator.Validate(x509Token.Certificate);
 0346                }
 0347                catch (SecurityTokenValidationException e)
 348                {
 0349                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.Fo
 0350                        X509Util.GetCertificateId(x509Token.Certificate)), e));
 351                }
 352
 0353                if (Configuration.IssuerNameRegistry == null)
 354                {
 0355                    throw new InvalidOperationException(SR.Format(SR.ID4277));
 356                }
 357
 0358                string issuer = X509Util.GetCertificateIssuerName(x509Token.Certificate, Configuration.IssuerNameRegistr
 0359                if (string.IsNullOrEmpty(issuer))
 360                {
 0361                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.ID
 362                }
 363
 0364                ClaimsIdentity identity = null;
 365
 0366                if (!MapToWindows)
 367                {
 0368                    identity = new ClaimsIdentity("X509");
 369
 370                    // PARTIAL TRUST: will fail when adding claims, AddClaim is SecurityCritical.
 0371                    identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.X509));
 372                }
 373                else
 374                {
 375                    WindowsIdentity windowsIdentity;
 376
 377                    // if this is the case, then the user has already been mapped to a windows account, just return the 
 0378                    if (token is X509WindowsSecurityToken x509WindowsSecurityToken && x509WindowsSecurityToken.WindowsId
 379                    {
 380                        // X509WindowsSecurityToken is disposable, make a copy.
 0381                        windowsIdentity = new WindowsIdentity(x509WindowsSecurityToken.WindowsIdentity.Token, x509Window
 382                    }
 383                    else
 384                    {
 0385                        throw new NotImplementedException();
 386                        // Ensure NT_AUTH chain policy for certificate account mapping
 387                        //if (this.x509NTAuthChainTrustValidator == null)
 388                        //{
 389                        //    lock (this.lockObject)
 390                        //    {
 391                        //        if (this.x509NTAuthChainTrustValidator == null)
 392                        //        {
 393                        //            this.x509NTAuthChainTrustValidator = new X509NTAuthChainTrustValidator();
 394                        //        }
 395                        //    }
 396                        //}
 397
 398                        //this.x509NTAuthChainTrustValidator.Validate(x509Token.Certificate);
 399                        //windowsIdentity = ClaimsHelper.CertificateLogon(x509Token.Certificate);
 400                    }
 401
 402                    // PARTIAL TRUST: will fail when adding claims, AddClaim is SecurityCritical.
 0403                    windowsIdentity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.X509));
 0404                    identity = windowsIdentity;
 405                }
 406
 0407                if (Configuration.SaveBootstrapContext)
 408                {
 0409                    identity.BootstrapContext = new BootstrapContext(token, this);
 410                }
 411
 0412                identity.AddClaim(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(DateTime.UtcNow, DateT
 0413                identity.AddClaims(X509Util.GetClaimsFromCertificate(x509Token.Certificate, issuer));
 414
 415                //this.TraceTokenValidationSuccess(token);
 0416                List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1)
 0417                {
 0418                    identity
 0419                };
 0420                return identities.AsReadOnly();
 421            }
 422            catch (Exception e)
 423            {
 0424                if (Fx.IsFatal(e))
 425                {
 0426                    throw;
 427                }
 428
 429               // this.TraceTokenValidationFailure(token, e.Message);
 0430                throw e;
 431            }
 0432        }
 433
 434        /// <summary>
 435        /// Serializes a given SecurityKeyIdentifierClause to the XmlWriter.
 436        /// </summary>
 437        /// <param name="writer">XmlWriter to which the 'securityKeyIdentifierClause' should be serialized.</param>
 438        /// <param name=nameof(securityKeyIdentifierClause)>SecurityKeyIdentifierClause to serialize.</param>
 439        /// <exception cref="ArgumentNullException">Input parameter 'wrtier' or 'securityKeyIdentifierClause' is null.</
 440        /// <exception cref="InvalidOperationException">The property WriteXmlDSigDefinedClauseTypes is false.</exception
 441        public override void WriteKeyIdentifierClause(XmlWriter writer, SecurityKeyIdentifierClause securityKeyIdentifie
 442        {
 0443            if (writer == null)
 444            {
 0445                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer));
 446            }
 447
 0448            if (securityKeyIdentifierClause == null)
 449            {
 0450                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(securityKeyIdentifierClause));
 451            }
 452
 0453            if (!WriteXmlDSigDefinedClauseTypes)
 454            {
 0455                throw new InvalidOperationException(SR.Format(SR.ID4261));
 456            }
 457
 0458            _x509DataKeyIdentifierClauseSerializer.WriteKeyIdentifierClause(writer, securityKeyIdentifierClause);
 0459        }
 460
 461        /// <summary>
 462        /// Writes the X509SecurityToken to the given XmlWriter.
 463        /// </summary>
 464        /// <param name="writer">XmlWriter to write the token into.</param>
 465        /// <param name="token">The SecurityToken of type X509SecurityToken to be written.</param>
 466        /// <exception cref="ArgumentNullException">The parameter 'writer' or 'token' is null.</exception>
 467        /// <exception cref="ArgumentException">The token is not of type X509SecurityToken.</exception>
 468        public override void WriteToken(XmlWriter writer, SecurityToken token)
 469        {
 0470            if (writer == null)
 471            {
 0472                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer));
 473            }
 474
 0475            if (token == null)
 476            {
 0477                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token));
 478            }
 479
 0480            if (!(token is X509SecurityToken x509Token))
 481            {
 0482                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(token), SR.Format(SR.ID0018, typeof(
 483            }
 484
 0485            writer.WriteStartElement(WSSecurity10Constants.Elements.BinarySecurityToken, WSSecurity10Constants.Namespace
 0486            if (!string.IsNullOrEmpty(x509Token.Id))
 487            {
 0488                writer.WriteAttributeString(WSSecurityUtilityConstants.Attributes.Id, WSSecurityUtilityConstants.Namespa
 489            }
 490
 0491            writer.WriteAttributeString(WSSecurity10Constants.Attributes.ValueType, null, WSSecurity10Constants.X509Toke
 0492            writer.WriteAttributeString(WSSecurity10Constants.Attributes.EncodingType, WSSecurity10Constants.Base64Encod
 493
 0494            byte[] rawData = x509Token.Certificate.GetRawCertData();
 0495            writer.WriteBase64(rawData, 0, rawData.Length);
 0496            writer.WriteEndElement();
 0497        }
 498
 499        internal static WindowsIdentity KerberosCertificateLogon(X509Certificate2 certificate)
 500        {
 0501            throw new NotSupportedException();
 502            //return X509SecurityTokenAuthenticator.KerberosCertificateLogon(certificate);
 503        }
 504    }
 505}