//---------------------------------------------------------- // 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 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, 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 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 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 = CompletedAsyncResult.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 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 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 signingTokenPolicies = this.initiatorTokenAuthenticator.ValidateToken(signingToken); DoIdentityCheckAndAttachInitiatorSecurityProperty(message, signingToken, signingTokenPolicies); } else { SecurityToken wrappingToken = (signingToken as WrappedKeySecurityToken).WrappingToken; ReadOnlyCollection 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)); } } } }