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