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 
InitiatorMessageSecurityGenerator( Message msg, InitiatorMessageSecurityBindingSupport security, EndpointAddress messageTo)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 
ShouldIncludeToken(SecurityTokenInclusionMode mode, bool isInitialized)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 
RecipientMessageSecurityGenerator( Message msg, SecurityMessageProperty requestSecProp, RecipientMessageSecurityBindingSupport security)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 
ShouldIncludeToken(SecurityTokenInclusionMode mode, bool isInitialized)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 
MessageSecurityGenerator(Message msg, MessageSecurityBindingSupport security)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 
ShouldIncludeToken(SecurityTokenInclusionMode mode, bool isInitialized)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 
SecureMessage()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 
CreateSignature(XmlDocument doc, XmlElement body, XmlNamespaceManager nsmgr, SupportingTokenInfoCollection tokenInfos, SecurityKeyIdentifierClause actualClause, SymmetricAlgorithm actualKey, SecurityToken signToken, bool includeSigToken, bool signatureProtection, WSSecurityMessageHeader header, Collection<WSSignedXml> endorsedSignatures, ref string bodyId)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 
AddAddressingToHeader(MessageHeaders headers)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 
CreateDerivedKey(string id, SecurityKeyIdentifierClause actualClause, SymmetricAlgorithm actualKey)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 
AddTimestampToHeader(WSSecurityMessageHeader header, string id)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 
CreateReference(Signature sig, XmlElement el, string id)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 
CreateReference(Signature sig, XmlDocument doc, string id)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 
CreateTransform(string url)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 
Encrypt(XmlElement target, SymmetricAlgorithm actualKey, string ekeyId, ReferenceList refList, SecurityKeyIdentifierClause encClause, EncryptedXml exml, XmlDocument doc, string elementType)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 
GenerateId(XmlDocument doc)704 		string GenerateId (XmlDocument doc)
705 		{
706 			idbase++;
707 			return secprop.SenderIdPrefix + idbase;
708 		}
709 
GetAction()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 
WSSecurityMessage(Message msg, string bodyId)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 
OnCreateBufferedCopy(int maxBufferSize)746 		protected override MessageBuffer OnCreateBufferedCopy (int maxBufferSize)
747 		{
748 			return new WSSecurityMessageBuffer (msg.CreateBufferedCopy (maxBufferSize), body_id);
749 		}
750 
OnGetBodyAttribute(string localName, string ns)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 
OnWriteStartBody( XmlDictionaryWriter writer)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 
OnWriteBodyContents(XmlDictionaryWriter w)769 		protected override void OnWriteBodyContents (XmlDictionaryWriter w)
770 		{
771 			msg.WriteBodyContents (w);
772 		}
773 	}
774 
775 	internal class WSSecurityMessageBuffer : MessageBuffer
776 	{
WSSecurityMessageBuffer(MessageBuffer mb, string bodyId)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 
Close()790 		public override void Close ()
791 		{
792 			buffer.Close ();
793 		}
794 
CreateMessage()795 		public override Message CreateMessage ()
796 		{
797 			return new WSSecurityMessage (buffer.CreateMessage (), body_id);
798 		}
799 	}
800 }
801