DerivedKeyCachingSecurityTokenSerializer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Security
  5. {
  6. using System.Collections;
  7. using System.Collections.ObjectModel;
  8. using System.ServiceModel;
  9. using System.Xml;
  10. using System.IdentityModel.Tokens;
  11. using System.IdentityModel.Selectors;
  12. using System.Collections.Generic;
  13. using System.ServiceModel.Security.Tokens;
  14. class DerivedKeyCachingSecurityTokenSerializer : SecurityTokenSerializer
  15. {
  16. DerivedKeySecurityTokenCache[] cachedTokens;
  17. WSSecureConversation secureConversation;
  18. SecurityTokenSerializer innerTokenSerializer;
  19. bool isInitiator;
  20. int indexToCache = 0;
  21. Object thisLock;
  22. internal DerivedKeyCachingSecurityTokenSerializer(int cacheSize, bool isInitiator, WSSecureConversation secureConversation, SecurityTokenSerializer innerTokenSerializer)
  23. : base()
  24. {
  25. if (innerTokenSerializer == null)
  26. {
  27. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("innerTokenSerializer");
  28. }
  29. if (secureConversation == null)
  30. {
  31. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("secureConversation");
  32. }
  33. if (cacheSize <= 0)
  34. {
  35. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("cacheSize", SR.GetString(SR.ValueMustBeGreaterThanZero)));
  36. }
  37. this.cachedTokens = new DerivedKeySecurityTokenCache[cacheSize];
  38. this.isInitiator = isInitiator;
  39. this.secureConversation = secureConversation;
  40. this.innerTokenSerializer = innerTokenSerializer;
  41. this.thisLock = new Object();
  42. }
  43. protected override bool CanReadKeyIdentifierClauseCore(XmlReader reader)
  44. {
  45. return this.innerTokenSerializer.CanReadKeyIdentifierClause(reader);
  46. }
  47. protected override bool CanReadKeyIdentifierCore(XmlReader reader)
  48. {
  49. return this.innerTokenSerializer.CanReadKeyIdentifier(reader);
  50. }
  51. protected override bool CanReadTokenCore(XmlReader reader)
  52. {
  53. return this.innerTokenSerializer.CanReadToken(reader);
  54. }
  55. protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
  56. {
  57. XmlDictionaryReader dictionaryReader = XmlDictionaryReader.CreateDictionaryReader(reader);
  58. if (this.secureConversation.IsAtDerivedKeyToken(dictionaryReader))
  59. {
  60. string id;
  61. string derivationAlgorithm;
  62. string label;
  63. int length;
  64. byte[] nonce;
  65. int offset;
  66. int generation;
  67. SecurityKeyIdentifierClause tokenToDeriveIdentifier;
  68. SecurityToken tokenToDerive;
  69. this.secureConversation.ReadDerivedKeyTokenParameters(dictionaryReader, tokenResolver, out id, out derivationAlgorithm, out label,
  70. out length, out nonce, out offset, out generation, out tokenToDeriveIdentifier, out tokenToDerive);
  71. DerivedKeySecurityToken cachedToken = GetCachedToken(id, generation, offset, length, label, nonce, tokenToDerive, tokenToDeriveIdentifier, derivationAlgorithm);
  72. if (cachedToken != null)
  73. {
  74. return cachedToken;
  75. }
  76. lock (this.thisLock)
  77. {
  78. cachedToken = GetCachedToken(id, generation, offset, length, label, nonce, tokenToDerive, tokenToDeriveIdentifier, derivationAlgorithm);
  79. if (cachedToken != null)
  80. {
  81. return cachedToken;
  82. }
  83. SecurityToken result = this.secureConversation.CreateDerivedKeyToken( id, derivationAlgorithm, label, length, nonce, offset, generation, tokenToDeriveIdentifier, tokenToDerive );
  84. DerivedKeySecurityToken newToken = result as DerivedKeySecurityToken;
  85. if (newToken != null)
  86. {
  87. int pos = this.indexToCache;
  88. if (this.indexToCache == int.MaxValue)
  89. this.indexToCache = 0;
  90. else
  91. this.indexToCache = (++this.indexToCache) % this.cachedTokens.Length;
  92. this.cachedTokens[pos] = new DerivedKeySecurityTokenCache(newToken);
  93. }
  94. return result;
  95. }
  96. }
  97. else
  98. {
  99. return this.innerTokenSerializer.ReadToken(reader, tokenResolver);
  100. }
  101. }
  102. protected override bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause)
  103. {
  104. return this.innerTokenSerializer.CanWriteKeyIdentifierClause(keyIdentifierClause);
  105. }
  106. protected override bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier)
  107. {
  108. return this.innerTokenSerializer.CanWriteKeyIdentifier(keyIdentifier);
  109. }
  110. protected override bool CanWriteTokenCore(SecurityToken token)
  111. {
  112. return this.innerTokenSerializer.CanWriteToken(token);
  113. }
  114. protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
  115. {
  116. return this.innerTokenSerializer.ReadKeyIdentifierClause(reader);
  117. }
  118. protected override SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader)
  119. {
  120. return this.innerTokenSerializer.ReadKeyIdentifier(reader);
  121. }
  122. protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
  123. {
  124. this.innerTokenSerializer.WriteKeyIdentifierClause(writer, keyIdentifierClause);
  125. }
  126. protected override void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier)
  127. {
  128. this.innerTokenSerializer.WriteKeyIdentifier(writer, keyIdentifier);
  129. }
  130. protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
  131. {
  132. this.innerTokenSerializer.WriteToken(writer, token);
  133. }
  134. bool IsMatch(DerivedKeySecurityTokenCache cachedToken, string id, int generation, int offset, int length,
  135. string label, byte[] nonce, SecurityToken tokenToDerive, string derivationAlgorithm)
  136. {
  137. if ((cachedToken.Generation == generation)
  138. && (cachedToken.Offset == offset)
  139. && (cachedToken.Length == length)
  140. && (cachedToken.Label == label)
  141. && (cachedToken.KeyDerivationAlgorithm == derivationAlgorithm))
  142. {
  143. if (!cachedToken.IsSourceKeyEqual(tokenToDerive))
  144. {
  145. return false;
  146. }
  147. // since derived key token keys are delay initialized during security processing, it may be possible
  148. // that the cached derived key token does not have its keys initialized as yet. If so return false for
  149. // the match so that the framework doesnt try to reference a null key.
  150. return (CryptoHelper.IsEqual(cachedToken.Nonce, nonce) && (cachedToken.SecurityKeys != null));
  151. }
  152. else
  153. {
  154. return false;
  155. }
  156. }
  157. DerivedKeySecurityToken GetCachedToken(string id, int generation, int offset, int length,
  158. string label, byte[] nonce, SecurityToken tokenToDerive, SecurityKeyIdentifierClause tokenToDeriveIdentifier, string derivationAlgorithm)
  159. {
  160. for (int i = 0; i < this.cachedTokens.Length; ++i)
  161. {
  162. DerivedKeySecurityTokenCache cachedToken = this.cachedTokens[i];
  163. if (cachedToken != null && IsMatch(cachedToken, id, generation, offset, length,
  164. label, nonce, tokenToDerive, derivationAlgorithm))
  165. {
  166. DerivedKeySecurityToken token = new DerivedKeySecurityToken(generation, offset, length, label, nonce, tokenToDerive,
  167. tokenToDeriveIdentifier, derivationAlgorithm, id);
  168. token.InitializeDerivedKey(cachedToken.SecurityKeys);
  169. return token;
  170. }
  171. }
  172. return null;
  173. }
  174. class DerivedKeySecurityTokenCache
  175. {
  176. byte[] keyToDerive;
  177. int generation;
  178. int offset;
  179. int length;
  180. string label;
  181. string keyDerivationAlgorithm;
  182. byte[] nonce;
  183. ReadOnlyCollection<SecurityKey> keys;
  184. DerivedKeySecurityToken cachedToken;
  185. public DerivedKeySecurityTokenCache(DerivedKeySecurityToken cachedToken)
  186. {
  187. this.keyToDerive = ((SymmetricSecurityKey)cachedToken.TokenToDerive.SecurityKeys[0]).GetSymmetricKey();
  188. this.generation = cachedToken.Generation;
  189. this.offset = cachedToken.Offset;
  190. this.length = cachedToken.Length;
  191. this.label = cachedToken.Label;
  192. this.keyDerivationAlgorithm = cachedToken.KeyDerivationAlgorithm;
  193. this.nonce = cachedToken.Nonce;
  194. this.cachedToken = cachedToken;
  195. }
  196. public int Generation
  197. {
  198. get { return this.generation; }
  199. }
  200. public int Offset
  201. {
  202. get { return this.offset; }
  203. }
  204. public int Length
  205. {
  206. get { return this.length; }
  207. }
  208. public string Label
  209. {
  210. get { return this.label; }
  211. }
  212. public string KeyDerivationAlgorithm
  213. {
  214. get { return this.keyDerivationAlgorithm; }
  215. }
  216. public byte[] Nonce
  217. {
  218. get { return this.nonce; }
  219. }
  220. public ReadOnlyCollection<SecurityKey> SecurityKeys
  221. {
  222. get
  223. {
  224. // we would need to hold onto the cached token till a hit is obtained because of
  225. // the delay initialization of derived key crypto by the security header.
  226. lock (this)
  227. {
  228. if (this.keys == null)
  229. {
  230. ReadOnlyCollection<SecurityKey> computedKeys;
  231. if (this.cachedToken.TryGetSecurityKeys(out computedKeys))
  232. {
  233. this.keys = computedKeys;
  234. this.cachedToken = null;
  235. }
  236. }
  237. }
  238. return this.keys;
  239. }
  240. }
  241. public bool IsSourceKeyEqual(SecurityToken token)
  242. {
  243. if (token.SecurityKeys.Count != 1)
  244. {
  245. return false;
  246. }
  247. SymmetricSecurityKey key = token.SecurityKeys[0] as SymmetricSecurityKey;
  248. if (key == null)
  249. {
  250. return false;
  251. }
  252. return CryptoHelper.IsEqual(this.keyToDerive, key.GetSymmetricKey());
  253. }
  254. }
  255. }
  256. }