379 lines
22 KiB
C#
379 lines
22 KiB
C#
//----------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Security
|
|
{
|
|
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;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Description;
|
|
using System.ServiceModel.Security.Tokens;
|
|
|
|
sealed class AsymmetricSecurityProtocol : MessageSecurityProtocol
|
|
{
|
|
SecurityTokenAuthenticator initiatorAsymmetricTokenAuthenticator;
|
|
SecurityTokenProvider initiatorAsymmetricTokenProvider;
|
|
SecurityTokenProvider initiatorCryptoTokenProvider;
|
|
|
|
public AsymmetricSecurityProtocol(AsymmetricSecurityProtocolFactory factory,
|
|
EndpointAddress target, Uri via)
|
|
: base(factory, target, via)
|
|
{
|
|
}
|
|
|
|
protected override bool DoAutomaticEncryptionMatch
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
AsymmetricSecurityProtocolFactory Factory
|
|
{
|
|
get { return (AsymmetricSecurityProtocolFactory)base.MessageSecurityProtocolFactory; }
|
|
}
|
|
|
|
public SecurityTokenProvider InitiatorCryptoTokenProvider
|
|
{
|
|
get
|
|
{
|
|
this.CommunicationObject.ThrowIfNotOpened();
|
|
return this.initiatorCryptoTokenProvider;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenAuthenticator InitiatorAsymmetricTokenAuthenticator
|
|
{
|
|
get
|
|
{
|
|
this.CommunicationObject.ThrowIfNotOpened();
|
|
return this.initiatorAsymmetricTokenAuthenticator;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenProvider InitiatorAsymmetricTokenProvider
|
|
{
|
|
get
|
|
{
|
|
this.CommunicationObject.ThrowIfNotOpened();
|
|
return this.initiatorAsymmetricTokenProvider;
|
|
}
|
|
}
|
|
|
|
public override void OnOpen(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
base.OnOpen(timeoutHelper.RemainingTime());
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
if (this.Factory.ApplyIntegrity)
|
|
{
|
|
InitiatorServiceModelSecurityTokenRequirement requirement = CreateInitiatorSecurityTokenRequirement();
|
|
this.Factory.CryptoTokenParameters.InitializeSecurityTokenRequirement(requirement);
|
|
requirement.KeyUsage = SecurityKeyUsage.Signature;
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Output;
|
|
this.initiatorCryptoTokenProvider = this.Factory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
|
|
SecurityUtils.OpenTokenProviderIfRequired(this.initiatorCryptoTokenProvider, timeoutHelper.RemainingTime());
|
|
}
|
|
if (this.Factory.RequireIntegrity || this.Factory.ApplyConfidentiality)
|
|
{
|
|
InitiatorServiceModelSecurityTokenRequirement providerRequirement = CreateInitiatorSecurityTokenRequirement();
|
|
this.Factory.AsymmetricTokenParameters.InitializeSecurityTokenRequirement(providerRequirement);
|
|
providerRequirement.KeyUsage = SecurityKeyUsage.Exchange;
|
|
providerRequirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = (this.Factory.ApplyConfidentiality) ? MessageDirection.Output : MessageDirection.Input;
|
|
this.initiatorAsymmetricTokenProvider = this.Factory.SecurityTokenManager.CreateSecurityTokenProvider(providerRequirement);
|
|
SecurityUtils.OpenTokenProviderIfRequired(this.initiatorAsymmetricTokenProvider, timeoutHelper.RemainingTime());
|
|
|
|
InitiatorServiceModelSecurityTokenRequirement authenticatorRequirement = CreateInitiatorSecurityTokenRequirement();
|
|
this.Factory.AsymmetricTokenParameters.InitializeSecurityTokenRequirement(authenticatorRequirement);
|
|
authenticatorRequirement.IsOutOfBandToken = !this.Factory.AllowSerializedSigningTokenOnReply;
|
|
authenticatorRequirement.KeyUsage = SecurityKeyUsage.Exchange;
|
|
authenticatorRequirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = (this.Factory.ApplyConfidentiality) ? MessageDirection.Output : MessageDirection.Input;
|
|
// Create authenticator (we dont support out of band resolvers on the client side
|
|
SecurityTokenResolver outOfBandTokenResolver;
|
|
this.initiatorAsymmetricTokenAuthenticator = this.Factory.SecurityTokenManager.CreateSecurityTokenAuthenticator(authenticatorRequirement, out outOfBandTokenResolver);
|
|
SecurityUtils.OpenTokenAuthenticatorIfRequired(this.initiatorAsymmetricTokenAuthenticator, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnAbort()
|
|
{
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
if (this.initiatorCryptoTokenProvider != null)
|
|
{
|
|
SecurityUtils.AbortTokenProviderIfRequired(this.initiatorCryptoTokenProvider);
|
|
}
|
|
if (this.initiatorAsymmetricTokenProvider != null)
|
|
{
|
|
SecurityUtils.AbortTokenProviderIfRequired(this.initiatorAsymmetricTokenProvider);
|
|
}
|
|
if (this.initiatorAsymmetricTokenAuthenticator != null)
|
|
{
|
|
SecurityUtils.AbortTokenAuthenticatorIfRequired(this.initiatorAsymmetricTokenAuthenticator);
|
|
}
|
|
}
|
|
base.OnAbort();
|
|
}
|
|
|
|
public override void OnClose(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
if (this.initiatorCryptoTokenProvider != null)
|
|
{
|
|
SecurityUtils.CloseTokenProviderIfRequired(this.initiatorCryptoTokenProvider, timeoutHelper.RemainingTime());
|
|
}
|
|
if (this.initiatorAsymmetricTokenProvider != null)
|
|
{
|
|
SecurityUtils.CloseTokenProviderIfRequired(this.initiatorAsymmetricTokenProvider, timeoutHelper.RemainingTime());
|
|
}
|
|
if (this.initiatorAsymmetricTokenAuthenticator != null)
|
|
{
|
|
SecurityUtils.CloseTokenAuthenticatorIfRequired(this.initiatorAsymmetricTokenAuthenticator, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
base.OnClose(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
protected override IAsyncResult BeginSecureOutgoingMessageCore(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state)
|
|
{
|
|
SecurityToken encryptingToken;
|
|
SecurityToken signingToken;
|
|
SecurityProtocolCorrelationState newCorrelationState;
|
|
IList<SupportingTokenSpecification> supportingTokens;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (TryGetTokenSynchronouslyForOutgoingSecurity(message, correlationState, false, timeoutHelper.RemainingTime(), out encryptingToken, out signingToken, out supportingTokens, out newCorrelationState))
|
|
{
|
|
SetUpDelayedSecurityExecution(ref message, encryptingToken, signingToken, supportingTokens, GetSignatureConfirmationCorrelationState(correlationState, newCorrelationState));
|
|
return new CompletedAsyncResult<Message, SecurityProtocolCorrelationState>(message, newCorrelationState, callback, state);
|
|
}
|
|
else
|
|
{
|
|
if (this.Factory.ActAsInitiator == false)
|
|
{
|
|
Fx.Assert("Unexpected code path for server security application");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SendingOutgoingmessageOnRecipient)));
|
|
}
|
|
AsymmetricSecurityProtocolFactory factory = this.Factory;
|
|
SecurityTokenProvider encProvider = factory.ApplyConfidentiality ? this.initiatorAsymmetricTokenProvider : null;
|
|
SecurityTokenProvider sigProvider = factory.ApplyIntegrity ? this.initiatorCryptoTokenProvider : null;
|
|
return new SecureOutgoingMessageAsyncResult(message, this,
|
|
encProvider, sigProvider, factory.ApplyConfidentiality, this.initiatorAsymmetricTokenAuthenticator, correlationState, timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
}
|
|
|
|
protected override void EndSecureOutgoingMessageCore(IAsyncResult result, out Message message, out SecurityProtocolCorrelationState newCorrelationState)
|
|
{
|
|
if (result is CompletedAsyncResult<Message, SecurityProtocolCorrelationState>)
|
|
{
|
|
message = CompletedAsyncResult<Message, SecurityProtocolCorrelationState>.End(result, out newCorrelationState);
|
|
}
|
|
else
|
|
{
|
|
message = SecureOutgoingMessageAsyncResult.End(result, out newCorrelationState);
|
|
}
|
|
}
|
|
|
|
protected override SecurityProtocolCorrelationState SecureOutgoingMessageCore(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
|
|
{
|
|
SecurityToken encryptingToken;
|
|
SecurityToken signingToken;
|
|
SecurityProtocolCorrelationState newCorrelationState;
|
|
IList<SupportingTokenSpecification> supportingTokens;
|
|
TryGetTokenSynchronouslyForOutgoingSecurity(message, correlationState, true, timeout, out encryptingToken, out signingToken, out supportingTokens, out newCorrelationState);
|
|
SetUpDelayedSecurityExecution(ref message, encryptingToken, signingToken, supportingTokens, GetSignatureConfirmationCorrelationState(correlationState, newCorrelationState));
|
|
return newCorrelationState;
|
|
}
|
|
|
|
void SetUpDelayedSecurityExecution(ref Message message, SecurityToken encryptingToken, SecurityToken signingToken,
|
|
IList<SupportingTokenSpecification> supportingTokens, SecurityProtocolCorrelationState correlationState)
|
|
{
|
|
AsymmetricSecurityProtocolFactory factory = this.Factory;
|
|
string actor = string.Empty;
|
|
SendSecurityHeader securityHeader = ConfigureSendSecurityHeader(message, actor, supportingTokens, correlationState);
|
|
SecurityTokenParameters signingTokenParameters = (this.Factory.ActAsInitiator) ? this.Factory.CryptoTokenParameters : this.Factory.AsymmetricTokenParameters;
|
|
SecurityTokenParameters encryptionTokenParameters = (this.Factory.ActAsInitiator) ? this.Factory.AsymmetricTokenParameters : this.Factory.CryptoTokenParameters;
|
|
if (this.Factory.ApplyIntegrity || securityHeader.HasSignedTokens)
|
|
{
|
|
if (!this.Factory.ApplyIntegrity)
|
|
{
|
|
securityHeader.SignatureParts = MessagePartSpecification.NoParts;
|
|
}
|
|
securityHeader.SetSigningToken(signingToken, signingTokenParameters);
|
|
}
|
|
if (Factory.ApplyConfidentiality || securityHeader.HasEncryptedTokens)
|
|
{
|
|
if (!this.Factory.ApplyConfidentiality)
|
|
{
|
|
securityHeader.EncryptionParts = MessagePartSpecification.NoParts;
|
|
}
|
|
securityHeader.SetEncryptionToken(encryptingToken, encryptionTokenParameters);
|
|
}
|
|
message = securityHeader.SetupExecution();
|
|
}
|
|
|
|
void AttachRecipientSecurityProperty(Message message, SecurityToken initiatorToken, SecurityToken recipientToken, IList<SecurityToken> basicTokens, IList<SecurityToken> endorsingTokens,
|
|
IList<SecurityToken> signedEndorsingTokens, IList<SecurityToken> signedTokens, Dictionary<SecurityToken, ReadOnlyCollection<IAuthorizationPolicy>> tokenPoliciesMapping)
|
|
{
|
|
SecurityMessageProperty security = SecurityMessageProperty.GetOrCreate(message);
|
|
security.InitiatorToken = (initiatorToken != null) ? new SecurityTokenSpecification(initiatorToken, tokenPoliciesMapping[initiatorToken]) : null;
|
|
security.RecipientToken = (recipientToken != null) ? new SecurityTokenSpecification(recipientToken, EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance) : null;
|
|
AddSupportingTokenSpecification(security, basicTokens, endorsingTokens, signedEndorsingTokens, signedTokens, tokenPoliciesMapping);
|
|
security.ServiceSecurityContext = new ServiceSecurityContext(security.GetInitiatorTokenAuthorizationPolicies());
|
|
}
|
|
|
|
void DoIdentityCheckAndAttachInitiatorSecurityProperty(Message message, SecurityToken initiatorToken, SecurityToken recipientToken, ReadOnlyCollection<IAuthorizationPolicy> recipientTokenPolicies)
|
|
{
|
|
AuthorizationContext recipientAuthorizationContext = base.EnsureIncomingIdentity(message, recipientToken, recipientTokenPolicies);
|
|
SecurityMessageProperty security = SecurityMessageProperty.GetOrCreate(message);
|
|
security.InitiatorToken = (initiatorToken != null) ? new SecurityTokenSpecification(initiatorToken, EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance) : null;
|
|
security.RecipientToken = new SecurityTokenSpecification(recipientToken, recipientTokenPolicies);
|
|
security.ServiceSecurityContext = new ServiceSecurityContext(recipientAuthorizationContext, recipientTokenPolicies ?? EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance);
|
|
}
|
|
|
|
protected override SecurityProtocolCorrelationState VerifyIncomingMessageCore(ref Message message, string actor, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
|
|
{
|
|
AsymmetricSecurityProtocolFactory factory = this.Factory;
|
|
IList<SupportingTokenAuthenticatorSpecification> supportingAuthenticators;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
ReceiveSecurityHeader securityHeader = ConfigureReceiveSecurityHeader(message, string.Empty, correlationStates, out supportingAuthenticators);
|
|
SecurityToken requiredReplySigningToken = null;
|
|
if (factory.ActAsInitiator)
|
|
{
|
|
SecurityToken encryptionToken = null;
|
|
SecurityToken receiverToken = null;
|
|
if (factory.RequireIntegrity)
|
|
{
|
|
receiverToken = GetToken(this.initiatorAsymmetricTokenProvider, null, timeoutHelper.RemainingTime());
|
|
requiredReplySigningToken = receiverToken;
|
|
}
|
|
if (factory.RequireConfidentiality)
|
|
{
|
|
encryptionToken = GetCorrelationToken(correlationStates);
|
|
if (!SecurityUtils.HasSymmetricSecurityKey(encryptionToken))
|
|
{
|
|
securityHeader.WrappedKeySecurityTokenAuthenticator = this.Factory.WrappedKeySecurityTokenAuthenticator;
|
|
}
|
|
}
|
|
SecurityTokenAuthenticator primaryTokenAuthenticator;
|
|
if (factory.AllowSerializedSigningTokenOnReply)
|
|
{
|
|
primaryTokenAuthenticator = this.initiatorAsymmetricTokenAuthenticator;
|
|
requiredReplySigningToken = null;
|
|
}
|
|
else
|
|
{
|
|
primaryTokenAuthenticator = null;
|
|
}
|
|
|
|
securityHeader.ConfigureAsymmetricBindingClientReceiveHeader(receiverToken,
|
|
factory.AsymmetricTokenParameters, encryptionToken, factory.CryptoTokenParameters,
|
|
primaryTokenAuthenticator);
|
|
}
|
|
else
|
|
{
|
|
SecurityToken wrappingToken;
|
|
if (this.Factory.RecipientAsymmetricTokenProvider != null && this.Factory.RequireConfidentiality)
|
|
{
|
|
wrappingToken = GetToken(factory.RecipientAsymmetricTokenProvider, null, timeoutHelper.RemainingTime());
|
|
}
|
|
else
|
|
{
|
|
wrappingToken = null;
|
|
}
|
|
securityHeader.ConfigureAsymmetricBindingServerReceiveHeader(this.Factory.RecipientCryptoTokenAuthenticator,
|
|
this.Factory.CryptoTokenParameters, wrappingToken, this.Factory.AsymmetricTokenParameters, supportingAuthenticators);
|
|
securityHeader.WrappedKeySecurityTokenAuthenticator = this.Factory.WrappedKeySecurityTokenAuthenticator;
|
|
|
|
securityHeader.ConfigureOutOfBandTokenResolver(MergeOutOfBandResolvers(supportingAuthenticators, this.Factory.RecipientOutOfBandTokenResolverList));
|
|
}
|
|
|
|
ProcessSecurityHeader(securityHeader, ref message, requiredReplySigningToken, timeoutHelper.RemainingTime(), correlationStates);
|
|
SecurityToken signingToken = securityHeader.SignatureToken;
|
|
SecurityToken encryptingToken = securityHeader.EncryptionToken;
|
|
if (factory.RequireIntegrity)
|
|
{
|
|
if (factory.ActAsInitiator)
|
|
{
|
|
ReadOnlyCollection<IAuthorizationPolicy> signingTokenPolicies = this.initiatorAsymmetricTokenAuthenticator.ValidateToken(signingToken);
|
|
EnsureNonWrappedToken(signingToken, message);
|
|
DoIdentityCheckAndAttachInitiatorSecurityProperty(message, encryptingToken, signingToken, signingTokenPolicies);
|
|
}
|
|
else
|
|
{
|
|
EnsureNonWrappedToken(signingToken, message);
|
|
AttachRecipientSecurityProperty(message, signingToken, encryptingToken, securityHeader.BasicSupportingTokens, securityHeader.EndorsingSupportingTokens, securityHeader.SignedEndorsingSupportingTokens,
|
|
securityHeader.SignedSupportingTokens, securityHeader.SecurityTokenAuthorizationPoliciesMapping);
|
|
}
|
|
}
|
|
|
|
return GetCorrelationState(signingToken, securityHeader);
|
|
}
|
|
|
|
bool TryGetTokenSynchronouslyForOutgoingSecurity(Message message, SecurityProtocolCorrelationState correlationState, bool isBlockingCall, TimeSpan timeout,
|
|
out SecurityToken encryptingToken, out SecurityToken signingToken, out IList<SupportingTokenSpecification> supportingTokens, out SecurityProtocolCorrelationState newCorrelationState)
|
|
{
|
|
AsymmetricSecurityProtocolFactory factory = this.Factory;
|
|
encryptingToken = null;
|
|
signingToken = null;
|
|
newCorrelationState = null;
|
|
supportingTokens = null;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (factory.ActAsInitiator)
|
|
{
|
|
if (!isBlockingCall || !TryGetSupportingTokens(this.Factory, this.Target, this.Via, message, timeoutHelper.RemainingTime(), isBlockingCall, out supportingTokens))
|
|
{
|
|
return false;
|
|
}
|
|
if (factory.ApplyConfidentiality)
|
|
{
|
|
encryptingToken = GetTokenAndEnsureOutgoingIdentity(this.initiatorAsymmetricTokenProvider, true, timeoutHelper.RemainingTime(), this.initiatorAsymmetricTokenAuthenticator);
|
|
}
|
|
if (factory.ApplyIntegrity)
|
|
{
|
|
signingToken = GetToken(this.initiatorCryptoTokenProvider, this.Target, timeoutHelper.RemainingTime());
|
|
newCorrelationState = GetCorrelationState(signingToken);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (factory.ApplyConfidentiality)
|
|
{
|
|
encryptingToken = GetCorrelationToken(correlationState);
|
|
}
|
|
if (factory.ApplyIntegrity)
|
|
{
|
|
signingToken = GetToken(factory.RecipientAsymmetricTokenProvider, null, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
sealed class SecureOutgoingMessageAsyncResult : GetTwoTokensAndSetUpSecurityAsyncResult
|
|
{
|
|
public SecureOutgoingMessageAsyncResult(Message m, AsymmetricSecurityProtocol binding,
|
|
SecurityTokenProvider primaryProvider, SecurityTokenProvider secondaryProvider, bool doIdentityChecks, SecurityTokenAuthenticator identityCheckAuthenticator,
|
|
SecurityProtocolCorrelationState correlationState, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(m, binding, primaryProvider, secondaryProvider, doIdentityChecks, identityCheckAuthenticator, correlationState, timeout, callback, state)
|
|
{
|
|
Start();
|
|
}
|
|
|
|
protected override void OnBothGetTokenCallsDone(ref Message message, SecurityToken primaryToken, SecurityToken secondaryToken, TimeSpan timeout)
|
|
{
|
|
AsymmetricSecurityProtocol binding = (AsymmetricSecurityProtocol)this.Binding;
|
|
if (secondaryToken != null)
|
|
this.SetCorrelationToken(secondaryToken);
|
|
binding.SetUpDelayedSecurityExecution(ref message, primaryToken, secondaryToken, this.SupportingTokens, binding.GetSignatureConfirmationCorrelationState(OldCorrelationState, NewCorrelationState));
|
|
}
|
|
}
|
|
}
|
|
}
|