286 lines
12 KiB
C#
286 lines
12 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Security
|
||
|
{
|
||
|
using System.Collections;
|
||
|
using System.Collections.ObjectModel;
|
||
|
using System.ServiceModel;
|
||
|
using System.Xml;
|
||
|
using System.IdentityModel.Tokens;
|
||
|
using System.IdentityModel.Selectors;
|
||
|
using System.Collections.Generic;
|
||
|
using System.ServiceModel.Security.Tokens;
|
||
|
|
||
|
class DerivedKeyCachingSecurityTokenSerializer : SecurityTokenSerializer
|
||
|
{
|
||
|
DerivedKeySecurityTokenCache[] cachedTokens;
|
||
|
WSSecureConversation secureConversation;
|
||
|
SecurityTokenSerializer innerTokenSerializer;
|
||
|
bool isInitiator;
|
||
|
int indexToCache = 0;
|
||
|
Object thisLock;
|
||
|
|
||
|
internal DerivedKeyCachingSecurityTokenSerializer(int cacheSize, bool isInitiator, WSSecureConversation secureConversation, SecurityTokenSerializer innerTokenSerializer)
|
||
|
: base()
|
||
|
{
|
||
|
if (innerTokenSerializer == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("innerTokenSerializer");
|
||
|
}
|
||
|
if (secureConversation == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("secureConversation");
|
||
|
}
|
||
|
if (cacheSize <= 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("cacheSize", SR.GetString(SR.ValueMustBeGreaterThanZero)));
|
||
|
}
|
||
|
this.cachedTokens = new DerivedKeySecurityTokenCache[cacheSize];
|
||
|
this.isInitiator = isInitiator;
|
||
|
this.secureConversation = secureConversation;
|
||
|
this.innerTokenSerializer = innerTokenSerializer;
|
||
|
this.thisLock = new Object();
|
||
|
}
|
||
|
|
||
|
protected override bool CanReadKeyIdentifierClauseCore(XmlReader reader)
|
||
|
{
|
||
|
return this.innerTokenSerializer.CanReadKeyIdentifierClause(reader);
|
||
|
}
|
||
|
|
||
|
protected override bool CanReadKeyIdentifierCore(XmlReader reader)
|
||
|
{
|
||
|
return this.innerTokenSerializer.CanReadKeyIdentifier(reader);
|
||
|
}
|
||
|
|
||
|
protected override bool CanReadTokenCore(XmlReader reader)
|
||
|
{
|
||
|
return this.innerTokenSerializer.CanReadToken(reader);
|
||
|
}
|
||
|
|
||
|
protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
|
||
|
{
|
||
|
XmlDictionaryReader dictionaryReader = XmlDictionaryReader.CreateDictionaryReader(reader);
|
||
|
if (this.secureConversation.IsAtDerivedKeyToken(dictionaryReader))
|
||
|
{
|
||
|
string id;
|
||
|
string derivationAlgorithm;
|
||
|
string label;
|
||
|
int length;
|
||
|
byte[] nonce;
|
||
|
int offset;
|
||
|
int generation;
|
||
|
SecurityKeyIdentifierClause tokenToDeriveIdentifier;
|
||
|
SecurityToken tokenToDerive;
|
||
|
this.secureConversation.ReadDerivedKeyTokenParameters(dictionaryReader, tokenResolver, out id, out derivationAlgorithm, out label,
|
||
|
out length, out nonce, out offset, out generation, out tokenToDeriveIdentifier, out tokenToDerive);
|
||
|
|
||
|
DerivedKeySecurityToken cachedToken = GetCachedToken(id, generation, offset, length, label, nonce, tokenToDerive, tokenToDeriveIdentifier, derivationAlgorithm);
|
||
|
if (cachedToken != null)
|
||
|
{
|
||
|
return cachedToken;
|
||
|
}
|
||
|
|
||
|
lock (this.thisLock)
|
||
|
{
|
||
|
cachedToken = GetCachedToken(id, generation, offset, length, label, nonce, tokenToDerive, tokenToDeriveIdentifier, derivationAlgorithm);
|
||
|
if (cachedToken != null)
|
||
|
{
|
||
|
return cachedToken;
|
||
|
}
|
||
|
SecurityToken result = this.secureConversation.CreateDerivedKeyToken( id, derivationAlgorithm, label, length, nonce, offset, generation, tokenToDeriveIdentifier, tokenToDerive );
|
||
|
DerivedKeySecurityToken newToken = result as DerivedKeySecurityToken;
|
||
|
if (newToken != null)
|
||
|
{
|
||
|
int pos = this.indexToCache;
|
||
|
if (this.indexToCache == int.MaxValue)
|
||
|
this.indexToCache = 0;
|
||
|
else
|
||
|
this.indexToCache = (++this.indexToCache) % this.cachedTokens.Length;
|
||
|
this.cachedTokens[pos] = new DerivedKeySecurityTokenCache(newToken);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return this.innerTokenSerializer.ReadToken(reader, tokenResolver);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause)
|
||
|
{
|
||
|
return this.innerTokenSerializer.CanWriteKeyIdentifierClause(keyIdentifierClause);
|
||
|
}
|
||
|
|
||
|
protected override bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier)
|
||
|
{
|
||
|
return this.innerTokenSerializer.CanWriteKeyIdentifier(keyIdentifier);
|
||
|
}
|
||
|
|
||
|
protected override bool CanWriteTokenCore(SecurityToken token)
|
||
|
{
|
||
|
return this.innerTokenSerializer.CanWriteToken(token);
|
||
|
}
|
||
|
|
||
|
protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
|
||
|
{
|
||
|
return this.innerTokenSerializer.ReadKeyIdentifierClause(reader);
|
||
|
}
|
||
|
|
||
|
protected override SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader)
|
||
|
{
|
||
|
return this.innerTokenSerializer.ReadKeyIdentifier(reader);
|
||
|
}
|
||
|
|
||
|
protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
|
||
|
{
|
||
|
this.innerTokenSerializer.WriteKeyIdentifierClause(writer, keyIdentifierClause);
|
||
|
}
|
||
|
|
||
|
protected override void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier)
|
||
|
{
|
||
|
this.innerTokenSerializer.WriteKeyIdentifier(writer, keyIdentifier);
|
||
|
}
|
||
|
|
||
|
protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
|
||
|
{
|
||
|
this.innerTokenSerializer.WriteToken(writer, token);
|
||
|
}
|
||
|
|
||
|
bool IsMatch(DerivedKeySecurityTokenCache cachedToken, string id, int generation, int offset, int length,
|
||
|
string label, byte[] nonce, SecurityToken tokenToDerive, string derivationAlgorithm)
|
||
|
{
|
||
|
if ((cachedToken.Generation == generation)
|
||
|
&& (cachedToken.Offset == offset)
|
||
|
&& (cachedToken.Length == length)
|
||
|
&& (cachedToken.Label == label)
|
||
|
&& (cachedToken.KeyDerivationAlgorithm == derivationAlgorithm))
|
||
|
{
|
||
|
if (!cachedToken.IsSourceKeyEqual(tokenToDerive))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
// since derived key token keys are delay initialized during security processing, it may be possible
|
||
|
// that the cached derived key token does not have its keys initialized as yet. If so return false for
|
||
|
// the match so that the framework doesnt try to reference a null key.
|
||
|
return (CryptoHelper.IsEqual(cachedToken.Nonce, nonce) && (cachedToken.SecurityKeys != null));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DerivedKeySecurityToken GetCachedToken(string id, int generation, int offset, int length,
|
||
|
string label, byte[] nonce, SecurityToken tokenToDerive, SecurityKeyIdentifierClause tokenToDeriveIdentifier, string derivationAlgorithm)
|
||
|
{
|
||
|
for (int i = 0; i < this.cachedTokens.Length; ++i)
|
||
|
{
|
||
|
DerivedKeySecurityTokenCache cachedToken = this.cachedTokens[i];
|
||
|
if (cachedToken != null && IsMatch(cachedToken, id, generation, offset, length,
|
||
|
label, nonce, tokenToDerive, derivationAlgorithm))
|
||
|
{
|
||
|
DerivedKeySecurityToken token = new DerivedKeySecurityToken(generation, offset, length, label, nonce, tokenToDerive,
|
||
|
tokenToDeriveIdentifier, derivationAlgorithm, id);
|
||
|
token.InitializeDerivedKey(cachedToken.SecurityKeys);
|
||
|
return token;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
class DerivedKeySecurityTokenCache
|
||
|
{
|
||
|
byte[] keyToDerive;
|
||
|
int generation;
|
||
|
int offset;
|
||
|
int length;
|
||
|
string label;
|
||
|
string keyDerivationAlgorithm;
|
||
|
byte[] nonce;
|
||
|
ReadOnlyCollection<SecurityKey> keys;
|
||
|
DerivedKeySecurityToken cachedToken;
|
||
|
|
||
|
public DerivedKeySecurityTokenCache(DerivedKeySecurityToken cachedToken)
|
||
|
{
|
||
|
this.keyToDerive = ((SymmetricSecurityKey)cachedToken.TokenToDerive.SecurityKeys[0]).GetSymmetricKey();
|
||
|
this.generation = cachedToken.Generation;
|
||
|
this.offset = cachedToken.Offset;
|
||
|
this.length = cachedToken.Length;
|
||
|
this.label = cachedToken.Label;
|
||
|
this.keyDerivationAlgorithm = cachedToken.KeyDerivationAlgorithm;
|
||
|
this.nonce = cachedToken.Nonce;
|
||
|
this.cachedToken = cachedToken;
|
||
|
}
|
||
|
|
||
|
public int Generation
|
||
|
{
|
||
|
get { return this.generation; }
|
||
|
}
|
||
|
|
||
|
public int Offset
|
||
|
{
|
||
|
get { return this.offset; }
|
||
|
}
|
||
|
|
||
|
public int Length
|
||
|
{
|
||
|
get { return this.length; }
|
||
|
}
|
||
|
|
||
|
public string Label
|
||
|
{
|
||
|
get { return this.label; }
|
||
|
}
|
||
|
|
||
|
public string KeyDerivationAlgorithm
|
||
|
{
|
||
|
get { return this.keyDerivationAlgorithm; }
|
||
|
}
|
||
|
|
||
|
public byte[] Nonce
|
||
|
{
|
||
|
get { return this.nonce; }
|
||
|
}
|
||
|
|
||
|
public ReadOnlyCollection<SecurityKey> SecurityKeys
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
// we would need to hold onto the cached token till a hit is obtained because of
|
||
|
// the delay initialization of derived key crypto by the security header.
|
||
|
lock (this)
|
||
|
{
|
||
|
if (this.keys == null)
|
||
|
{
|
||
|
ReadOnlyCollection<SecurityKey> computedKeys;
|
||
|
if (this.cachedToken.TryGetSecurityKeys(out computedKeys))
|
||
|
{
|
||
|
this.keys = computedKeys;
|
||
|
this.cachedToken = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return this.keys;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsSourceKeyEqual(SecurityToken token)
|
||
|
{
|
||
|
if (token.SecurityKeys.Count != 1)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
SymmetricSecurityKey key = token.SecurityKeys[0] as SymmetricSecurityKey;
|
||
|
if (key == null)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
return CryptoHelper.IsEqual(this.keyToDerive, key.GetSymmetricKey());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|