//------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ using System.Collections.Generic; using System.Globalization; using System.Xml; using System.IdentityModel.Configuration; using System.IdentityModel.Diagnostics.Application; using System.Runtime.Diagnostics; namespace System.IdentityModel.Tokens { /// /// Implements a name service that resolves issuer tokens to strings. This class maintains a /// list of trusted issuers dictionary that maps the trust issuer certificate thumbpring to a /// issuer name. The class can only resolve X.509Certificates. The map can be configured in /// App.config/Web.Config using the following configuration settings. /// <system.identityModel> /// <issuerNameRegistry type='ConfigurationBasedIssuerNameRegistry'> /// <trustedIssuers> /// <add thumbprint='ASN.1EncodedFormOfTheThumbprint' name='MappedName' /> /// <add thumbprint='ASN.1EncodedFormOfTheThumbprint' /> /// <remove thumbprint='ASN.1EncodedFormOfTheThumbprint' /> /// < clear/> /// <trustedIssuers/> /// </issuerNameRegistry> /// </system.identityModel> /// public class ConfigurationBasedIssuerNameRegistry : IssuerNameRegistry { Dictionary _configuredTrustedIssuers = new Dictionary(new ThumbprintKeyComparer()); /// /// Creates an instance of /// public ConfigurationBasedIssuerNameRegistry() { } /// /// Custom handling of configuration elements /// /// Custom configuration to be loaded. This is the XmlElement /// that represents the map that is specified in App.config. /// The input parameter 'customConfiguration' is null. /// The configuration contains element that is not /// recognized. public override void LoadCustomConfiguration(XmlNodeList customConfiguration) { if (customConfiguration == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfiguration"); } // // We only expect a single child here - TrustedIssuers // List configNodes = XmlUtil.GetXmlElements(customConfiguration); if (configNodes.Count != 1) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7019, typeof(ConfigurationBasedIssuerNameRegistry).Name)); } XmlElement customConfigElement = configNodes[0]; if (!StringComparer.Ordinal.Equals(customConfigElement.LocalName, ConfigurationStrings.TrustedIssuers)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7002, customConfigElement.LocalName, ConfigurationStrings.TrustedIssuers)); } foreach (XmlNode node in customConfigElement.ChildNodes) { XmlElement childElement = node as XmlElement; if (childElement != null) { if (StringComparer.Ordinal.Equals(childElement.LocalName, ConfigurationStrings.Add)) { var thumbprintAttribute = childElement.Attributes.GetNamedItem(ConfigurationStrings.Thumbprint); var nameAttribute = childElement.Attributes.GetNamedItem(ConfigurationStrings.Name); if (childElement.Attributes.Count > 2 || thumbprintAttribute == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation( SR.GetString( SR.ID7010, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/{1}", customConfigElement.LocalName, childElement.LocalName), String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} and {1}", ConfigurationStrings.Thumbprint, ConfigurationStrings.Name))); } string thumbprint = thumbprintAttribute.Value; thumbprint = thumbprint.Replace(" ", ""); // add issuer name to interned strings since it will show up in many claims string issuerName = ((nameAttribute == null) || string.IsNullOrEmpty(nameAttribute.Value)) ? String.Empty : String.Intern(nameAttribute.Value); _configuredTrustedIssuers.Add(thumbprint, issuerName); } else if (StringComparer.Ordinal.Equals(childElement.LocalName, ConfigurationStrings.Remove)) { if (childElement.Attributes.Count != 1 || !StringComparer.Ordinal.Equals(childElement.Attributes[0].LocalName, ConfigurationStrings.Thumbprint)) { throw DiagnosticUtility.ThrowHelperInvalidOperation( SR.GetString( SR.ID7010, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/{1}", customConfigElement.LocalName, childElement.LocalName), ConfigurationStrings.Thumbprint)); } string thumbprint = childElement.Attributes.GetNamedItem(ConfigurationStrings.Thumbprint).Value; thumbprint = thumbprint.Replace(" ", ""); _configuredTrustedIssuers.Remove(thumbprint); } else if (StringComparer.Ordinal.Equals(childElement.LocalName, ConfigurationStrings.Clear)) { _configuredTrustedIssuers.Clear(); } else { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7002, customConfigElement.LocalName, childElement.LocalName)); } } } } /// /// Returns the issuer name of the given X509SecurityToken mapping the Certificate Thumbprint to /// a name in the configured map. /// /// SecurityToken for which the issuer name is requested. /// Issuer name if the token was registered, null otherwise. /// The input parameter 'securityToken' is null. public override string GetIssuerName(SecurityToken securityToken) { if (securityToken == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityToken"); } X509SecurityToken x509SecurityToken = securityToken as X509SecurityToken; if (x509SecurityToken != null) { string thumbprint = x509SecurityToken.Certificate.Thumbprint; if (_configuredTrustedIssuers.ContainsKey(thumbprint)) { string issuerName = _configuredTrustedIssuers[thumbprint]; issuerName = string.IsNullOrEmpty(issuerName) ? x509SecurityToken.Certificate.Subject : issuerName; if (TD.GetIssuerNameSuccessIsEnabled()) { TD.GetIssuerNameSuccess(EventTraceActivity.GetFromThreadOrCreate(), issuerName, securityToken.Id); } return issuerName; } } if (TD.GetIssuerNameFailureIsEnabled()) { TD.GetIssuerNameFailure(EventTraceActivity.GetFromThreadOrCreate(), securityToken.Id); } return null; } /// /// Gets the Dictionary of Configured Trusted Issuers. The key /// to the dictionary is the ASN.1 encoded form of the Thumbprint /// of the trusted issuer's certificate and the value is the issuer name. /// public IDictionary ConfiguredTrustedIssuers { get { return _configuredTrustedIssuers; } } /// /// Adds a trusted issuer to the collection. /// /// ASN.1 encoded form of the trusted issuer's certificate Thumbprint. /// Name of the trusted issuer. /// The argument 'certificateThumbprint' or 'name' is either null or Empty. /// The issuer specified by 'certificateThumbprint' argument has already been configured. public void AddTrustedIssuer(string certificateThumbprint, string name) { if (string.IsNullOrEmpty(certificateThumbprint)) { throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("certificateThumbprint"); } if (string.IsNullOrEmpty(name)) { throw DiagnosticUtility.ThrowHelperArgumentNullOrEmptyString("name"); } if (_configuredTrustedIssuers.ContainsKey(certificateThumbprint)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4265, certificateThumbprint)); } certificateThumbprint = certificateThumbprint.Replace(" ", ""); _configuredTrustedIssuers.Add(certificateThumbprint, name); } class ThumbprintKeyComparer : IEqualityComparer { #region IEqualityComparer Members public bool Equals(string x, string y) { return StringComparer.OrdinalIgnoreCase.Equals(x, y); } public int GetHashCode(string obj) { return obj.ToUpper(CultureInfo.InvariantCulture).GetHashCode(); } #endregion } } }