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