e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
679 lines
32 KiB
C#
679 lines
32 KiB
C#
//-----------------------------------------------------------------------
|
|
// <copyright file="X509SecurityTokenHandler.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//-----------------------------------------------------------------------
|
|
|
|
namespace System.IdentityModel.Tokens
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.IdentityModel.Configuration;
|
|
using System.IdentityModel.Selectors;
|
|
using System.Runtime;
|
|
using System.Runtime.Remoting.Metadata.W3cXsd2001;
|
|
using System.Security.Claims;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Security.Principal;
|
|
using System.ServiceModel.Security;
|
|
using System.Xml;
|
|
using Claim = System.Security.Claims.Claim;
|
|
|
|
/// <summary>
|
|
/// SecurityTokenHandler for X509SecurityToken. By default, the
|
|
/// handler will do chain-trust validation of the Certificate.
|
|
/// </summary>
|
|
public class X509SecurityTokenHandler : SecurityTokenHandler
|
|
{
|
|
//
|
|
// The below defaults will only be used if some verification properties are set in config and others are not
|
|
//
|
|
private static X509RevocationMode defaultRevocationMode = X509RevocationMode.Online;
|
|
private static X509CertificateValidationMode defaultValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
|
|
private static StoreLocation defaultStoreLocation = StoreLocation.LocalMachine;
|
|
private X509NTAuthChainTrustValidator x509NTAuthChainTrustValidator;
|
|
object lockObject = new object();
|
|
|
|
private bool mapToWindows;
|
|
private X509CertificateValidator certificateValidator;
|
|
private bool writeXmlDSigDefinedClauseTypes;
|
|
|
|
private X509DataSecurityKeyIdentifierClauseSerializer x509DataKeyIdentifierClauseSerializer = new X509DataSecurityKeyIdentifierClauseSerializer();
|
|
|
|
/// <summary>
|
|
/// Creates an instance of <see cref="X509SecurityTokenHandler"/>. MapToWindows is defaulted to false.
|
|
/// Uses <see cref="X509CertificateValidator.PeerOrChainTrust"/> as the default certificate validator.
|
|
/// </summary>
|
|
public X509SecurityTokenHandler()
|
|
: this(false, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an instance of <see cref="X509SecurityTokenHandler"/> with an X509 certificate validator.
|
|
/// MapToWindows is to false by default.
|
|
/// </summary>
|
|
/// <param name="certificateValidator">The certificate validator.</param>
|
|
public X509SecurityTokenHandler(X509CertificateValidator certificateValidator)
|
|
: this(false, certificateValidator)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an instance of <see cref="X509SecurityTokenHandler"/>. Uses <see cref="X509CertificateValidator.PeerOrChainTrust"/>
|
|
/// as the default certificate validator.
|
|
/// </summary>
|
|
/// <param name="mapToWindows">Boolean to indicate if the certificate should be mapped to a
|
|
/// windows account. Default is false.</param>
|
|
public X509SecurityTokenHandler(bool mapToWindows)
|
|
: this(mapToWindows, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an instance of <see cref="X509SecurityTokenHandler"/>.
|
|
/// </summary>
|
|
/// <param name="mapToWindows">Boolean to indicate if the certificate should be mapped to a windows account.</param>
|
|
/// <param name="certificateValidator">The certificate validator.</param>
|
|
public X509SecurityTokenHandler(bool mapToWindows, X509CertificateValidator certificateValidator)
|
|
{
|
|
this.mapToWindows = mapToWindows;
|
|
this.certificateValidator = certificateValidator;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load custom configuration from Xml
|
|
/// </summary>
|
|
/// <param name="customConfigElements">XmlElement to custom configuration.</param>
|
|
/// <exception cref="ArgumentNullException">The param 'customConfigElements' is null.</exception>
|
|
/// <exception cref="InvalidOperationException">Custom configuration specified was invalid.</exception>
|
|
public override void LoadCustomConfiguration(XmlNodeList customConfigElements)
|
|
{
|
|
if (customConfigElements == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfigElements");
|
|
}
|
|
|
|
List<XmlElement> configNodes = XmlUtil.GetXmlElements(customConfigElements);
|
|
|
|
bool foundValidConfig = false;
|
|
|
|
bool foundCustomX509Validator = false;
|
|
X509RevocationMode revocationMode = defaultRevocationMode;
|
|
X509CertificateValidationMode certificateValidationMode = defaultValidationMode;
|
|
StoreLocation trustedStoreLocation = defaultStoreLocation;
|
|
string customValidator = null;
|
|
|
|
foreach (XmlElement customConfigElement in configNodes)
|
|
{
|
|
if (!StringComparer.Ordinal.Equals(customConfigElement.LocalName, ConfigurationStrings.X509SecurityTokenHandlerRequirement))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (foundValidConfig)
|
|
{
|
|
throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7026, ConfigurationStrings.X509SecurityTokenHandlerRequirement));
|
|
}
|
|
|
|
foreach (XmlAttribute attribute in customConfigElement.Attributes)
|
|
{
|
|
if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.MapToWindows))
|
|
{
|
|
mapToWindows = XmlConvert.ToBoolean(attribute.Value.ToLowerInvariant());
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.X509CertificateValidator))
|
|
{
|
|
customValidator = attribute.Value.ToString();
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.X509CertificateRevocationMode))
|
|
{
|
|
foundCustomX509Validator = true;
|
|
|
|
string revocationModeString = attribute.Value.ToString();
|
|
|
|
if (StringComparer.OrdinalIgnoreCase.Equals(revocationModeString, ConfigurationStrings.X509RevocationModeNoCheck))
|
|
{
|
|
revocationMode = X509RevocationMode.NoCheck;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(revocationModeString, ConfigurationStrings.X509RevocationModeOffline))
|
|
{
|
|
revocationMode = X509RevocationMode.Offline;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(revocationModeString, ConfigurationStrings.X509RevocationModeOnline))
|
|
{
|
|
revocationMode = X509RevocationMode.Online;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7011, attribute.LocalName, customConfigElement.LocalName)));
|
|
}
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.X509CertificateValidationMode))
|
|
{
|
|
foundCustomX509Validator = true;
|
|
|
|
string validationModeString = attribute.Value.ToString();
|
|
|
|
if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModeChainTrust))
|
|
{
|
|
certificateValidationMode = X509CertificateValidationMode.ChainTrust;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModePeerOrChainTrust))
|
|
{
|
|
certificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModePeerTrust))
|
|
{
|
|
certificateValidationMode = X509CertificateValidationMode.PeerTrust;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModeNone))
|
|
{
|
|
certificateValidationMode = X509CertificateValidationMode.None;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(validationModeString, ConfigurationStrings.X509CertificateValidationModeCustom))
|
|
{
|
|
certificateValidationMode = X509CertificateValidationMode.Custom;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7011, attribute.LocalName, customConfigElement.LocalName)));
|
|
}
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.X509TrustedStoreLocation))
|
|
{
|
|
foundCustomX509Validator = true;
|
|
|
|
string trustedStoreLocationString = attribute.Value.ToString();
|
|
|
|
if (StringComparer.OrdinalIgnoreCase.Equals(trustedStoreLocationString, ConfigurationStrings.X509TrustedStoreLocationCurrentUser))
|
|
{
|
|
trustedStoreLocation = StoreLocation.CurrentUser;
|
|
}
|
|
else if (StringComparer.OrdinalIgnoreCase.Equals(trustedStoreLocationString, ConfigurationStrings.X509TrustedStoreLocationLocalMachine))
|
|
{
|
|
trustedStoreLocation = StoreLocation.LocalMachine;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7011, attribute.LocalName, customConfigElement.LocalName)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7004, attribute.LocalName, customConfigElement.LocalName)));
|
|
}
|
|
}
|
|
|
|
foundValidConfig = true;
|
|
}
|
|
|
|
if (certificateValidationMode == X509CertificateValidationMode.Custom)
|
|
{
|
|
if (String.IsNullOrEmpty(customValidator))
|
|
{
|
|
throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID7028));
|
|
}
|
|
|
|
Type customValidatorType = Type.GetType(customValidator, true);
|
|
|
|
if (customValidatorType == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.ID7007, customValidatorType));
|
|
}
|
|
|
|
certificateValidator = CustomTypeElement.Resolve<X509CertificateValidator>(new CustomTypeElement(customValidatorType));
|
|
}
|
|
else if (foundCustomX509Validator)
|
|
{
|
|
certificateValidator = X509Util.CreateCertificateValidator(certificateValidationMode, revocationMode, trustedStoreLocation);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether if the validating token should be mapped to a
|
|
/// Windows account.
|
|
/// </summary>
|
|
public bool MapToWindows
|
|
{
|
|
get { return mapToWindows; }
|
|
set { mapToWindows = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the X509CeritificateValidator that is used by the current instance.
|
|
/// </summary>
|
|
public X509CertificateValidator CertificateValidator
|
|
{
|
|
get
|
|
{
|
|
if (certificateValidator == null)
|
|
{
|
|
if (Configuration != null)
|
|
{
|
|
return Configuration.CertificateValidator;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return certificateValidator;
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
certificateValidator = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the X509NTAuthChainTrustValidator that is used by the current instance during certificate validation when the incoming certificate is mapped to windows.
|
|
/// </summary>
|
|
public X509NTAuthChainTrustValidator X509NTAuthChainTrustValidator
|
|
{
|
|
get
|
|
{
|
|
return this.x509NTAuthChainTrustValidator;
|
|
}
|
|
|
|
set
|
|
{
|
|
this.x509NTAuthChainTrustValidator = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether XmlDsig defined clause types are
|
|
/// preferred. Supported XmlDSig defined SecurityKeyIdentifierClause types
|
|
/// are,
|
|
/// 1. X509IssuerSerial
|
|
/// 2. X509SKI
|
|
/// 3. X509Certificate
|
|
/// </summary>
|
|
public bool WriteXmlDSigDefinedClauseTypes
|
|
{
|
|
get { return writeXmlDSigDefinedClauseTypes; }
|
|
set { writeXmlDSigDefinedClauseTypes = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a boolean indicating if the handler can validate tokens.
|
|
/// Returns true by default.
|
|
/// </summary>
|
|
public override bool CanValidateToken
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a boolean indicating if the handler can write tokens.
|
|
/// Returns true by default.
|
|
/// </summary>
|
|
public override bool CanWriteToken
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the given reader is referring to a <ds:X509Data> element.
|
|
/// </summary>
|
|
/// <param name="reader">XmlReader positioned at the SecurityKeyIdentifierClause. </param>
|
|
/// <returns>True if the XmlReader is referring to a <ds:X509Data> element.</returns>
|
|
/// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
|
|
public override bool CanReadKeyIdentifierClause(XmlReader reader)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
|
|
}
|
|
|
|
return x509DataKeyIdentifierClauseSerializer.CanReadKeyIdentifierClause(reader);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the reader points to a X.509 Security Token as defined in WS-Security.
|
|
/// </summary>
|
|
/// <param name="reader">Reader pointing to the token XML.</param>
|
|
/// <returns>Returns true if the element is pointing to a X.509 Security Token.</returns>
|
|
/// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
|
|
public override bool CanReadToken(XmlReader reader)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
|
|
}
|
|
|
|
if (reader.IsStartElement(WSSecurity10Constants.Elements.BinarySecurityToken, WSSecurity10Constants.Namespace))
|
|
{
|
|
string valueTypeUri = reader.GetAttribute(WSSecurity10Constants.Attributes.ValueType, null);
|
|
return StringComparer.Ordinal.Equals(valueTypeUri, WSSecurity10Constants.X509TokenType);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the given SecurityKeyIdentifierClause can be serialized by this handler. The
|
|
/// supported SecurityKeyIdentifierClause are,
|
|
/// 1. <see cref="System.IdentityModel.Tokens.X509IssuerSerialKeyIdentifierClause"/>
|
|
/// 2. <see cref="System.IdentityModel.Tokens.X509RawDataKeyIdentifierClause"/>
|
|
/// 3. <see cref="System.IdentityModel.Tokens.X509SubjectKeyIdentifierClause"/>
|
|
/// </summary>
|
|
/// <param name="securityKeyIdentifierClause">SecurityKeyIdentifierClause to be serialized.</param>
|
|
/// <returns>True if the 'securityKeyIdentifierClause' is supported and if WriteXmlDSigDefinedClausTypes
|
|
/// is set to true.</returns>
|
|
/// <exception cref="ArgumentNullException">The parameter 'securityKeyIdentifierClause' is null.</exception>
|
|
public override bool CanWriteKeyIdentifierClause(SecurityKeyIdentifierClause securityKeyIdentifierClause)
|
|
{
|
|
if (securityKeyIdentifierClause == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityKeyIdentifierClause");
|
|
}
|
|
|
|
return writeXmlDSigDefinedClauseTypes && x509DataKeyIdentifierClauseSerializer.CanWriteKeyIdentifierClause(securityKeyIdentifierClause);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets X509SecurityToken type.
|
|
/// </summary>
|
|
public override Type TokenType
|
|
{
|
|
get { return typeof(X509SecurityToken); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes a SecurityKeyIdentifierClause referenced by the XmlReader.
|
|
/// </summary>
|
|
/// <param name="reader">XmlReader referencing the SecurityKeyIdentifierClause.</param>
|
|
/// <returns>Instance of SecurityKeyIdentifierClause.</returns>
|
|
/// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception>
|
|
public override SecurityKeyIdentifierClause ReadKeyIdentifierClause(XmlReader reader)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
|
|
}
|
|
|
|
return x509DataKeyIdentifierClauseSerializer.ReadKeyIdentifierClause(reader);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the X.509 Security token referenced by the XmlReader.
|
|
/// </summary>
|
|
/// <param name="reader">XmlReader pointing to a X.509 Security token.</param>
|
|
/// <returns>An instance of <see cref="X509SecurityToken"/>.</returns>
|
|
/// <exception cref="ArgumentNullException">The parameter 'reader' is null.</exception>
|
|
/// <exception cref="XmlException">XmlReader is not pointing to an valid X509SecurityToken as
|
|
/// defined in WS-Security X.509 Token Profile. Or the encodingType specified is other than Base64
|
|
/// or HexBinary.</exception>
|
|
public override SecurityToken ReadToken(XmlReader reader)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
|
|
}
|
|
|
|
XmlDictionaryReader dicReader = XmlDictionaryReader.CreateDictionaryReader(reader);
|
|
if (!dicReader.IsStartElement(WSSecurity10Constants.Elements.BinarySecurityToken, WSSecurity10Constants.Namespace))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new XmlException(
|
|
SR.GetString(
|
|
SR.ID4065,
|
|
WSSecurity10Constants.Elements.BinarySecurityToken,
|
|
WSSecurity10Constants.Namespace,
|
|
dicReader.LocalName,
|
|
dicReader.NamespaceURI)));
|
|
}
|
|
|
|
string valueTypeUri = dicReader.GetAttribute(WSSecurity10Constants.Attributes.ValueType, null);
|
|
|
|
if (!StringComparer.Ordinal.Equals(valueTypeUri, WSSecurity10Constants.X509TokenType))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new XmlException(
|
|
SR.GetString(
|
|
SR.ID4066,
|
|
WSSecurity10Constants.Elements.BinarySecurityToken,
|
|
WSSecurity10Constants.Namespace,
|
|
WSSecurity10Constants.Attributes.ValueType,
|
|
WSSecurity10Constants.X509TokenType,
|
|
valueTypeUri)));
|
|
}
|
|
|
|
string wsuId = dicReader.GetAttribute(WSSecurityUtilityConstants.Attributes.Id, WSSecurityUtilityConstants.Namespace);
|
|
string encoding = dicReader.GetAttribute(WSSecurity10Constants.Attributes.EncodingType, null);
|
|
|
|
byte[] binaryData;
|
|
if (encoding == null || StringComparer.Ordinal.Equals(encoding, WSSecurity10Constants.Base64EncodingType))
|
|
{
|
|
binaryData = dicReader.ReadElementContentAsBase64();
|
|
}
|
|
else if (StringComparer.Ordinal.Equals(encoding, WSSecurity10Constants.HexBinaryEncodingType))
|
|
{
|
|
binaryData = SoapHexBinary.Parse(dicReader.ReadElementContentAsString()).Value;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4068)));
|
|
}
|
|
|
|
return String.IsNullOrEmpty(wsuId) ?
|
|
new X509SecurityToken(new X509Certificate2(binaryData)) :
|
|
new X509SecurityToken(new X509Certificate2(binaryData), wsuId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the X.509 Security Token Type defined in WS-Security X.509 Token profile.
|
|
/// </summary>
|
|
/// <returns>The token type identifier.</returns>
|
|
public override string[] GetTokenTypeIdentifiers()
|
|
{
|
|
return new string[] { SecurityTokenTypes.X509Certificate };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates an <see cref="X509SecurityToken"/>.
|
|
/// </summary>
|
|
/// <param name="token">The <see cref="X509SecurityToken"/> to validate.</param>
|
|
/// <returns>A <see cref="ReadOnlyCollection{T}"/> of <see cref="ClaimsIdentity"/> representing the identities contained in the token.</returns>
|
|
/// <exception cref="ArgumentNullException">The parameter 'token' is null.</exception>
|
|
/// <exception cref="ArgumentException">The token is not assignable from <see cref="X509SecurityToken"/>.</exception>
|
|
/// <exception cref="InvalidOperationException">Configuration <see cref="SecurityTokenHandlerConfiguration"/>is null.</exception>
|
|
/// <exception cref="SecurityTokenValidationException">The current <see cref="X509CertificateValidator"/> was unable to validate the certificate in the Token.</exception>
|
|
/// <exception cref="InvalidOperationException">Configuration.IssuerNameRegistry is null.</exception>
|
|
/// <exception cref="SecurityTokenException">Configuration.IssuerNameRegistry return null when resolving the issuer of the certificate in the Token.</exception>
|
|
public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
|
|
{
|
|
if (token == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
|
|
}
|
|
|
|
X509SecurityToken x509Token = token as X509SecurityToken;
|
|
if (x509Token == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID0018, typeof(X509SecurityToken)));
|
|
}
|
|
|
|
if (this.Configuration == null)
|
|
{
|
|
throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4274));
|
|
}
|
|
|
|
try
|
|
{
|
|
// Validate the token.
|
|
try
|
|
{
|
|
this.CertificateValidator.Validate(x509Token.Certificate);
|
|
}
|
|
catch (SecurityTokenValidationException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4257,
|
|
X509Util.GetCertificateId(x509Token.Certificate)), e));
|
|
}
|
|
|
|
if (this.Configuration.IssuerNameRegistry == null)
|
|
{
|
|
throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4277));
|
|
}
|
|
|
|
string issuer = X509Util.GetCertificateIssuerName(x509Token.Certificate, this.Configuration.IssuerNameRegistry);
|
|
if (String.IsNullOrEmpty(issuer))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4175)));
|
|
}
|
|
|
|
ClaimsIdentity identity = null;
|
|
|
|
if (!mapToWindows)
|
|
{
|
|
identity = new ClaimsIdentity(AuthenticationTypes.X509);
|
|
|
|
// PARTIAL TRUST: will fail when adding claims, AddClaim is SecurityCritical.
|
|
identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.X509));
|
|
}
|
|
else
|
|
{
|
|
WindowsIdentity windowsIdentity;
|
|
X509WindowsSecurityToken x509WindowsSecurityToken = token as X509WindowsSecurityToken;
|
|
|
|
// if this is the case, then the user has already been mapped to a windows account, just return the identity after adding a couple of claims.
|
|
if (x509WindowsSecurityToken != null && x509WindowsSecurityToken.WindowsIdentity != null)
|
|
{
|
|
// X509WindowsSecurityToken is disposable, make a copy.
|
|
windowsIdentity = new WindowsIdentity(x509WindowsSecurityToken.WindowsIdentity.Token, x509WindowsSecurityToken.AuthenticationType);
|
|
}
|
|
else
|
|
{
|
|
// Ensure NT_AUTH chain policy for certificate account mapping
|
|
if (this.x509NTAuthChainTrustValidator == null)
|
|
{
|
|
lock (this.lockObject)
|
|
{
|
|
if (this.x509NTAuthChainTrustValidator == null)
|
|
{
|
|
this.x509NTAuthChainTrustValidator = new X509NTAuthChainTrustValidator();
|
|
}
|
|
}
|
|
}
|
|
|
|
this.x509NTAuthChainTrustValidator.Validate(x509Token.Certificate);
|
|
windowsIdentity = ClaimsHelper.CertificateLogon(x509Token.Certificate);
|
|
}
|
|
|
|
// PARTIAL TRUST: will fail when adding claims, AddClaim is SecurityCritical.
|
|
windowsIdentity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.X509));
|
|
identity = windowsIdentity;
|
|
}
|
|
|
|
if (this.Configuration.SaveBootstrapContext)
|
|
{
|
|
identity.BootstrapContext = new BootstrapContext(token, this);
|
|
}
|
|
|
|
identity.AddClaim(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated), ClaimValueTypes.DateTime));
|
|
identity.AddClaims(X509Util.GetClaimsFromCertificate(x509Token.Certificate, issuer));
|
|
|
|
this.TraceTokenValidationSuccess(token);
|
|
|
|
List<ClaimsIdentity> identities = new List<ClaimsIdentity>(1);
|
|
identities.Add(identity);
|
|
return identities.AsReadOnly();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
this.TraceTokenValidationFailure(token, e.Message);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes a given SecurityKeyIdentifierClause to the XmlWriter.
|
|
/// </summary>
|
|
/// <param name="writer">XmlWriter to which the 'securityKeyIdentifierClause' should be serialized.</param>
|
|
/// <param name="securityKeyIdentifierClause">SecurityKeyIdentifierClause to serialize.</param>
|
|
/// <exception cref="ArgumentNullException">Input parameter 'wrtier' or 'securityKeyIdentifierClause' is null.</exception>
|
|
/// <exception cref="InvalidOperationException">The property WriteXmlDSigDefinedClauseTypes is false.</exception>
|
|
public override void WriteKeyIdentifierClause(XmlWriter writer, SecurityKeyIdentifierClause securityKeyIdentifierClause)
|
|
{
|
|
if (writer == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
|
|
}
|
|
|
|
if (securityKeyIdentifierClause == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityKeyIdentifierClause");
|
|
}
|
|
|
|
if (!writeXmlDSigDefinedClauseTypes)
|
|
{
|
|
throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4261));
|
|
}
|
|
|
|
x509DataKeyIdentifierClauseSerializer.WriteKeyIdentifierClause(writer, securityKeyIdentifierClause);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the X509SecurityToken to the given XmlWriter.
|
|
/// </summary>
|
|
/// <param name="writer">XmlWriter to write the token into.</param>
|
|
/// <param name="token">The SecurityToken of type X509SecurityToken to be written.</param>
|
|
/// <exception cref="ArgumentNullException">The parameter 'writer' or 'token' is null.</exception>
|
|
/// <exception cref="ArgumentException">The token is not of type X509SecurityToken.</exception>
|
|
public override void WriteToken(XmlWriter writer, SecurityToken token)
|
|
{
|
|
if (writer == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
|
|
}
|
|
|
|
if (token == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
|
|
}
|
|
|
|
X509SecurityToken x509Token = token as X509SecurityToken;
|
|
if (x509Token == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("token", SR.GetString(SR.ID0018, typeof(X509SecurityToken)));
|
|
}
|
|
|
|
writer.WriteStartElement(WSSecurity10Constants.Elements.BinarySecurityToken, WSSecurity10Constants.Namespace);
|
|
if (!String.IsNullOrEmpty(x509Token.Id))
|
|
{
|
|
writer.WriteAttributeString(WSSecurityUtilityConstants.Attributes.Id, WSSecurityUtilityConstants.Namespace, x509Token.Id);
|
|
}
|
|
|
|
writer.WriteAttributeString(WSSecurity10Constants.Attributes.ValueType, null, WSSecurity10Constants.X509TokenType);
|
|
writer.WriteAttributeString(WSSecurity10Constants.Attributes.EncodingType, WSSecurity10Constants.Base64EncodingType);
|
|
|
|
byte[] rawData = x509Token.Certificate.GetRawCertData();
|
|
writer.WriteBase64(rawData, 0, rawData.Length);
|
|
writer.WriteEndElement();
|
|
}
|
|
|
|
internal static WindowsIdentity KerberosCertificateLogon(X509Certificate2 certificate)
|
|
{
|
|
return X509SecurityTokenAuthenticator.KerberosCertificateLogon(certificate);
|
|
}
|
|
}
|
|
}
|