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