SecureMessageGenerator.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. //
  2. // MessageSecurityGenerator.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Collections.ObjectModel;
  31. using System.Globalization;
  32. using System.IdentityModel.Selectors;
  33. using System.IdentityModel.Tokens;
  34. using System.Runtime.Serialization;
  35. using System.Security.Cryptography;
  36. using System.Security.Cryptography.X509Certificates;
  37. using System.Security.Cryptography.Xml;
  38. using System.ServiceModel;
  39. using System.ServiceModel.Channels;
  40. using System.ServiceModel.Description;
  41. using System.ServiceModel.Security;
  42. using System.ServiceModel.Security.Tokens;
  43. using System.Text;
  44. using System.Xml;
  45. using System.Xml.XPath;
  46. using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
  47. namespace System.ServiceModel.Channels.Security
  48. {
  49. internal class InitiatorMessageSecurityGenerator : MessageSecurityGenerator
  50. {
  51. EndpointAddress message_to;
  52. InitiatorMessageSecurityBindingSupport security;
  53. public InitiatorMessageSecurityGenerator (
  54. Message msg,
  55. InitiatorMessageSecurityBindingSupport security,
  56. EndpointAddress messageTo)
  57. : base (msg, security)
  58. {
  59. // FIXME: I believe it should be done at channel
  60. // creation phase, but WinFX does not.
  61. // if (!security.InitiatorParameters.InternalHasAsymmetricKey)
  62. // throw new InvalidOperationException ("Wrong security token parameters: it must have an asymmetric key (HasAsymmetricKey). There is likely a misconfiguration in the custom security binding element.");
  63. this.security = security;
  64. this.message_to = messageTo;
  65. }
  66. public override SecurityTokenParameters Parameters {
  67. get { return security.InitiatorParameters; }
  68. }
  69. public override SecurityTokenParameters CounterParameters {
  70. get { return security.RecipientParameters; }
  71. }
  72. public override MessageDirection Direction {
  73. get { return MessageDirection.Input; }
  74. }
  75. public override EndpointAddress MessageTo {
  76. get { return message_to; }
  77. }
  78. public override bool ShouldIncludeToken (SecurityTokenInclusionMode mode, bool isInitialized)
  79. {
  80. switch (mode) {
  81. case SecurityTokenInclusionMode.Never:
  82. case SecurityTokenInclusionMode.AlwaysToInitiator:
  83. return false;
  84. case SecurityTokenInclusionMode.AlwaysToRecipient:
  85. return true;
  86. case SecurityTokenInclusionMode.Once:
  87. return !isInitialized;
  88. }
  89. throw new Exception ("Internal Error: should not happen.");
  90. }
  91. public override ScopedMessagePartSpecification SignatureParts {
  92. get { return Security.ChannelRequirements.IncomingSignatureParts; }
  93. }
  94. public override ScopedMessagePartSpecification EncryptionParts {
  95. get { return Security.ChannelRequirements.IncomingEncryptionParts; }
  96. }
  97. }
  98. internal class RecipientMessageSecurityGenerator : MessageSecurityGenerator
  99. {
  100. RecipientMessageSecurityBindingSupport security;
  101. public RecipientMessageSecurityGenerator (
  102. Message msg,
  103. SecurityMessageProperty requestSecProp,
  104. RecipientMessageSecurityBindingSupport security)
  105. : base (msg, security)
  106. {
  107. this.security = security;
  108. SecurityMessageProperty secprop =
  109. (SecurityMessageProperty) requestSecProp.CreateCopy ();
  110. msg.Properties.Security = secprop;
  111. }
  112. public override SecurityTokenParameters Parameters {
  113. get { return security.RecipientParameters; }
  114. }
  115. public override SecurityTokenParameters CounterParameters {
  116. get { return security.InitiatorParameters; }
  117. }
  118. public override MessageDirection Direction {
  119. get { return MessageDirection.Output; }
  120. }
  121. public override EndpointAddress MessageTo {
  122. get { return null; }
  123. }
  124. public override bool ShouldIncludeToken (SecurityTokenInclusionMode mode, bool isInitialized)
  125. {
  126. switch (mode) {
  127. case SecurityTokenInclusionMode.Never:
  128. case SecurityTokenInclusionMode.AlwaysToRecipient:
  129. return false;
  130. case SecurityTokenInclusionMode.AlwaysToInitiator:
  131. return true;
  132. case SecurityTokenInclusionMode.Once:
  133. return !isInitialized;
  134. }
  135. throw new Exception ("Internal Error: should not happen.");
  136. }
  137. public override ScopedMessagePartSpecification SignatureParts {
  138. get { return Security.ChannelRequirements.OutgoingSignatureParts; }
  139. }
  140. public override ScopedMessagePartSpecification EncryptionParts {
  141. get { return Security.ChannelRequirements.OutgoingEncryptionParts; }
  142. }
  143. }
  144. internal abstract class MessageSecurityGenerator
  145. {
  146. Message msg;
  147. SecurityMessageProperty secprop;
  148. MessageSecurityBindingSupport security;
  149. int idbase;
  150. public MessageSecurityGenerator (Message msg,
  151. MessageSecurityBindingSupport security)
  152. {
  153. this.msg = msg;
  154. this.security = security;
  155. }
  156. public Message Message {
  157. get { return msg; }
  158. }
  159. public MessageSecurityBindingSupport Security {
  160. get { return security; }
  161. }
  162. public abstract SecurityTokenParameters Parameters { get; }
  163. public abstract SecurityTokenParameters CounterParameters { get; }
  164. public abstract MessageDirection Direction { get; }
  165. public abstract EndpointAddress MessageTo { get; }
  166. public abstract ScopedMessagePartSpecification SignatureParts { get; }
  167. public abstract ScopedMessagePartSpecification EncryptionParts { get; }
  168. public MessagePartSpecification SignaturePart {
  169. get {
  170. MessagePartSpecification spec;
  171. if (!SignatureParts.TryGetParts (GetAction (), false, out spec))
  172. spec = SignatureParts.ChannelParts;
  173. return spec;
  174. }
  175. }
  176. public MessagePartSpecification EncryptionPart {
  177. get {
  178. MessagePartSpecification spec;
  179. if (!EncryptionParts.TryGetParts (GetAction (), false, out spec))
  180. spec = EncryptionParts.ChannelParts;
  181. return spec;
  182. }
  183. }
  184. public abstract bool ShouldIncludeToken (SecurityTokenInclusionMode mode, bool isInitialized);
  185. public bool ShouldOutputEncryptedKey {
  186. get { return Direction == MessageDirection.Input || secprop.ProtectionToken == null; } //security.Element is AsymmetricSecurityBindingElement; }
  187. }
  188. public Message SecureMessage ()
  189. {
  190. secprop = Message.Properties.Security ?? new SecurityMessageProperty ();
  191. SecurityToken encToken =
  192. secprop.InitiatorToken != null ? secprop.InitiatorToken.SecurityToken : security.EncryptionToken;
  193. // FIXME: it might be still incorrect.
  194. SecurityToken signToken =
  195. Parameters == CounterParameters ? null :
  196. security.SigningToken;
  197. MessageProtectionOrder protectionOrder =
  198. security.MessageProtectionOrder;
  199. SecurityBindingElement element =
  200. security.Element;
  201. SecurityAlgorithmSuite suite = element.DefaultAlgorithmSuite;
  202. string messageId = "uuid-" + Guid.NewGuid ();
  203. int identForMessageId = 1;
  204. XmlDocument doc = new XmlDocument ();
  205. doc.PreserveWhitespace = true;
  206. var action = msg.Headers.Action;
  207. if (msg.Version.Addressing != AddressingVersion.None) {
  208. AddAddressingToHeader (msg.Headers);
  209. }
  210. // wss:Security
  211. WSSecurityMessageHeader header =
  212. new WSSecurityMessageHeader (security.TokenSerializer);
  213. msg.Headers.Add (header);
  214. // 1. [Timestamp]
  215. if (element.IncludeTimestamp) {
  216. AddTimestampToHeader (header, messageId + "-" + identForMessageId++);
  217. }
  218. XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
  219. nsmgr.AddNamespace ("s", msg.Version.Envelope.Namespace);
  220. nsmgr.AddNamespace ("o", Constants.WssNamespace);
  221. nsmgr.AddNamespace ("u", Constants.WsuNamespace);
  222. nsmgr.AddNamespace ("o11", Constants.Wss11Namespace);
  223. /*WrappedKey*/SecurityToken primaryToken = null;
  224. SecurityToken actualToken = null;
  225. SecurityKeyIdentifierClause actualClause = null;
  226. SymmetricAlgorithm masterKey = new RijndaelManaged ();
  227. masterKey.KeySize = suite.DefaultSymmetricKeyLength;
  228. masterKey.Mode = CipherMode.CBC;
  229. masterKey.Padding = PaddingMode.ISO10126;
  230. SymmetricAlgorithm actualKey = masterKey;
  231. // 2. [Encryption Token]
  232. // SecurityTokenInclusionMode
  233. // - Initiator or Recipient
  234. // - done or notyet. FIXME: not implemented yet
  235. // It also affects on key reference output
  236. bool includeEncToken = // /* FIXME: remove this hack */Parameters is SslSecurityTokenParameters ? false :
  237. ShouldIncludeToken (
  238. Security.RecipientParameters.InclusionMode, false);
  239. bool includeSigToken = // /* FIXME: remove this hack */ Parameters is SslSecurityTokenParameters ? false :
  240. ShouldIncludeToken (
  241. Security.InitiatorParameters.InclusionMode, false);
  242. SecurityKeyIdentifierClause encClause = ShouldOutputEncryptedKey ?
  243. CounterParameters.CallCreateKeyIdentifierClause (encToken, !ShouldOutputEncryptedKey ? SecurityTokenReferenceStyle.Internal : includeEncToken ? Parameters.ReferenceStyle : SecurityTokenReferenceStyle.External) : null;
  244. MessagePartSpecification encSpec = EncryptionPart;
  245. // encryption key (possibly also used for signing)
  246. // FIXME: get correct SymmetricAlgorithm according to the algorithm suite
  247. if (secprop.EncryptionKey != null)
  248. actualKey.Key = secprop.EncryptionKey;
  249. // FIXME: remove thid hack
  250. if (!ShouldOutputEncryptedKey)
  251. primaryToken = secprop.ProtectionToken.SecurityToken as WrappedKeySecurityToken;
  252. else
  253. primaryToken =
  254. // FIXME: remove this hack?
  255. encToken is SecurityContextSecurityToken ? encToken :
  256. new WrappedKeySecurityToken (messageId + "-" + identForMessageId++,
  257. actualKey.Key,
  258. // security.DefaultKeyWrapAlgorithm,
  259. Parameters.InternalHasAsymmetricKey ?
  260. suite.DefaultAsymmetricKeyWrapAlgorithm :
  261. suite.DefaultSymmetricKeyWrapAlgorithm,
  262. encToken,
  263. encClause != null ? new SecurityKeyIdentifier (encClause) : null);
  264. // If it reuses request's encryption key, do not output.
  265. if (ShouldOutputEncryptedKey)
  266. header.AddContent (primaryToken);
  267. actualToken = primaryToken;
  268. // FIXME: I doubt it is correct...
  269. WrappedKeySecurityToken requestEncKey = ShouldOutputEncryptedKey ? null : primaryToken as WrappedKeySecurityToken;
  270. actualClause = requestEncKey == null ? (SecurityKeyIdentifierClause)
  271. new LocalIdKeyIdentifierClause (actualToken.Id, typeof (WrappedKeySecurityToken)) :
  272. new InternalEncryptedKeyIdentifierClause (SHA1.Create ().ComputeHash (requestEncKey.GetWrappedKey ()));
  273. // generate derived key if needed
  274. if (CounterParameters.RequireDerivedKeys) {
  275. var dkeyToken = CreateDerivedKey (GenerateId (doc), actualClause, actualKey);
  276. actualToken = dkeyToken;
  277. actualKey.Key = ((SymmetricSecurityKey)dkeyToken.SecurityKeys [0]).GetSymmetricKey ();
  278. actualClause = new LocalIdKeyIdentifierClause (dkeyToken.Id);
  279. header.AddContent (dkeyToken);
  280. }
  281. ReferenceList refList = new ReferenceList ();
  282. // When encrypted with DerivedKeyToken, put references
  283. // immediately after the derived token (not inside the
  284. // primary token).
  285. // Similarly, when we do not output EncryptedKey,
  286. // output ReferenceList in the same way.
  287. if (CounterParameters.RequireDerivedKeys ||
  288. !ShouldOutputEncryptedKey)
  289. header.AddContent (refList);
  290. else
  291. ((WrappedKeySecurityToken) primaryToken).ReferenceList = refList;
  292. // [Signature Confirmation]
  293. if (security.RequireSignatureConfirmation && secprop.ConfirmedSignatures.Count > 0)
  294. foreach (string value in secprop.ConfirmedSignatures)
  295. header.AddContent (new Wss11SignatureConfirmation (GenerateId (doc), value));
  296. SupportingTokenInfoCollection tokenInfos =
  297. Direction == MessageDirection.Input ?
  298. security.CollectSupportingTokens (GetAction ()) :
  299. new SupportingTokenInfoCollection (); // empty
  300. foreach (SupportingTokenInfo tinfo in tokenInfos)
  301. header.AddContent (tinfo.Token);
  302. // populate DOM to sign.
  303. XPathNavigator nav = doc.CreateNavigator ();
  304. using (XmlWriter w = nav.AppendChild ()) {
  305. msg.WriteMessage (w);
  306. }
  307. XmlElement body = doc.SelectSingleNode ("/s:Envelope/s:Body/*", nsmgr) as XmlElement;
  308. string bodyId = null;
  309. Collection<WSSignedXml> endorsedSignatures =
  310. new Collection<WSSignedXml> ();
  311. bool signatureProtection = (protectionOrder == MessageProtectionOrder.SignBeforeEncryptAndEncryptSignature);
  312. // Below are o:Security contents that are not signed...
  313. if (includeSigToken && signToken != null)
  314. header.AddContent (signToken);
  315. switch (protectionOrder) {
  316. case MessageProtectionOrder.EncryptBeforeSign:
  317. // FIXME: implement
  318. throw new NotImplementedException ();
  319. case MessageProtectionOrder.SignBeforeEncrypt:
  320. case MessageProtectionOrder.SignBeforeEncryptAndEncryptSignature:
  321. var sig = CreateSignature (doc, body, nsmgr, tokenInfos,
  322. actualClause, actualKey, signToken, includeSigToken,
  323. signatureProtection, header, endorsedSignatures,
  324. ref bodyId);
  325. // encrypt
  326. WSEncryptedXml exml = new WSEncryptedXml (doc);
  327. EncryptedData edata = Encrypt (body, actualKey, actualToken.Id, refList, actualClause, exml, doc, EncryptedXml.XmlEncElementContentUrl);
  328. EncryptedXml.ReplaceElement (body, edata, false);
  329. // encrypt signature
  330. if (signatureProtection) {
  331. XmlElement sigxml = sig.GetXml ();
  332. edata = Encrypt (sigxml, actualKey, actualToken.Id, refList, actualClause, exml, doc, EncryptedXml.XmlEncElementUrl);
  333. header.AddContent (edata);
  334. foreach (WSSignedXml ssxml in endorsedSignatures) {
  335. sigxml = ssxml.GetXml ();
  336. edata = Encrypt (sigxml, actualKey, actualToken.Id, refList, actualClause, exml, doc, EncryptedXml.XmlEncElementUrl);
  337. header.AddContent (edata);
  338. }
  339. if (security.RequireSignatureConfirmation) {
  340. Collection<Wss11SignatureConfirmation> confs = header.FindAll<Wss11SignatureConfirmation> ();
  341. int count = 0;
  342. foreach (XmlElement elem in doc.SelectNodes ("/s:Envelope/s:Header/o:Security/o11:SignatureConfirmation", nsmgr)) {
  343. edata = Encrypt (elem, actualKey, confs [count].Id, refList, actualClause, exml, doc, EncryptedXml.XmlEncElementUrl);
  344. EncryptedXml.ReplaceElement (elem, edata, false);
  345. header.Contents.Insert (header.Contents.IndexOf (confs [count]), edata);
  346. header.Contents.Remove (confs [count++]);
  347. }
  348. }
  349. }
  350. // encrypt Encrypted supporting tokens
  351. foreach (SupportingTokenInfo tinfo in tokenInfos) {
  352. if (tinfo.Mode == SecurityTokenAttachmentMode.SignedEncrypted) {
  353. XmlElement el = exml.GetIdElement (doc, tinfo.Token.Id);
  354. tinfo.Encrypted = Encrypt (el, actualKey, actualToken.Id, refList, actualClause, exml, doc, EncryptedXml.XmlEncElementUrl);
  355. EncryptedXml.ReplaceElement (el, tinfo.Encrypted, false);
  356. header.Contents.Insert (header.Contents.IndexOf (tinfo.Token), tinfo.Encrypted);
  357. header.Contents.Remove (tinfo.Token);
  358. }
  359. }
  360. break;
  361. }
  362. Message ret = new WSSecurityMessage (Message.CreateMessage (msg.Version, action, new XmlNodeReader (doc.SelectSingleNode ("/s:Envelope/s:Body/*", nsmgr) as XmlElement)), bodyId);
  363. ret.Properties.Security = (SecurityMessageProperty) secprop.CreateCopy ();
  364. ret.Properties.Security.EncryptionKey = masterKey.Key;
  365. // FIXME: can we support TransportToken here?
  366. if (element is AsymmetricSecurityBindingElement) {
  367. ret.Properties.Security.InitiatorToken = new SecurityTokenSpecification (encToken, null); // FIXME: second argument
  368. ret.Properties.Security.InitiatorToken = new SecurityTokenSpecification (signToken, null); // FIXME: second argument
  369. }
  370. else
  371. ret.Properties.Security.ProtectionToken = new SecurityTokenSpecification (primaryToken, null);
  372. ret.Headers.Clear ();
  373. ret.Headers.CopyHeadersFrom (msg);
  374. // Header contents are:
  375. // - Timestamp
  376. // - SignatureConfirmation if required
  377. // - EncryptionToken if included
  378. // - derived key token for EncryptionToken
  379. // - ReferenceList for encrypted items
  380. // - signed supporting tokens
  381. // - signed endorsing supporting tokens
  382. // (i.e. Signed/SignedEncrypted/SignedEndorsing)
  383. // - Signature Token if different from enc token.
  384. // - derived key token for sig token if different
  385. // - Signature for:
  386. // - Timestamp
  387. // - supporting tokens (regardless of
  388. // its inclusion)
  389. // - message parts in SignedParts
  390. // - SignatureToken if TokenProtection
  391. // (regardless of its inclusion)
  392. // - Signatures for the main signature (above),
  393. // for every endorsing token and signed
  394. // endorsing token.
  395. //
  396. //MessageBuffer zzz = ret.CreateBufferedCopy (100000);
  397. //ret = zzz.CreateMessage ();
  398. //Console.WriteLine (zzz.CreateMessage ());
  399. return ret;
  400. }
  401. Signature CreateSignature (XmlDocument doc, XmlElement body,
  402. XmlNamespaceManager nsmgr,
  403. SupportingTokenInfoCollection tokenInfos,
  404. SecurityKeyIdentifierClause actualClause,
  405. SymmetricAlgorithm actualKey,
  406. SecurityToken signToken,
  407. bool includeSigToken,
  408. bool signatureProtection,
  409. WSSecurityMessageHeader header,
  410. Collection<WSSignedXml> endorsedSignatures,
  411. ref string bodyId)
  412. {
  413. // sign
  414. // see clause 8 of WS-SecurityPolicy C.2.2
  415. WSSignedXml sxml = new WSSignedXml (doc);
  416. SecurityTokenReferenceKeyInfo sigKeyInfo;
  417. XmlElement secElem = null;
  418. var sigSpec = SignaturePart;
  419. var serializer = security.TokenSerializer;
  420. var suite = security.Element.DefaultAlgorithmSuite;
  421. var sig = sxml.Signature;
  422. sig.SignedInfo.CanonicalizationMethod =
  423. suite.DefaultCanonicalizationAlgorithm;
  424. foreach (XmlElement elem in doc.SelectNodes ("/s:Envelope/s:Header/o:Security/u:Timestamp", nsmgr))
  425. CreateReference(sig, elem, elem.GetAttribute ("Id", Constants.WsuNamespace));
  426. foreach (XmlElement elem in doc.SelectNodes ("/s:Envelope/s:Header/o:Security/o11:SignatureConfirmation", nsmgr))
  427. CreateReference(sig, elem, elem.GetAttribute ("Id", Constants.WsuNamespace));
  428. foreach (SupportingTokenInfo tinfo in tokenInfos)
  429. if (tinfo.Mode != SecurityTokenAttachmentMode.Endorsing) {
  430. XmlElement el = sxml.GetIdElement (doc, tinfo.Token.Id);
  431. CreateReference (sig, el, el.GetAttribute ("Id", Constants.WsuNamespace));
  432. }
  433. XmlNodeList nodes = doc.SelectNodes ("/s:Envelope/s:Header/*", nsmgr);
  434. for (int i = 0; i < msg.Headers.Count; i++) {
  435. MessageHeaderInfo h = msg.Headers [i];
  436. if (h.Name == "Security" && h.Namespace == Constants.WssNamespace)
  437. secElem = nodes [i] as XmlElement;
  438. else if ((sigSpec.HeaderTypes.Count == 0 ||
  439. sigSpec.HeaderTypes.Contains (new XmlQualifiedName(h.Name, h.Namespace))) &&
  440. (msg.Version.Addressing != AddressingVersion.None ||
  441. !String.Equals (h.Name, "Action", StringComparison.Ordinal))) {
  442. string id = GenerateId (doc);
  443. h.Id = id;
  444. CreateReference (sig, nodes [i] as XmlElement, id);
  445. }
  446. }
  447. if (sigSpec.IsBodyIncluded) {
  448. bodyId = GenerateId (doc);
  449. CreateReference (sig, body.ParentNode as XmlElement, bodyId);
  450. }
  451. if (security.DefaultSignatureAlgorithm == SignedXml.XmlDsigHMACSHA1Url) {
  452. // FIXME: use appropriate hash algorithm
  453. sxml.ComputeSignature (new HMACSHA1(actualKey.Key));
  454. sigKeyInfo = new SecurityTokenReferenceKeyInfo (actualClause, serializer, doc);
  455. } else {
  456. SecurityKeyIdentifierClause signClause =
  457. CounterParameters.CallCreateKeyIdentifierClause (signToken, includeSigToken ? CounterParameters.ReferenceStyle : SecurityTokenReferenceStyle.External);
  458. AsymmetricSecurityKey signKey = (AsymmetricSecurityKey)signToken.ResolveKeyIdentifierClause (signClause);
  459. sxml.SigningKey = signKey.GetAsymmetricAlgorithm (security.DefaultSignatureAlgorithm, true);
  460. sxml.ComputeSignature ();
  461. sigKeyInfo = new SecurityTokenReferenceKeyInfo (signClause, serializer, doc);
  462. }
  463. sxml.KeyInfo = new KeyInfo ();
  464. sxml.KeyInfo.AddClause (sigKeyInfo);
  465. if (!signatureProtection)
  466. header.AddContent (sig);
  467. // endorse the signature with (signed)endorsing
  468. // supporting tokens.
  469. foreach (SupportingTokenInfo tinfo in tokenInfos) {
  470. switch (tinfo.Mode) {
  471. case SecurityTokenAttachmentMode.Endorsing:
  472. case SecurityTokenAttachmentMode.SignedEndorsing:
  473. if (sxml.Signature.Id == null) {
  474. sig.Id = GenerateId (doc);
  475. secElem.AppendChild (sxml.GetXml ());
  476. }
  477. WSSignedXml ssxml = new WSSignedXml (doc);
  478. ssxml.Signature.SignedInfo.CanonicalizationMethod = suite.DefaultCanonicalizationAlgorithm;
  479. CreateReference (ssxml.Signature, doc, sig.Id);
  480. SecurityToken sst = tinfo.Token;
  481. SecurityKey ssk = sst.SecurityKeys [0]; // FIXME: could be different?
  482. SecurityKeyIdentifierClause tclause = new LocalIdKeyIdentifierClause (sst.Id); // FIXME: could be different?
  483. if (ssk is SymmetricSecurityKey) {
  484. SymmetricSecurityKey signKey = (SymmetricSecurityKey)ssk;
  485. ssxml.ComputeSignature (signKey.GetKeyedHashAlgorithm(suite.DefaultSymmetricSignatureAlgorithm));
  486. } else {
  487. AsymmetricSecurityKey signKey = (AsymmetricSecurityKey)ssk;
  488. ssxml.SigningKey = signKey.GetAsymmetricAlgorithm (suite.DefaultAsymmetricSignatureAlgorithm, true);
  489. ssxml.ComputeSignature ();
  490. }
  491. ssxml.KeyInfo.AddClause (new SecurityTokenReferenceKeyInfo (tclause, serializer, doc));
  492. if (!signatureProtection)
  493. header.AddContent (ssxml.Signature);
  494. endorsedSignatures.Add (ssxml);
  495. break;
  496. }
  497. }
  498. return sig;
  499. }
  500. void AddAddressingToHeader (MessageHeaders headers)
  501. {
  502. // FIXME: get correct ReplyTo value
  503. if (Direction == MessageDirection.Input)
  504. headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
  505. if (MessageTo != null)
  506. headers.To = MessageTo.Uri;
  507. }
  508. DerivedKeySecurityToken CreateDerivedKey (string id,
  509. SecurityKeyIdentifierClause actualClause,
  510. SymmetricAlgorithm actualKey)
  511. {
  512. RijndaelManaged deriv = new RijndaelManaged ();
  513. deriv.KeySize = security.Element.DefaultAlgorithmSuite.DefaultEncryptionKeyDerivationLength;
  514. deriv.Mode = CipherMode.CBC;
  515. deriv.Padding = PaddingMode.ISO10126;
  516. deriv.GenerateKey ();
  517. var dkeyToken = new DerivedKeySecurityToken (
  518. id,
  519. null, // algorithm
  520. actualClause,
  521. new InMemorySymmetricSecurityKey (actualKey.Key),
  522. null, // name
  523. null, // generation
  524. null, // offset
  525. deriv.Key.Length,
  526. null, // label
  527. deriv.Key);
  528. return dkeyToken;
  529. }
  530. void AddTimestampToHeader (WSSecurityMessageHeader header, string id)
  531. {
  532. WsuTimestamp timestamp = new WsuTimestamp ();
  533. timestamp.Id = id;
  534. timestamp.Created = DateTime.Now;
  535. // FIXME: on service side, use element.LocalServiceSettings.TimestampValidityDuration
  536. timestamp.Expires = timestamp.Created.Add (security.Element.LocalClientSettings.TimestampValidityDuration);
  537. header.AddContent (timestamp);
  538. }
  539. void CreateReference (Signature sig, XmlElement el, string id)
  540. {
  541. CreateReference (sig, el.OwnerDocument, id);
  542. if (el.GetAttribute ("Id", Constants.WsuNamespace) != id) {
  543. XmlAttribute a = el.SetAttributeNode ("Id", Constants.WsuNamespace);
  544. a.Prefix = "u";
  545. a.Value = id;
  546. }
  547. }
  548. void CreateReference (Signature sig, XmlDocument doc, string id)
  549. {
  550. SecurityAlgorithmSuite suite = security.Element.DefaultAlgorithmSuite;
  551. if (id == String.Empty)
  552. id = GenerateId (doc);
  553. Reference r = new Reference ("#" + id);
  554. r.AddTransform (CreateTransform (suite.DefaultCanonicalizationAlgorithm));
  555. r.DigestMethod = suite.DefaultDigestAlgorithm;
  556. sig.SignedInfo.AddReference (r);
  557. }
  558. Transform CreateTransform (string url)
  559. {
  560. switch (url) {
  561. case SignedXml.XmlDsigC14NTransformUrl:
  562. return new XmlDsigC14NTransform ();
  563. case SignedXml.XmlDsigC14NWithCommentsTransformUrl:
  564. return new XmlDsigC14NWithCommentsTransform ();
  565. case SignedXml.XmlDsigExcC14NTransformUrl:
  566. return new XmlDsigExcC14NTransform ();
  567. case SignedXml.XmlDsigExcC14NWithCommentsTransformUrl:
  568. return new XmlDsigExcC14NWithCommentsTransform ();
  569. }
  570. throw new Exception (String.Format ("INTERNAL ERROR: Invalid canonicalization URL: {0}", url));
  571. }
  572. EncryptedData Encrypt (XmlElement target, SymmetricAlgorithm actualKey, string ekeyId, ReferenceList refList, SecurityKeyIdentifierClause encClause, EncryptedXml exml, XmlDocument doc, string elementType)
  573. {
  574. SecurityAlgorithmSuite suite = security.Element.DefaultAlgorithmSuite;
  575. SecurityTokenSerializer serializer = security.TokenSerializer;
  576. byte [] encrypted = exml.EncryptData (target, actualKey, false);
  577. EncryptedData edata = new EncryptedData ();
  578. edata.Id = GenerateId (doc);
  579. edata.Type = elementType;
  580. edata.EncryptionMethod = new EncryptionMethod (suite.DefaultEncryptionAlgorithm);
  581. // FIXME: here wsse:DigestMethod should be embedded
  582. // inside EncryptionMethod. Since it is not possible
  583. // with S.S.C.Xml.EncryptionMethod, we will have to
  584. // build our own XML encryption classes.
  585. edata.CipherData.CipherValue = encrypted;
  586. DataReference dr = new DataReference ();
  587. dr.Uri = "#" + edata.Id;
  588. refList.Add (dr);
  589. if (ShouldOutputEncryptedKey && !CounterParameters.RequireDerivedKeys)
  590. edata.KeyInfo = null;
  591. else {
  592. edata.KeyInfo = new KeyInfo ();
  593. edata.KeyInfo.AddClause (new SecurityTokenReferenceKeyInfo (encClause, serializer, doc));
  594. }
  595. return edata;
  596. }
  597. string GenerateId (XmlDocument doc)
  598. {
  599. idbase++;
  600. return secprop.SenderIdPrefix + idbase;
  601. }
  602. public string GetAction ()
  603. {
  604. string ret = msg.Headers.Action;
  605. if (ret == null) {
  606. HttpRequestMessageProperty reqprop =
  607. msg.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
  608. if (reqprop != null)
  609. ret = reqprop.Headers ["Action"];
  610. }
  611. return ret;
  612. }
  613. }
  614. internal class WSSecurityMessage : Message
  615. {
  616. Message msg;
  617. string body_id;
  618. public WSSecurityMessage (Message msg, string bodyId)
  619. {
  620. this.msg = msg;
  621. this.body_id = bodyId;
  622. }
  623. public override MessageVersion Version {
  624. get { return msg.Version; }
  625. }
  626. public override MessageHeaders Headers {
  627. get { return msg.Headers; }
  628. }
  629. public override MessageProperties Properties {
  630. get { return msg.Properties; }
  631. }
  632. protected override MessageBuffer OnCreateBufferedCopy (int maxBufferSize)
  633. {
  634. return new WSSecurityMessageBuffer (msg.CreateBufferedCopy (maxBufferSize), body_id);
  635. }
  636. protected override string OnGetBodyAttribute (string localName, string ns)
  637. {
  638. if (localName == "Id" && ns == Constants.WsuNamespace)
  639. return body_id;
  640. return msg.GetBodyAttribute (localName, ns);
  641. }
  642. protected override void OnWriteStartBody (
  643. XmlDictionaryWriter writer)
  644. {
  645. var dic = Constants.SoapDictionary;
  646. writer.WriteStartElement ("s", dic.Add ("Body"), dic.Add (Version.Envelope.Namespace));
  647. if (body_id != null)
  648. writer.WriteAttributeString ("u", "Id", Constants.WsuNamespace, body_id);
  649. }
  650. protected override void OnWriteBodyContents (XmlDictionaryWriter w)
  651. {
  652. msg.WriteBodyContents (w);
  653. }
  654. }
  655. internal class WSSecurityMessageBuffer : MessageBuffer
  656. {
  657. public WSSecurityMessageBuffer (MessageBuffer mb, string bodyId)
  658. {
  659. buffer = mb;
  660. body_id = bodyId;
  661. }
  662. MessageBuffer buffer;
  663. string body_id;
  664. public override int BufferSize {
  665. get { return buffer.BufferSize; }
  666. }
  667. public override void Close ()
  668. {
  669. buffer.Close ();
  670. }
  671. public override Message CreateMessage ()
  672. {
  673. return new WSSecurityMessage (buffer.CreateMessage (), body_id);
  674. }
  675. }
  676. }