420 lines
22 KiB
C#
420 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.Diagnostics;
|
|
using System.ServiceModel.Security.Tokens;
|
|
|
|
sealed class SymmetricSecurityProtocol : MessageSecurityProtocol
|
|
{
|
|
SecurityTokenProvider initiatorSymmetricTokenProvider;
|
|
SecurityTokenProvider initiatorAsymmetricTokenProvider;
|
|
SecurityTokenAuthenticator initiatorTokenAuthenticator;
|
|
|
|
public SymmetricSecurityProtocol(SymmetricSecurityProtocolFactory factory,
|
|
EndpointAddress target, Uri via)
|
|
: base(factory, target, via)
|
|
{
|
|
}
|
|
|
|
SymmetricSecurityProtocolFactory Factory
|
|
{
|
|
get { return (SymmetricSecurityProtocolFactory)base.MessageSecurityProtocolFactory; }
|
|
}
|
|
|
|
public SecurityTokenProvider InitiatorSymmetricTokenProvider
|
|
{
|
|
get
|
|
{
|
|
this.CommunicationObject.ThrowIfNotOpened();
|
|
return this.initiatorSymmetricTokenProvider;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenProvider InitiatorAsymmetricTokenProvider
|
|
{
|
|
get
|
|
{
|
|
this.CommunicationObject.ThrowIfNotOpened();
|
|
return this.initiatorAsymmetricTokenProvider;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenAuthenticator InitiatorTokenAuthenticator
|
|
{
|
|
get
|
|
{
|
|
|
|
this.CommunicationObject.ThrowIfNotOpened();
|
|
return this.initiatorTokenAuthenticator;
|
|
}
|
|
}
|
|
|
|
InitiatorServiceModelSecurityTokenRequirement CreateInitiatorTokenRequirement()
|
|
{
|
|
InitiatorServiceModelSecurityTokenRequirement tokenRequirement = CreateInitiatorSecurityTokenRequirement();
|
|
this.Factory.SecurityTokenParameters.InitializeSecurityTokenRequirement(tokenRequirement);
|
|
tokenRequirement.KeyUsage = this.Factory.SecurityTokenParameters.HasAsymmetricKey ? SecurityKeyUsage.Exchange : SecurityKeyUsage.Signature;
|
|
tokenRequirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Output;
|
|
if (this.Factory.SecurityTokenParameters.HasAsymmetricKey)
|
|
{
|
|
tokenRequirement.IsOutOfBandToken = true;
|
|
}
|
|
return tokenRequirement;
|
|
}
|
|
|
|
public override void OnOpen(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
base.OnOpen(timeoutHelper.RemainingTime());
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
// 1. Create a token requirement for the provider
|
|
InitiatorServiceModelSecurityTokenRequirement tokenProviderRequirement = CreateInitiatorTokenRequirement();
|
|
|
|
// 2. Create a provider
|
|
SecurityTokenProvider tokenProvider = this.Factory.SecurityTokenManager.CreateSecurityTokenProvider(tokenProviderRequirement);
|
|
SecurityUtils.OpenTokenProviderIfRequired(tokenProvider, timeoutHelper.RemainingTime());
|
|
if (this.Factory.SecurityTokenParameters.HasAsymmetricKey)
|
|
{
|
|
this.initiatorAsymmetricTokenProvider = tokenProvider;
|
|
}
|
|
else
|
|
{
|
|
this.initiatorSymmetricTokenProvider = tokenProvider;
|
|
}
|
|
|
|
// 3. Create a token requirement for authenticator
|
|
InitiatorServiceModelSecurityTokenRequirement tokenAuthenticatorRequirement = CreateInitiatorTokenRequirement();
|
|
|
|
// 4. Create authenticator (we dont support out of band resolvers on the client side
|
|
SecurityTokenResolver outOfBandTokenResolver;
|
|
this.initiatorTokenAuthenticator = this.Factory.SecurityTokenManager.CreateSecurityTokenAuthenticator(tokenAuthenticatorRequirement, out outOfBandTokenResolver);
|
|
SecurityUtils.OpenTokenAuthenticatorIfRequired(this.initiatorTokenAuthenticator, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
public override void OnAbort()
|
|
{
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
SecurityTokenProvider provider = this.initiatorSymmetricTokenProvider ?? this.initiatorAsymmetricTokenProvider;
|
|
if (provider != null)
|
|
{
|
|
SecurityUtils.AbortTokenProviderIfRequired(provider);
|
|
}
|
|
if (this.initiatorTokenAuthenticator != null)
|
|
{
|
|
SecurityUtils.AbortTokenAuthenticatorIfRequired(this.initiatorTokenAuthenticator);
|
|
}
|
|
}
|
|
base.OnAbort();
|
|
}
|
|
|
|
public override void OnClose(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
SecurityTokenProvider provider = this.initiatorSymmetricTokenProvider ?? this.initiatorAsymmetricTokenProvider;
|
|
if (provider != null)
|
|
{
|
|
SecurityUtils.CloseTokenProviderIfRequired(provider, timeoutHelper.RemainingTime());
|
|
}
|
|
if (this.initiatorTokenAuthenticator != null)
|
|
{
|
|
SecurityUtils.CloseTokenAuthenticatorIfRequired(this.initiatorTokenAuthenticator, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
base.OnClose(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
|
|
SecurityTokenProvider GetTokenProvider()
|
|
{
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
return (this.initiatorSymmetricTokenProvider ?? this.initiatorAsymmetricTokenProvider);
|
|
}
|
|
else
|
|
{
|
|
return this.Factory.RecipientAsymmetricTokenProvider;
|
|
}
|
|
}
|
|
|
|
protected override IAsyncResult BeginSecureOutgoingMessageCore(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state)
|
|
{
|
|
SecurityToken token;
|
|
SecurityTokenParameters tokenParameters;
|
|
IList<SupportingTokenSpecification> supportingTokens;
|
|
SecurityToken prerequisiteWrappingToken;
|
|
SecurityProtocolCorrelationState newCorrelationState;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (TryGetTokenSynchronouslyForOutgoingSecurity(message, correlationState, false, timeoutHelper.RemainingTime(), out token, out tokenParameters, out prerequisiteWrappingToken, out supportingTokens, out newCorrelationState))
|
|
{
|
|
SetUpDelayedSecurityExecution(ref message, prerequisiteWrappingToken, token, tokenParameters, supportingTokens, GetSignatureConfirmationCorrelationState(correlationState, newCorrelationState));
|
|
return new CompletedAsyncResult<Message, SecurityProtocolCorrelationState>(message, newCorrelationState, callback, state);
|
|
}
|
|
else
|
|
{
|
|
if (this.Factory.ActAsInitiator != true)
|
|
{
|
|
Fx.Assert("Unexpected code path for server security application");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ProtocolMustBeInitiator, this.GetType().ToString())));
|
|
}
|
|
SecurityTokenProvider provider = GetTokenProvider();
|
|
return new SecureOutgoingMessageAsyncResult(message, this, provider, this.Factory.ApplyConfidentiality, this.initiatorTokenAuthenticator, correlationState, timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
}
|
|
|
|
protected override SecurityProtocolCorrelationState SecureOutgoingMessageCore(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
|
|
{
|
|
SecurityToken token;
|
|
SecurityTokenParameters tokenParameters;
|
|
IList<SupportingTokenSpecification> supportingTokens;
|
|
SecurityToken prerequisiteWrappingToken;
|
|
SecurityProtocolCorrelationState newCorrelationState;
|
|
TryGetTokenSynchronouslyForOutgoingSecurity(message, correlationState, true, timeout, out token, out tokenParameters, out prerequisiteWrappingToken, out supportingTokens, out newCorrelationState);
|
|
SetUpDelayedSecurityExecution(ref message, prerequisiteWrappingToken, token, tokenParameters, supportingTokens, GetSignatureConfirmationCorrelationState(correlationState, newCorrelationState));
|
|
return newCorrelationState;
|
|
}
|
|
|
|
void SetUpDelayedSecurityExecution(ref Message message,
|
|
SecurityToken prerequisiteToken,
|
|
SecurityToken primaryToken,
|
|
SecurityTokenParameters primaryTokenParameters,
|
|
IList<SupportingTokenSpecification> supportingTokens,
|
|
SecurityProtocolCorrelationState correlationState
|
|
)
|
|
{
|
|
string actor = string.Empty;
|
|
SendSecurityHeader securityHeader = ConfigureSendSecurityHeader(message, actor, supportingTokens, correlationState);
|
|
if (prerequisiteToken != null)
|
|
{
|
|
securityHeader.AddPrerequisiteToken(prerequisiteToken);
|
|
}
|
|
if (this.Factory.ApplyIntegrity || securityHeader.HasSignedTokens)
|
|
{
|
|
if (!this.Factory.ApplyIntegrity)
|
|
{
|
|
securityHeader.SignatureParts = MessagePartSpecification.NoParts;
|
|
}
|
|
securityHeader.SetSigningToken(primaryToken, primaryTokenParameters);
|
|
}
|
|
if (Factory.ApplyConfidentiality || securityHeader.HasEncryptedTokens)
|
|
{
|
|
if (!this.Factory.ApplyConfidentiality)
|
|
{
|
|
securityHeader.EncryptionParts = MessagePartSpecification.NoParts;
|
|
}
|
|
securityHeader.SetEncryptionToken(primaryToken, primaryTokenParameters);
|
|
}
|
|
message = securityHeader.SetupExecution();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
WrappedKeySecurityToken CreateWrappedKeyToken(SecurityToken wrappingToken, SecurityTokenParameters wrappingTokenParameters, SecurityTokenReferenceStyle wrappingTokenReferenceStyle)
|
|
{
|
|
int keyLength = Math.Max(128, this.Factory.OutgoingAlgorithmSuite.DefaultSymmetricKeyLength);
|
|
CryptoHelper.ValidateSymmetricKeyLength(keyLength, this.Factory.OutgoingAlgorithmSuite);
|
|
byte[] key = new byte[keyLength / 8];
|
|
CryptoHelper.FillRandomBytes(key);
|
|
string tokenId = SecurityUtils.GenerateId();
|
|
string wrappingAlgorithm = this.Factory.OutgoingAlgorithmSuite.DefaultAsymmetricKeyWrapAlgorithm;
|
|
SecurityKeyIdentifierClause clause = wrappingTokenParameters.CreateKeyIdentifierClause(wrappingToken, wrappingTokenReferenceStyle);
|
|
SecurityKeyIdentifier identifier = new SecurityKeyIdentifier();
|
|
identifier.Add(clause);
|
|
return new WrappedKeySecurityToken(tokenId, key, wrappingAlgorithm, wrappingToken, identifier);
|
|
}
|
|
|
|
SecurityToken GetInitiatorToken(SecurityToken providerToken,
|
|
Message message,
|
|
TimeSpan timeout,
|
|
out SecurityTokenParameters tokenParameters,
|
|
out SecurityToken prerequisiteWrappingToken)
|
|
{
|
|
tokenParameters = null;
|
|
prerequisiteWrappingToken = null;
|
|
SecurityToken token;
|
|
if (this.Factory.SecurityTokenParameters.HasAsymmetricKey)
|
|
{
|
|
SecurityToken asymmetricToken = providerToken;
|
|
// non-Indigo server implementations might require the wrapping token to be included in the message
|
|
bool isAsymmetricTokenInMessage = SendSecurityHeader.ShouldSerializeToken(this.Factory.SecurityTokenParameters, MessageDirection.Input);
|
|
if (isAsymmetricTokenInMessage)
|
|
{
|
|
prerequisiteWrappingToken = asymmetricToken;
|
|
}
|
|
token = CreateWrappedKeyToken(asymmetricToken, this.Factory.SecurityTokenParameters, (isAsymmetricTokenInMessage) ? SecurityTokenReferenceStyle.Internal : SecurityTokenReferenceStyle.External);
|
|
}
|
|
else
|
|
{
|
|
token = providerToken;
|
|
}
|
|
|
|
tokenParameters = this.Factory.GetProtectionTokenParameters();
|
|
return token;
|
|
}
|
|
|
|
// try to get the token if it can be obtained within the
|
|
// synchronous requirements of the call; return true iff a
|
|
// token not required OR a token is required AND has been
|
|
// obtained within the specified synchronous requirements.
|
|
bool TryGetTokenSynchronouslyForOutgoingSecurity(Message message, SecurityProtocolCorrelationState correlationState, bool isBlockingCall, TimeSpan timeout, out SecurityToken token, out SecurityTokenParameters tokenParameters, out SecurityToken prerequisiteWrappingToken, out IList<SupportingTokenSpecification> supportingTokens, out SecurityProtocolCorrelationState newCorrelationState)
|
|
{
|
|
SymmetricSecurityProtocolFactory factory = this.Factory;
|
|
supportingTokens = null;
|
|
prerequisiteWrappingToken = null;
|
|
token = null;
|
|
tokenParameters = null;
|
|
newCorrelationState = null;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (factory.ApplyIntegrity || factory.ApplyConfidentiality)
|
|
{
|
|
if (factory.ActAsInitiator)
|
|
{
|
|
if (!isBlockingCall || !TryGetSupportingTokens(factory, this.Target, this.Via, message, timeoutHelper.RemainingTime(), isBlockingCall, out supportingTokens))
|
|
{
|
|
return false;
|
|
}
|
|
SecurityTokenProvider provider = GetTokenProvider();
|
|
SecurityToken providerToken = GetTokenAndEnsureOutgoingIdentity(provider, factory.ApplyConfidentiality, timeoutHelper.RemainingTime(), this.initiatorTokenAuthenticator);
|
|
token = GetInitiatorToken(providerToken, message, timeoutHelper.RemainingTime(), out tokenParameters, out prerequisiteWrappingToken);
|
|
newCorrelationState = GetCorrelationState(token);
|
|
}
|
|
else
|
|
{
|
|
token = GetCorrelationToken(correlationState);
|
|
tokenParameters = this.Factory.GetProtectionTokenParameters();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
SecurityToken GetCorrelationToken(SecurityProtocolCorrelationState[] correlationStates, out SecurityTokenParameters correlationTokenParameters)
|
|
{
|
|
SecurityToken token = GetCorrelationToken(correlationStates);
|
|
correlationTokenParameters = this.Factory.GetProtectionTokenParameters();
|
|
return token;
|
|
}
|
|
|
|
void EnsureWrappedToken(SecurityToken token, Message message)
|
|
{
|
|
if (!(token is WrappedKeySecurityToken))
|
|
{
|
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.IncomingSigningTokenMustBeAnEncryptedKey)), message);
|
|
}
|
|
}
|
|
|
|
protected override SecurityProtocolCorrelationState VerifyIncomingMessageCore(ref Message message, string actor, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
|
|
{
|
|
SymmetricSecurityProtocolFactory factory = this.Factory;
|
|
IList<SupportingTokenAuthenticatorSpecification> supportingAuthenticators;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
ReceiveSecurityHeader securityHeader = ConfigureReceiveSecurityHeader(message, string.Empty, correlationStates, out supportingAuthenticators);
|
|
SecurityToken requiredReplySigningToken = null;
|
|
if (this.Factory.ActAsInitiator)
|
|
{
|
|
// set the outofband protection token
|
|
SecurityTokenParameters outOfBandTokenParameters;
|
|
SecurityToken outOfBandToken = GetCorrelationToken(correlationStates, out outOfBandTokenParameters);
|
|
securityHeader.ConfigureSymmetricBindingClientReceiveHeader(outOfBandToken, outOfBandTokenParameters);
|
|
requiredReplySigningToken = outOfBandToken;
|
|
}
|
|
else
|
|
{
|
|
if (factory.RecipientSymmetricTokenAuthenticator != null)
|
|
{
|
|
securityHeader.ConfigureSymmetricBindingServerReceiveHeader(this.Factory.RecipientSymmetricTokenAuthenticator, this.Factory.SecurityTokenParameters, supportingAuthenticators);
|
|
}
|
|
else
|
|
{
|
|
securityHeader.ConfigureSymmetricBindingServerReceiveHeader(this.Factory.RecipientAsymmetricTokenProvider.GetToken(timeoutHelper.RemainingTime()), this.Factory.SecurityTokenParameters, 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;
|
|
if (factory.RequireIntegrity)
|
|
{
|
|
if (factory.SecurityTokenParameters.HasAsymmetricKey)
|
|
{
|
|
// enforce that the signing token is a wrapped key token
|
|
EnsureWrappedToken(signingToken, message);
|
|
}
|
|
else
|
|
{
|
|
EnsureNonWrappedToken(signingToken, message);
|
|
}
|
|
|
|
if (factory.ActAsInitiator)
|
|
{
|
|
if (!factory.SecurityTokenParameters.HasAsymmetricKey)
|
|
{
|
|
ReadOnlyCollection<IAuthorizationPolicy> signingTokenPolicies = this.initiatorTokenAuthenticator.ValidateToken(signingToken);
|
|
DoIdentityCheckAndAttachInitiatorSecurityProperty(message, signingToken, signingTokenPolicies);
|
|
}
|
|
else
|
|
{
|
|
SecurityToken wrappingToken = (signingToken as WrappedKeySecurityToken).WrappingToken;
|
|
ReadOnlyCollection<IAuthorizationPolicy> wrappingTokenPolicies = this.initiatorTokenAuthenticator.ValidateToken(wrappingToken);
|
|
DoIdentityCheckAndAttachInitiatorSecurityProperty(message, signingToken, wrappingTokenPolicies);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AttachRecipientSecurityProperty(message, signingToken, this.Factory.SecurityTokenParameters.HasAsymmetricKey, securityHeader.BasicSupportingTokens, securityHeader.EndorsingSupportingTokens, securityHeader.SignedEndorsingSupportingTokens,
|
|
securityHeader.SignedSupportingTokens, securityHeader.SecurityTokenAuthorizationPoliciesMapping);
|
|
}
|
|
}
|
|
return GetCorrelationState(signingToken, securityHeader);
|
|
}
|
|
|
|
sealed class SecureOutgoingMessageAsyncResult : GetOneTokenAndSetUpSecurityAsyncResult
|
|
{
|
|
SymmetricSecurityProtocol symmetricBinding;
|
|
|
|
public SecureOutgoingMessageAsyncResult(Message m, SymmetricSecurityProtocol binding, SecurityTokenProvider provider,
|
|
bool doIdentityChecks, SecurityTokenAuthenticator identityCheckAuthenticator, SecurityProtocolCorrelationState correlationState, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(m, binding, provider, doIdentityChecks, identityCheckAuthenticator, correlationState, timeout, callback, state)
|
|
{
|
|
symmetricBinding = binding;
|
|
Start();
|
|
}
|
|
|
|
protected override void OnGetTokenDone(ref Message message, SecurityToken providerToken, TimeSpan timeout)
|
|
{
|
|
SecurityTokenParameters tokenParameters;
|
|
SecurityToken prerequisiteWrappingToken;
|
|
SecurityToken token = symmetricBinding.GetInitiatorToken(providerToken, message, timeout, out tokenParameters, out prerequisiteWrappingToken);
|
|
this.SetCorrelationToken(token);
|
|
symmetricBinding.SetUpDelayedSecurityExecution(ref message, prerequisiteWrappingToken, token, tokenParameters, this.SupportingTokens, Binding.GetSignatureConfirmationCorrelationState(OldCorrelationState, NewCorrelationState));
|
|
}
|
|
}
|
|
}
|
|
}
|