1 // 2 // SignedXml.cs - SignedXml implementation for XML Signature 3 // 4 // Author: 5 // Sebastien Pouliot <sebastien@ximian.com> 6 // Atsushi Enomoto <atsushi@ximian.com> 7 // Tim Coleman <tim@timcoleman.com> 8 // 9 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) 10 // Copyright (C) Tim Coleman, 2004 11 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining 14 // a copy of this software and associated documentation files (the 15 // "Software"), to deal in the Software without restriction, including 16 // without limitation the rights to use, copy, modify, merge, publish, 17 // distribute, sublicense, and/or sell copies of the Software, and to 18 // permit persons to whom the Software is furnished to do so, subject to 19 // the following conditions: 20 // 21 // The above copyright notice and this permission notice shall be 22 // included in all copies or substantial portions of the Software. 23 // 24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 // 32 33 using System.Collections; 34 using System.IO; 35 using System.Runtime.InteropServices; 36 using System.Security.Cryptography; 37 using System.Security.Policy; 38 using System.Net; 39 using System.Text; 40 using System.Xml; 41 using System.Security.Cryptography.X509Certificates; 42 43 namespace System.Security.Cryptography.Xml { 44 45 public class SignedXml { 46 public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#"; 47 public const string XmlDsigMinimalCanonicalizationUrl = "http://www.w3.org/2000/09/xmldsig#minimal"; 48 public const string XmlDsigCanonicalizationUrl = XmlDsigC14NTransformUrl; 49 public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigC14NWithCommentsTransformUrl; 50 51 public const string XmlDsigSHA1Url = "http://www.w3.org/2000/09/xmldsig#sha1"; 52 public const string XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; 53 public const string XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; 54 public const string XmlDsigHMACSHA1Url = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; 55 56 public const string XmlDsigSHA256Url = "http://www.w3.org/2001/04/xmlenc#sha256"; 57 public const string XmlDsigRSASHA256Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; 58 59 // Yes, SHA384 is in the xmldsig-more namespace even though all the other SHA variants are in xmlenc. That's the standard. 60 public const string XmlDsigSHA384Url = "http://www.w3.org/2001/04/xmldsig-more#sha384"; 61 public const string XmlDsigRSASHA384Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"; 62 63 public const string XmlDsigSHA512Url = "http://www.w3.org/2001/04/xmlenc#sha512"; 64 public const string XmlDsigRSASHA512Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; 65 66 public const string XmlDsigC14NTransformUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; 67 public const string XmlDsigC14NWithCommentsTransformUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; 68 public const string XmlDsigExcC14NTransformUrl = "http://www.w3.org/2001/10/xml-exc-c14n#"; 69 public const string XmlDsigExcC14NWithCommentsTransformUrl = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; 70 public const string XmlDsigBase64TransformUrl = "http://www.w3.org/2000/09/xmldsig#base64"; 71 public const string XmlDsigXPathTransformUrl = "http://www.w3.org/TR/1999/REC-xpath-19991116"; 72 public const string XmlDsigXsltTransformUrl = "http://www.w3.org/TR/1999/REC-xslt-19991116"; 73 public const string XmlDsigEnvelopedSignatureTransformUrl = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; 74 public const string XmlDecryptionTransformUrl = "http://www.w3.org/2002/07/decrypt#XML"; 75 public const string XmlLicenseTransformUrl = "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform"; 76 77 private EncryptedXml encryptedXml; 78 79 protected Signature m_signature; 80 private AsymmetricAlgorithm key; 81 protected string m_strSigningKeyName; 82 private XmlDocument envdoc; 83 private IEnumerator pkEnumerator; 84 private XmlElement signatureElement; 85 private Hashtable hashes; 86 // FIXME: enable it after CAS implementation 87 internal XmlResolver _xmlResolver = new XmlUrlResolver (); 88 private bool _bResolverSet = true; 89 internal XmlElement _context; 90 private ArrayList manifests; 91 private IEnumerator _x509Enumerator; 92 93 private static readonly char [] whitespaceChars = new char [] {' ', '\r', '\n', '\t'}; 94 SignedXml()95 public SignedXml () 96 { 97 m_signature = new Signature (); 98 m_signature.SignedInfo = new SignedInfo (); 99 hashes = new Hashtable (2); // 98% SHA1 for now 100 _context = null; 101 } 102 SignedXml(XmlDocument document)103 public SignedXml (XmlDocument document) : this () 104 { 105 if (document == null) 106 throw new ArgumentNullException ("document"); 107 envdoc = document; 108 _context = document.DocumentElement; 109 } 110 SignedXml(XmlElement elem)111 public SignedXml (XmlElement elem) : this () 112 { 113 if (elem == null) 114 throw new ArgumentNullException ("elem"); 115 envdoc = new XmlDocument (); 116 _context = elem; 117 envdoc.LoadXml (elem.OuterXml); 118 } 119 120 [ComVisible (false)] 121 public EncryptedXml EncryptedXml { 122 get { return encryptedXml; } 123 set { encryptedXml = value; } 124 } 125 126 public KeyInfo KeyInfo { 127 get { 128 if (m_signature.KeyInfo == null) 129 m_signature.KeyInfo = new KeyInfo (); 130 return m_signature.KeyInfo; 131 } 132 set { m_signature.KeyInfo = value; } 133 } 134 135 public Signature Signature { 136 get { return m_signature; } 137 } 138 139 public string SignatureLength { 140 get { return m_signature.SignedInfo.SignatureLength; } 141 } 142 143 public string SignatureMethod { 144 get { return m_signature.SignedInfo.SignatureMethod; } 145 } 146 147 public byte[] SignatureValue { 148 get { return m_signature.SignatureValue; } 149 } 150 151 public SignedInfo SignedInfo { 152 get { return m_signature.SignedInfo; } 153 } 154 155 public AsymmetricAlgorithm SigningKey { 156 get { return key; } 157 set { key = value; } 158 } 159 160 // NOTE: CryptoAPI related ? documented as fx internal 161 public string SigningKeyName { 162 get { return m_strSigningKeyName; } 163 set { m_strSigningKeyName = value; } 164 } 165 166 public XmlResolver Resolver 167 { 168 // This property only has a setter. The rationale for this is that we don't have a good value 169 // to return when it has not been explicitely set, as we are using XmlSecureResolver by default 170 set 171 { 172 _xmlResolver = value; 173 _bResolverSet = true; 174 } 175 } 176 177 internal bool ResolverSet 178 { 179 get { return _bResolverSet; } 180 } 181 AddObject(DataObject dataObject)182 public void AddObject (DataObject dataObject) 183 { 184 m_signature.AddObject (dataObject); 185 } 186 AddReference(Reference reference)187 public void AddReference (Reference reference) 188 { 189 if (reference == null) 190 throw new ArgumentNullException ("reference"); 191 m_signature.SignedInfo.AddReference (reference); 192 } 193 ApplyTransform(Transform t, XmlDocument input)194 private Stream ApplyTransform (Transform t, XmlDocument input) 195 { 196 // These transformer modify input document, which should 197 // not affect to the input itself. 198 if (t is XmlDsigXPathTransform 199 || t is XmlDsigEnvelopedSignatureTransform 200 || t is XmlDecryptionTransform 201 ) 202 input = (XmlDocument) input.Clone (); 203 204 t.LoadInput (input); 205 206 if (t is XmlDsigEnvelopedSignatureTransform) 207 // It returns XmlDocument for XmlDocument input. 208 return CanonicalizeOutput (t.GetOutput ()); 209 210 object obj = t.GetOutput (); 211 if (obj is Stream) 212 return (Stream) obj; 213 else if (obj is XmlDocument) { 214 MemoryStream ms = new MemoryStream (); 215 XmlTextWriter xtw = new XmlTextWriter (ms, Encoding.UTF8); 216 ((XmlDocument) obj).WriteTo (xtw); 217 218 xtw.Flush (); 219 220 // Rewind to the start of the stream 221 ms.Position = 0; 222 return ms; 223 } 224 else if (obj == null) { 225 throw new NotImplementedException ("This should not occur. Transform is " + t + "."); 226 } 227 else { 228 // e.g. XmlDsigXPathTransform returns XmlNodeList 229 return CanonicalizeOutput (obj); 230 } 231 } 232 CanonicalizeOutput(object obj)233 private Stream CanonicalizeOutput (object obj) 234 { 235 Transform c14n = GetC14NMethod (); 236 c14n.LoadInput (obj); 237 return (Stream) c14n.GetOutput (); 238 } 239 GetManifest(Reference r)240 private XmlDocument GetManifest (Reference r) 241 { 242 XmlDocument doc = new XmlDocument (); 243 doc.PreserveWhitespace = true; 244 245 if (r.Uri [0] == '#') { 246 // local manifest 247 if (signatureElement != null) { 248 XmlElement xel = GetIdElement (signatureElement.OwnerDocument, r.Uri.Substring (1)); 249 if (xel == null) 250 throw new CryptographicException ("Manifest targeted by Reference was not found: " + r.Uri.Substring (1)); 251 doc.AppendChild (doc.ImportNode (xel, true)); 252 FixupNamespaceNodes (xel, doc.DocumentElement, false); 253 } 254 } 255 else if (_xmlResolver != null) { 256 // TODO: need testing 257 Stream s = (Stream) _xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream)); 258 doc.Load (s); 259 } 260 261 if (doc.FirstChild != null) { 262 // keep a copy of the manifests to check their references later 263 if (manifests == null) 264 manifests = new ArrayList (); 265 manifests.Add (doc); 266 267 return doc; 268 } 269 return null; 270 } 271 FixupNamespaceNodes(XmlElement src, XmlElement dst, bool ignoreDefault)272 private void FixupNamespaceNodes (XmlElement src, XmlElement dst, bool ignoreDefault) 273 { 274 // add namespace nodes 275 foreach (XmlAttribute attr in src.SelectNodes ("namespace::*")) { 276 if (attr.LocalName == "xml") 277 continue; 278 if (ignoreDefault && attr.LocalName == "xmlns") 279 continue; 280 dst.SetAttributeNode (dst.OwnerDocument.ImportNode (attr, true) as XmlAttribute); 281 } 282 } 283 GetReferenceHash(Reference r, bool check_hmac)284 private byte[] GetReferenceHash (Reference r, bool check_hmac) 285 { 286 Stream s = null; 287 XmlDocument doc = null; 288 if (r.Uri == String.Empty) { 289 doc = envdoc; 290 } 291 else if (r.Type == XmlSignature.Uri.Manifest) { 292 doc = GetManifest (r); 293 } 294 else { 295 doc = new XmlDocument (); 296 doc.PreserveWhitespace = true; 297 string objectName = null; 298 299 if (r.Uri.StartsWith ("#xpointer")) { 300 string uri = string.Join ("", r.Uri.Substring (9).Split (whitespaceChars)); 301 if (uri.Length < 2 || uri [0] != '(' || uri [uri.Length - 1] != ')') 302 // FIXME: how to handle invalid xpointer? 303 uri = String.Empty; 304 else 305 uri = uri.Substring (1, uri.Length - 2); 306 if (uri == "/") 307 doc = envdoc; 308 else if (uri.Length > 6 && uri.StartsWith ("id(") && uri [uri.Length - 1] == ')') 309 // id('foo'), id("foo") 310 objectName = uri.Substring (4, uri.Length - 6); 311 } 312 else if (r.Uri [0] == '#') { 313 objectName = r.Uri.Substring (1); 314 } 315 else if (_xmlResolver != null) { 316 // TODO: test but doc says that Resolver = null -> no access 317 try { 318 // no way to know if valid without throwing an exception 319 Uri uri = new Uri (r.Uri); 320 s = (Stream) _xmlResolver.GetEntity (uri, null, typeof (Stream)); 321 } 322 catch { 323 // may still be a local file (and maybe not xml) 324 s = File.OpenRead (r.Uri); 325 } 326 } 327 if (objectName != null) { 328 XmlElement found = null; 329 foreach (DataObject obj in m_signature.ObjectList) { 330 if (obj.Id == objectName) { 331 found = obj.GetXml (); 332 found.SetAttribute ("xmlns", SignedXml.XmlDsigNamespaceUrl); 333 doc.AppendChild (doc.ImportNode (found, true)); 334 // FIXME: there should be theoretical justification of copying namespace declaration nodes this way. 335 foreach (XmlNode n in found.ChildNodes) 336 // Do not copy default namespace as it must be xmldsig namespace for "Object" element. 337 if (n.NodeType == XmlNodeType.Element) 338 FixupNamespaceNodes (n as XmlElement, doc.DocumentElement, true); 339 break; 340 } 341 } 342 if (found == null && envdoc != null) { 343 found = GetIdElement (envdoc, objectName); 344 if (found != null) { 345 doc.AppendChild (doc.ImportNode (found, true)); 346 FixupNamespaceNodes (found, doc.DocumentElement, false); 347 } 348 } 349 if (found == null) 350 throw new CryptographicException (String.Format ("Malformed reference object: {0}", objectName)); 351 } 352 } 353 354 if (r.TransformChain.Count > 0) { 355 foreach (Transform t in r.TransformChain) { 356 if (s == null) { 357 s = ApplyTransform (t, doc); 358 } 359 else { 360 t.LoadInput (s); 361 object o = t.GetOutput (); 362 if (o is Stream) 363 s = (Stream) o; 364 else 365 s = CanonicalizeOutput (o); 366 } 367 } 368 } 369 else if (s == null) { 370 // we must not C14N references from outside the document 371 // e.g. non-xml documents 372 if (r.Uri [0] != '#') { 373 s = new MemoryStream (); 374 doc.Save (s); 375 } 376 else { 377 // apply default C14N transformation 378 s = ApplyTransform (new XmlDsigC14NTransform (), doc); 379 } 380 } 381 HashAlgorithm digest = GetHash (r.DigestMethod, check_hmac); 382 return (digest == null) ? null : digest.ComputeHash (s); 383 } 384 DigestReferences()385 private void DigestReferences () 386 { 387 // we must tell each reference which hash algorithm to use 388 // before asking for the SignedInfo XML ! 389 foreach (Reference r in m_signature.SignedInfo.References) { 390 // assume SHA-1 if nothing is specified 391 if (r.DigestMethod == null) 392 r.DigestMethod = XmlDsigSHA1Url; 393 r.DigestValue = GetReferenceHash (r, false); 394 } 395 } 396 GetC14NMethod()397 private Transform GetC14NMethod () 398 { 399 Transform t = (Transform) CryptoConfig.CreateFromName (m_signature.SignedInfo.CanonicalizationMethod); 400 if (t == null) 401 throw new CryptographicException ("Unknown Canonicalization Method {0}", m_signature.SignedInfo.CanonicalizationMethod); 402 return t; 403 } 404 SignedInfoTransformed()405 private Stream SignedInfoTransformed () 406 { 407 Transform t = GetC14NMethod (); 408 409 if (signatureElement == null) { 410 // when creating signatures 411 XmlDocument doc = new XmlDocument (); 412 doc.PreserveWhitespace = true; 413 doc.LoadXml (m_signature.SignedInfo.GetXml ().OuterXml); 414 if (envdoc != null) 415 foreach (XmlAttribute attr in envdoc.DocumentElement.SelectNodes ("namespace::*")) { 416 if (attr.LocalName == "xml") 417 continue; 418 if (attr.Prefix == doc.DocumentElement.Prefix) 419 continue; 420 doc.DocumentElement.SetAttributeNode (doc.ImportNode (attr, true) as XmlAttribute); 421 } 422 t.LoadInput (doc); 423 } 424 else { 425 // when verifying signatures 426 // TODO - check m_signature.SignedInfo.Id 427 XmlElement el = signatureElement.GetElementsByTagName (XmlSignature.ElementNames.SignedInfo, XmlSignature.NamespaceURI) [0] as XmlElement; 428 StringWriter sw = new StringWriter (); 429 XmlTextWriter xtw = new XmlTextWriter (sw); 430 xtw.WriteStartElement (el.Prefix, el.LocalName, el.NamespaceURI); 431 432 // context namespace nodes (except for "xmlns:xml") 433 XmlNodeList nl = el.SelectNodes ("namespace::*"); 434 foreach (XmlAttribute attr in nl) { 435 if (attr.ParentNode == el) 436 continue; 437 if (attr.LocalName == "xml") 438 continue; 439 if (attr.Prefix == el.Prefix) 440 continue; 441 attr.WriteTo (xtw); 442 } 443 foreach (XmlNode attr in el.Attributes) 444 attr.WriteTo (xtw); 445 foreach (XmlNode n in el.ChildNodes) 446 n.WriteTo (xtw); 447 448 xtw.WriteEndElement (); 449 byte [] si = Encoding.UTF8.GetBytes (sw.ToString ()); 450 451 MemoryStream ms = new MemoryStream (); 452 ms.Write (si, 0, si.Length); 453 ms.Position = 0; 454 455 t.LoadInput (ms); 456 } 457 // C14N and C14NWithComments always return a Stream in GetOutput 458 return (Stream) t.GetOutput (); 459 } 460 461 // reuse hash - most document will always use the same hash GetHash(string algorithm, bool check_hmac)462 private HashAlgorithm GetHash (string algorithm, bool check_hmac) 463 { 464 HashAlgorithm hash = (HashAlgorithm) hashes [algorithm]; 465 if (hash == null) { 466 hash = HashAlgorithm.Create (algorithm); 467 if (hash == null) 468 throw new CryptographicException ("Unknown hash algorithm: {0}", algorithm); 469 hashes.Add (algorithm, hash); 470 // now ready to be used 471 } 472 else { 473 // important before reusing an hash object 474 hash.Initialize (); 475 } 476 // we can sign using any hash algorith, including HMAC, but we can only verify hash (MS compatibility) 477 if (check_hmac && (hash is KeyedHashAlgorithm)) 478 return null; 479 return hash; 480 } 481 CheckSignature()482 public bool CheckSignature () 483 { 484 return (CheckSignatureInternal (null) != null); 485 } 486 CheckReferenceIntegrity(ArrayList referenceList)487 private bool CheckReferenceIntegrity (ArrayList referenceList) 488 { 489 if (referenceList == null) 490 return false; 491 492 // check digest (hash) for every reference 493 foreach (Reference r in referenceList) { 494 // stop at first broken reference 495 byte[] hash = GetReferenceHash (r, true); 496 if (! Compare (r.DigestValue, hash)) 497 return false; 498 } 499 return true; 500 } 501 CheckSignature(AsymmetricAlgorithm key)502 public bool CheckSignature (AsymmetricAlgorithm key) 503 { 504 if (key == null) 505 throw new ArgumentNullException ("key"); 506 return (CheckSignatureInternal (key) != null); 507 } 508 CheckSignatureInternal(AsymmetricAlgorithm key)509 private AsymmetricAlgorithm CheckSignatureInternal (AsymmetricAlgorithm key) 510 { 511 pkEnumerator = null; 512 513 if (key != null) { 514 // check with supplied key 515 if (!CheckSignatureWithKey (key)) 516 return null; 517 } else { 518 if (Signature.KeyInfo == null) 519 return null; 520 // no supplied key, iterates all KeyInfo 521 while ((key = GetPublicKey ()) != null) { 522 if (CheckSignatureWithKey (key)) { 523 break; 524 } 525 } 526 pkEnumerator = null; 527 if (key == null) 528 return null; 529 } 530 531 // some parts may need to be downloaded 532 // so where doing it last 533 if (!CheckReferenceIntegrity (m_signature.SignedInfo.References)) 534 return null; 535 536 if (manifests != null) { 537 // do not use foreach as a manifest could contain manifests... 538 for (int i=0; i < manifests.Count; i++) { 539 Manifest manifest = new Manifest ((manifests [i] as XmlDocument).DocumentElement); 540 if (! CheckReferenceIntegrity (manifest.References)) 541 return null; 542 } 543 } 544 return key; 545 } 546 547 // Is the signature (over SignedInfo) valid ? CheckSignatureWithKey(AsymmetricAlgorithm key)548 private bool CheckSignatureWithKey (AsymmetricAlgorithm key) 549 { 550 if (key == null) 551 return false; 552 553 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (m_signature.SignedInfo.SignatureMethod); 554 if (sd == null) 555 return false; 556 557 AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm); 558 if (verifier == null) 559 return false; 560 561 try { 562 verifier.SetKey (key); 563 verifier.SetHashAlgorithm (sd.DigestAlgorithm); 564 565 HashAlgorithm hash = GetHash (sd.DigestAlgorithm, true); 566 // get the hash of the C14N SignedInfo element 567 MemoryStream ms = (MemoryStream) SignedInfoTransformed (); 568 569 byte[] digest = hash.ComputeHash (ms); 570 return verifier.VerifySignature (digest, m_signature.SignatureValue); 571 } 572 catch { 573 // e.g. SignatureMethod != AsymmetricAlgorithm type 574 return false; 575 } 576 } 577 Compare(byte[] expected, byte[] actual)578 private bool Compare (byte[] expected, byte[] actual) 579 { 580 bool result = ((expected != null) && (actual != null)); 581 if (result) { 582 int l = expected.Length; 583 result = (l == actual.Length); 584 if (result) { 585 for (int i=0; i < l; i++) { 586 if (expected[i] != actual[i]) 587 return false; 588 } 589 } 590 } 591 return result; 592 } 593 CheckSignature(KeyedHashAlgorithm macAlg)594 public bool CheckSignature (KeyedHashAlgorithm macAlg) 595 { 596 if (macAlg == null) 597 throw new ArgumentNullException ("macAlg"); 598 599 pkEnumerator = null; 600 601 // Is the signature (over SignedInfo) valid ? 602 Stream s = SignedInfoTransformed (); 603 if (s == null) 604 return false; 605 606 byte[] actual = macAlg.ComputeHash (s); 607 // HMAC signature may be partial and specified by <HMACOutputLength> 608 if (m_signature.SignedInfo.SignatureLength != null) { 609 int length = Int32.Parse (m_signature.SignedInfo.SignatureLength); 610 // we only support signatures with a multiple of 8 bits 611 // and the value must match the signature length 612 if ((length & 7) != 0) 613 throw new CryptographicException ("Signature length must be a multiple of 8 bits."); 614 615 // SignatureLength is in bits (and we works on bytes, only in multiple of 8 bits) 616 // and both values must match for a signature to be valid 617 length >>= 3; 618 if (length != m_signature.SignatureValue.Length) 619 throw new CryptographicException ("Invalid signature length."); 620 621 // is the length "big" enough to make the signature meaningful ? 622 // we use a minimum of 80 bits (10 bytes) or half the HMAC normal output length 623 // e.g. HMACMD5 output 128 bits but our minimum is 80 bits (not 64 bits) 624 int minimum = Math.Max (10, actual.Length / 2); 625 if (length < minimum) 626 throw new CryptographicException ("HMAC signature is too small"); 627 628 if (length < actual.Length) { 629 byte[] trunked = new byte [length]; 630 Buffer.BlockCopy (actual, 0, trunked, 0, length); 631 actual = trunked; 632 } 633 } 634 635 if (Compare (m_signature.SignatureValue, actual)) { 636 // some parts may need to be downloaded 637 // so where doing it last 638 return CheckReferenceIntegrity (m_signature.SignedInfo.References); 639 } 640 return false; 641 } 642 643 [MonoTODO] 644 [ComVisible (false)] CheckSignature(X509Certificate2 certificate, bool verifySignatureOnly)645 public bool CheckSignature (X509Certificate2 certificate, bool verifySignatureOnly) 646 { 647 throw new NotImplementedException (); 648 } 649 CheckSignatureReturningKey(out AsymmetricAlgorithm signingKey)650 public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey) 651 { 652 signingKey = CheckSignatureInternal (null); 653 return (signingKey != null); 654 } 655 ComputeSignature()656 public void ComputeSignature () 657 { 658 DigestReferences (); 659 660 if (key == null) 661 throw new CryptographicException (SR.Cryptography_Xml_LoadKeyFailed); 662 663 // Check the signature algorithm associated with the key so that we can accordingly set the signature method 664 if (SignedInfo.SignatureMethod == null) { 665 if (key is DSA) { 666 SignedInfo.SignatureMethod = XmlDsigDSAUrl; 667 } else if (key is RSA) { 668 // Default to RSA-SHA1 669 SignedInfo.SignatureMethod = XmlDsigRSASHA1Url; 670 } else { 671 throw new CryptographicException (SR.Cryptography_Xml_CreatedKeyFailed); 672 } 673 } 674 675 // See if there is a signature description class defined in the Config file 676 SignatureDescription signatureDescription = CryptoConfig.CreateFromName (SignedInfo.SignatureMethod) as SignatureDescription; 677 if (signatureDescription == null) 678 throw new CryptographicException (SR.Cryptography_Xml_SignatureDescriptionNotCreated); 679 680 HashAlgorithm hashAlg = signatureDescription.CreateDigest (); 681 if (hashAlg == null) 682 throw new CryptographicException (SR.Cryptography_Xml_CreateHashAlgorithmFailed); 683 684 byte[] hashvalue = hashAlg.ComputeHash (SignedInfoTransformed ()); 685 AsymmetricSignatureFormatter asymmetricSignatureFormatter = signatureDescription.CreateFormatter (key); 686 687 m_signature.SignatureValue = asymmetricSignatureFormatter.CreateSignature (hashAlg); 688 } 689 ComputeSignature(KeyedHashAlgorithm macAlg)690 public void ComputeSignature (KeyedHashAlgorithm macAlg) 691 { 692 if (macAlg == null) 693 throw new ArgumentNullException ("macAlg"); 694 695 string method = null; 696 697 if (macAlg is HMACSHA1) { 698 method = XmlDsigHMACSHA1Url; 699 } else if (macAlg is HMACSHA256) { 700 method = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; 701 } else if (macAlg is HMACSHA384) { 702 method = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha384"; 703 } else if (macAlg is HMACSHA512) { 704 method = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512"; 705 } else if (macAlg is HMACRIPEMD160) { 706 method = "http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160"; 707 } 708 709 if (method == null) 710 throw new CryptographicException ("unsupported algorithm"); 711 712 DigestReferences (); 713 m_signature.SignedInfo.SignatureMethod = method; 714 m_signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ()); 715 } 716 GetIdElement(XmlDocument document, string idValue)717 public virtual XmlElement GetIdElement (XmlDocument document, string idValue) 718 { 719 if ((document == null) || (idValue == null)) 720 return null; 721 722 // this works only if there's a DTD or XSD available to define the ID 723 XmlElement xel = document.GetElementById (idValue); 724 if (xel == null) { 725 // search an "undefined" ID 726 xel = (XmlElement) document.SelectSingleNode ("//*[@Id='" + idValue + "']"); 727 if (xel == null) { 728 xel = (XmlElement) document.SelectSingleNode ("//*[@ID='" + idValue + "']"); 729 if (xel == null) { 730 xel = (XmlElement) document.SelectSingleNode ("//*[@id='" + idValue + "']"); 731 } 732 } 733 } 734 return xel; 735 } 736 DefaultGetIdElement(XmlDocument document, string idValue)737 internal static XmlElement DefaultGetIdElement(XmlDocument document, string idValue) 738 { 739 if (document == null) 740 return null; 741 742 try 743 { 744 XmlConvert.VerifyNCName(idValue); 745 } 746 catch 747 { 748 // Identifiers are required to be an NCName 749 // (xml:id version 1.0, part 4, paragraph 2, bullet 1) 750 // 751 // If it isn't an NCName, it isn't allowed to match. 752 return null; 753 } 754 755 // Get the element with idValue 756 XmlElement elem = document.GetElementById(idValue); 757 758 if (elem != null) 759 { 760 // Have to check for duplicate ID values from the DTD. 761 762 XmlDocument docClone = (XmlDocument)document.CloneNode(true); 763 XmlElement cloneElem = docClone.GetElementById(idValue); 764 765 // If it's null here we want to know about it, because it means that 766 // GetElementById failed to work across the cloning, and our uniqueness 767 // test is invalid. 768 System.Diagnostics.Debug.Assert(cloneElem != null); 769 770 // Guard against null anyways 771 if (cloneElem != null) 772 { 773 cloneElem.Attributes.RemoveAll(); 774 775 XmlElement cloneElem2 = docClone.GetElementById(idValue); 776 777 if (cloneElem2 != null) 778 { 779 throw new CryptographicException( 780 SR.Cryptography_Xml_InvalidReference); 781 } 782 } 783 784 return elem; 785 } 786 787 elem = GetSingleReferenceTarget(document, "Id", idValue); 788 if (elem != null) 789 return elem; 790 elem = GetSingleReferenceTarget(document, "id", idValue); 791 if (elem != null) 792 return elem; 793 elem = GetSingleReferenceTarget(document, "ID", idValue); 794 795 return elem; 796 } 797 GetSingleReferenceTarget(XmlDocument document, string idAttributeName, string idValue)798 private static XmlElement GetSingleReferenceTarget(XmlDocument document, string idAttributeName, string idValue) 799 { 800 // idValue has already been tested as an NCName (unless overridden for compatibility), so there's no 801 // escaping that needs to be done here. 802 string xPath = "//*[@" + idAttributeName + "=\"" + idValue + "\"]"; 803 804 // http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel says that for the form URI="#chapter1": 805 // 806 // Identifies a node-set containing the element with ID attribute value 'chapter1' ... 807 // 808 // Note that it uses the singular. Therefore, if the match is ambiguous, we should consider the document invalid. 809 // 810 // In this case, we'll treat it the same as having found nothing across all fallbacks (but shortcut so that we don't 811 // fall into a trap of finding a secondary element which wasn't the originally signed one). 812 813 XmlNodeList nodeList = document.SelectNodes(xPath); 814 815 if (nodeList == null || nodeList.Count == 0) 816 { 817 return null; 818 } 819 820 if (nodeList.Count == 1) 821 { 822 return nodeList[0] as XmlElement; 823 } 824 825 throw new CryptographicException(SR.Cryptography_Xml_InvalidReference); 826 } 827 828 // According to book ".NET Framework Security" this method 829 // iterates all possible keys then return null GetPublicKey()830 protected virtual AsymmetricAlgorithm GetPublicKey () 831 { 832 if (m_signature.KeyInfo == null) 833 return null; 834 835 if (pkEnumerator == null) { 836 pkEnumerator = m_signature.KeyInfo.GetEnumerator (); 837 } 838 839 #if SECURITY_DEP 840 if (_x509Enumerator != null) { 841 if (_x509Enumerator.MoveNext ()) { 842 X509Certificate cert = (X509Certificate) _x509Enumerator.Current; 843 return new X509Certificate2 (cert.GetRawCertData ()).PublicKey.Key; 844 } else { 845 _x509Enumerator = null; 846 } 847 } 848 #endif 849 while (pkEnumerator.MoveNext ()) { 850 AsymmetricAlgorithm key = null; 851 KeyInfoClause kic = (KeyInfoClause) pkEnumerator.Current; 852 853 if (kic is DSAKeyValue) 854 key = DSA.Create (); 855 else if (kic is RSAKeyValue) 856 key = RSA.Create (); 857 858 if (key != null) { 859 key.FromXmlString (kic.GetXml ().InnerXml); 860 return key; 861 } 862 863 #if SECURITY_DEP 864 if (kic is KeyInfoX509Data) { 865 _x509Enumerator = ((KeyInfoX509Data) kic).Certificates.GetEnumerator (); 866 if (_x509Enumerator.MoveNext ()) { 867 X509Certificate cert = (X509Certificate) _x509Enumerator.Current; 868 return new X509Certificate2 (cert.GetRawCertData ()).PublicKey.Key; 869 } 870 } 871 #endif 872 } 873 return null; 874 } 875 GetXml()876 public XmlElement GetXml () 877 { 878 return m_signature.GetXml (envdoc); 879 } 880 LoadXml(XmlElement value)881 public void LoadXml (XmlElement value) 882 { 883 if (value == null) 884 throw new ArgumentNullException ("value"); 885 886 signatureElement = value; 887 m_signature.LoadXml (value); 888 889 if (_context == null) { 890 _context = value; 891 } 892 893 // Need to give the EncryptedXml object to the 894 // XmlDecryptionTransform to give it a fighting 895 // chance at decrypting the document. 896 foreach (Reference r in m_signature.SignedInfo.References) { 897 foreach (Transform t in r.TransformChain) { 898 if (t is XmlDecryptionTransform) 899 ((XmlDecryptionTransform) t).EncryptedXml = EncryptedXml; 900 } 901 } 902 } 903 } 904 } 905