2004-08-31 Tim Coleman <tim@timcoleman.com>
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / SignedXml.cs
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 // (C) 2004 Novell (http://www.novell.com)
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System.Collections;
36 using System.IO;
37 using System.Runtime.InteropServices;
38 using System.Security.Cryptography;
39 using System.Security.Policy;
40 using System.Net;
41 using System.Text;
42 using System.Xml;
43
44 #if NET_2_0
45 using System.Security.Cryptography.X509Certificates;
46 #endif
47
48 namespace System.Security.Cryptography.Xml {
49
50         public class SignedXml {
51
52                 public const string XmlDsigCanonicalizationUrl                  = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
53                 public const string XmlDsigCanonicalizationWithCommentsUrl      = XmlDsigCanonicalizationUrl + "#WithComments";
54                 public const string XmlDsigDSAUrl                               = XmlDsigNamespaceUrl + "dsa-sha1";
55                 public const string XmlDsigHMACSHA1Url                          = XmlDsigNamespaceUrl + "hmac-sha1";
56                 public const string XmlDsigMinimalCanonicalizationUrl           = XmlDsigNamespaceUrl + "minimal";
57                 public const string XmlDsigNamespaceUrl                         = "http://www.w3.org/2000/09/xmldsig#";
58                 public const string XmlDsigRSASHA1Url                           = XmlDsigNamespaceUrl + "rsa-sha1";
59                 public const string XmlDsigSHA1Url                              = XmlDsigNamespaceUrl + "sha1";
60
61 #if NET_2_0
62                 public const string XmlDecryptionTransformUrl                   = "http://www.w3.org/2002/07/decrypt#XML";
63                 public const string XmlDsigBase64TransformUrl                   = XmlDsigNamespaceUrl + "base64";
64                 public const string XmlDsigC14NTransformUrl                     = XmlDsigCanonicalizationUrl;
65                 public const string XmlDsigC14NWithCommentsTransformUrl         = XmlDsigCanonicalizationWithCommentsUrl;
66                 public const string XmlDsigEnvelopedSignatureTransformUrl       = XmlDsigNamespaceUrl + "enveloped-signature";
67                 public const string XmlDsigExcC14NTransformUrl                  = "http://www.w3.org/2001/10/xml-exc-c14n#";
68                 public const string XmlDsigExcC14NWithCommentsTransformUrl      = XmlDsigExcC14NTransformUrl + "WithComments";
69                 public const string XmlDsigXPathTransformUrl                    = "http://www.w3.org/TR/1999/REC-xpath-19991116";
70                 public const string XmlDsigXsltTransformUrl                     = "http://www.w3.org/TR/1999/REC-xslt-19991116";
71
72                 private EncryptedXml encryptedXml;
73 #endif
74
75                 protected Signature m_signature;
76                 private AsymmetricAlgorithm key;
77                 protected string m_strSigningKeyName;
78                 private XmlDocument envdoc;
79                 private IEnumerator pkEnumerator;
80                 private XmlElement signatureElement;
81                 private Hashtable hashes;
82                 // FIXME: enable it after CAS implementation
83 #if false //NET_1_1
84                 private XmlResolver xmlResolver = new XmlSecureResolver (new XmlUrlResolver (), new Evidence ());
85 #else
86                 private XmlResolver xmlResolver = new XmlUrlResolver ();
87 #endif
88                 private ArrayList manifests;
89                 
90                 private static readonly char [] whitespaceChars = new char [] {' ', '\r', '\n', '\t'};
91
92                 public SignedXml () 
93                 {
94                         m_signature = new Signature ();
95                         m_signature.SignedInfo = new SignedInfo ();
96                         hashes = new Hashtable (2); // 98% SHA1 for now
97                 }
98
99                 public SignedXml (XmlDocument document) : this ()
100                 {
101                         if (document == null)
102                                 throw new ArgumentNullException ("document");
103                         envdoc = document;
104                 }
105
106                 public SignedXml (XmlElement elem) : this ()
107                 {
108                         if (elem == null)
109                                 throw new ArgumentNullException ("elem");
110                         envdoc = new XmlDocument ();
111                         envdoc.LoadXml (elem.OuterXml);
112                 }
113
114 #if NET_2_0
115                 public EncryptedXml EncryptedXml {
116                         get { return encryptedXml; }
117                         set { encryptedXml = value; }
118                 }
119 #endif
120
121                 public KeyInfo KeyInfo {
122                         get { return m_signature.KeyInfo; }
123                         set { m_signature.KeyInfo = value; }
124                 }
125
126                 public Signature Signature {
127                         get { return m_signature; }
128                 }
129
130                 public string SignatureLength {
131                         get { return m_signature.SignedInfo.SignatureLength; }
132                 }
133
134                 public string SignatureMethod {
135                         get { return m_signature.SignedInfo.SignatureMethod; }
136                 }
137
138                 public byte[] SignatureValue {
139                         get { return m_signature.SignatureValue; }
140                 }
141
142                 public SignedInfo SignedInfo {
143                         get { return m_signature.SignedInfo; }
144                 }
145
146                 public AsymmetricAlgorithm SigningKey {
147                         get { return key; }
148                         set { key = value; }
149                 }
150
151                 // NOTE: CryptoAPI related ? documented as fx internal
152                 public string SigningKeyName {
153                         get { return m_strSigningKeyName; }
154                         set { m_strSigningKeyName = value; }
155                 }
156
157                 public void AddObject (DataObject dataObject) 
158                 {
159                         m_signature.AddObject (dataObject);
160                 }
161
162                 public void AddReference (Reference reference) 
163                 {
164                         m_signature.SignedInfo.AddReference (reference);
165                 }
166
167                 private Stream ApplyTransform (Transform t, XmlDocument input) 
168                 {
169                         // These transformer modify input document, which should
170                         // not affect to the input itself.
171                         if (t is XmlDsigXPathTransform ||
172                                 t is XmlDsigEnvelopedSignatureTransform)
173                                 input = (XmlDocument) input.Clone ();
174
175                         t.LoadInput (input);
176
177                         if (t is XmlDsigEnvelopedSignatureTransform)
178                                 // It returns XmlDocument for XmlDocument input.
179                                 return CanonicalizeOutput (t.GetOutput ());
180
181                         object obj = t.GetOutput ();
182                         if (obj is Stream)
183                                 return (Stream) obj;
184                         else if (obj is XmlDocument) {
185                                 MemoryStream ms = new MemoryStream ();
186                                 XmlTextWriter xtw = new XmlTextWriter (ms, Encoding.UTF8);
187                                 ((XmlDocument) obj).WriteTo (xtw);
188                                 return ms;
189                         }
190                         else if (obj == null) {
191                                 throw new NotImplementedException ("This should not occur. Transform is " + t + ".");
192                         }
193                         else {
194                                 // e.g. XmlDsigXPathTransform returns XmlNodeList
195                                 return CanonicalizeOutput (obj);
196                         }
197                 }
198
199                 private Stream CanonicalizeOutput (object obj)
200                 {
201                         Transform c14n = GetC14NMethod ();
202                         c14n.LoadInput (obj);
203                         return (Stream) c14n.GetOutput ();
204                 }
205
206                 private XmlDocument GetManifest (Reference r) 
207                 {
208                         XmlDocument doc = new XmlDocument ();
209                         doc.PreserveWhitespace = true;
210
211                         if (r.Uri [0] == '#') {
212                                 // local manifest
213                                 if (signatureElement != null) {
214                                         XmlElement xel = GetIdElement (signatureElement.OwnerDocument, r.Uri.Substring (1));
215                                         if (xel == null)
216                                                 throw new CryptographicException ("Manifest targeted by Reference was not found: " + r.Uri.Substring (1));
217                                         doc.LoadXml (xel.OuterXml);
218                                         FixupNamespaceNodes (xel, doc.DocumentElement);
219                                 }
220                         }
221                         else if (xmlResolver != null) {
222                                 // TODO: need testing
223                                 Stream s = (Stream) xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream));
224                                 doc.Load (s);
225                         }
226
227                         if (doc.FirstChild != null) {
228                                 // keep a copy of the manifests to check their references later
229                                 if (manifests == null)
230                                         manifests = new ArrayList ();
231                                 manifests.Add (doc);
232
233                                 return doc;
234                         }
235                         return null;
236                 }
237
238                 private void FixupNamespaceNodes (XmlElement src, XmlElement dst)
239                 {
240                         // add namespace nodes
241                         foreach (XmlAttribute attr in src.SelectNodes ("namespace::*")) {
242                                 if (attr.LocalName == "xml")
243                                         continue;
244                                 if (attr.OwnerElement == src)
245                                         continue;
246                                 dst.SetAttributeNode (dst.OwnerDocument.ImportNode (attr, true) as XmlAttribute);
247                         }
248                 }
249
250                 [MonoTODO ("Need testing")]
251                 private byte[] GetReferenceHash (Reference r) 
252                 {
253                         Stream s = null;
254                         XmlDocument doc = null;
255                         if (r.Uri == String.Empty) {
256                                 doc = envdoc;
257                         }
258                         else if (r.Type == XmlSignature.Uri.Manifest) {
259                                 doc = GetManifest (r);
260                         }
261                         else {
262                                 doc = new XmlDocument ();
263                                 doc.PreserveWhitespace = true;
264                                 string objectName = null;
265
266                                 if (r.Uri.StartsWith ("#xpointer")) {
267                                         string uri = string.Join ("", r.Uri.Substring (9).Split (whitespaceChars));
268                                         if (uri.Length < 2 || uri [0] != '(' || uri [uri.Length - 1] != ')')
269                                                 // FIXME: how to handle invalid xpointer?
270                                                 uri = String.Empty;
271                                         else
272                                                 uri = uri.Substring (1, uri.Length - 2);
273                                         if (uri == "/")
274                                                 doc = envdoc;
275                                         else if (uri.Length > 6 && uri.StartsWith ("id(") && uri [uri.Length - 1] == ')')
276                                                 // id('foo'), id("foo")
277                                                 objectName = uri.Substring (4, uri.Length - 6);
278                                 }
279                                 else if (r.Uri [0] == '#') {
280                                         objectName = r.Uri.Substring (1);
281                                 }
282                                 else if (xmlResolver != null) {
283                                         // TODO: test but doc says that Resolver = null -> no access
284                                         try {
285                                                 // no way to know if valid without throwing an exception
286                                                 Uri uri = new Uri (r.Uri);
287                                                 s = (Stream) xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream));
288                                         }
289                                         catch {
290                                                 // may still be a local file (and maybe not xml)
291                                                 s = File.OpenRead (r.Uri);
292                                         }
293                                 }
294                                 if (objectName != null) {
295                                         foreach (DataObject obj in m_signature.ObjectList) {
296                                                 if (obj.Id == objectName) {
297                                                         XmlElement xel = obj.GetXml ();
298                                                         doc.LoadXml (xel.OuterXml);
299                                                         FixupNamespaceNodes (xel, doc.DocumentElement);
300                                                         break;
301                                                 }
302                                         }
303                                 }
304                         }
305
306                         if (r.TransformChain.Count > 0) {               
307                                 foreach (Transform t in r.TransformChain) {
308                                         if (s == null) {
309                                                 s = ApplyTransform (t, doc);
310                                         }
311                                         else {
312                                                 t.LoadInput (s);
313                                                 object o = t.GetOutput ();
314                                                 if (o is Stream)
315                                                         s = (Stream) o;
316                                                 else
317                                                         s = CanonicalizeOutput (o);
318                                         }
319                                 }
320                         }
321                         else if (s == null) {
322                                 // we must not C14N references from outside the document
323                                 // e.g. non-xml documents
324                                 if (r.Uri [0] != '#') {
325                                         s = new MemoryStream ();
326                                         doc.Save (s);
327                                 }
328                                 else {
329                                         // apply default C14N transformation
330                                         s = ApplyTransform (new XmlDsigC14NTransform (), doc);
331                                 }
332                         }
333                         HashAlgorithm digest = GetHash (r.DigestMethod);
334                         return digest.ComputeHash (s);
335                 }
336
337                 private void DigestReferences () 
338                 {
339                         // we must tell each reference which hash algorithm to use 
340                         // before asking for the SignedInfo XML !
341                         foreach (Reference r in m_signature.SignedInfo.References) {
342                                 // assume SHA-1 if nothing is specified
343                                 if (r.DigestMethod == null)
344                                         r.DigestMethod = XmlDsigSHA1Url;
345                                 r.DigestValue = GetReferenceHash (r);
346                         }
347                 }
348
349                 private Transform GetC14NMethod ()
350                 {
351                         Transform t = (Transform) CryptoConfig.CreateFromName (m_signature.SignedInfo.CanonicalizationMethod);
352                         if (t == null)
353                                 throw new CryptographicException ("Unknown Canonicalization Method {0}", m_signature.SignedInfo.CanonicalizationMethod);
354                         return t;
355                 }
356
357                 private Stream SignedInfoTransformed () 
358                 {
359                         Transform t = GetC14NMethod ();
360
361                         if (signatureElement == null) {
362                                 // when creating signatures
363                                 XmlDocument doc = new XmlDocument ();
364                                 doc.PreserveWhitespace = true;
365                                 doc.LoadXml (m_signature.SignedInfo.GetXml ().OuterXml);
366                                 if (envdoc != null)
367                                 foreach (XmlAttribute attr in envdoc.DocumentElement.SelectNodes ("namespace::*")) {
368                                         if (attr.LocalName == "xml")
369                                                 continue;
370                                         if (attr.Prefix == doc.DocumentElement.Prefix)
371                                                 continue;
372                                         doc.DocumentElement.SetAttributeNode (doc.ImportNode (attr, true) as XmlAttribute);
373                                 }
374                                 t.LoadInput (doc);
375                         }
376                         else {
377                                 // when verifying signatures
378                                 // TODO - check m_signature.SignedInfo.Id
379                                 XmlElement el = signatureElement.GetElementsByTagName (XmlSignature.ElementNames.SignedInfo, XmlSignature.NamespaceURI) [0] as XmlElement;
380                                 StringWriter sw = new StringWriter ();
381                                 XmlTextWriter xtw = new XmlTextWriter (sw);
382                                 xtw.WriteStartElement (el.Prefix, el.LocalName, el.NamespaceURI);
383
384                                 // context namespace nodes (except for "xmlns:xml")
385                                 XmlNodeList nl = el.SelectNodes ("namespace::*");
386                                 foreach (XmlAttribute attr in nl) {
387                                         if (attr.ParentNode == el)
388                                                 continue;
389                                         if (attr.LocalName == "xml")
390                                                 continue;
391                                         if (attr.Prefix == el.Prefix)
392                                                 continue;
393                                         attr.WriteTo (xtw);
394                                 }
395                                 foreach (XmlNode attr in el.Attributes)
396                                         attr.WriteTo (xtw);
397                                 foreach (XmlNode n in el.ChildNodes)
398                                         n.WriteTo (xtw);
399
400                                 xtw.WriteEndElement ();
401                                 byte [] si = Encoding.UTF8.GetBytes (sw.ToString ());
402
403                                 MemoryStream ms = new MemoryStream ();
404                                 ms.Write (si, 0, si.Length);
405                                 ms.Position = 0;
406
407                                 t.LoadInput (ms);
408                         }
409                         // C14N and C14NWithComments always return a Stream in GetOutput
410                         return (Stream) t.GetOutput ();
411                 }
412
413                 // reuse hash - most document will always use the same hash
414                 private HashAlgorithm GetHash (string algorithm) 
415                 {
416                         HashAlgorithm hash = (HashAlgorithm) hashes [algorithm];
417                         if (hash == null) {
418                                 hash = HashAlgorithm.Create (algorithm);
419                                 if (hash == null)
420                                         throw new CryptographicException ("Unknown hash algorithm: {0}", algorithm);
421                                 hashes.Add (algorithm, hash);
422                                 // now ready to be used
423                         }
424                         else {
425                                 // important before reusing an hash object
426                                 hash.Initialize ();
427                         }
428                         return hash;
429                 }
430
431                 public bool CheckSignature () 
432                 {
433                         return (CheckSignatureInternal (null) != null);
434                 }
435
436                 private bool CheckReferenceIntegrity (ArrayList referenceList) 
437                 {
438                         if (referenceList == null)
439                                 return false;
440
441                         // check digest (hash) for every reference
442                         foreach (Reference r in referenceList) {
443                                 // stop at first broken reference
444                                 byte[] hash = GetReferenceHash (r);
445                                 if (! Compare (r.DigestValue, hash))
446                                         return false;
447                         }
448                         return true;
449                 }
450
451                 public bool CheckSignature (AsymmetricAlgorithm key) 
452                 {
453                         if (key == null)
454                                 throw new ArgumentNullException ("key");
455                         return (CheckSignatureInternal (key) != null);
456                 }
457
458                 private AsymmetricAlgorithm CheckSignatureInternal (AsymmetricAlgorithm key)
459                 {
460                         pkEnumerator = null;
461
462                         if (key != null) {
463                                 // check with supplied key
464                                 if (!CheckSignatureWithKey (key))
465                                         return null;
466                         }
467                         else {
468                                 if (Signature.KeyInfo == null)
469                                         throw new CryptographicException ("At least one KeyInfo is required.");
470                                 // no supplied key, iterates all KeyInfo
471                                 while ((key = GetPublicKey ()) != null) {
472                                         if (CheckSignatureWithKey (key)) {
473                                                 break;
474                                         }
475                                 }
476                                 pkEnumerator = null;
477                                 if (key == null)
478                                         return null;
479                         }
480
481                         // some parts may need to be downloaded
482                         // so where doing it last
483                         if (!CheckReferenceIntegrity (m_signature.SignedInfo.References))
484                                 return null;
485
486                         if (manifests != null) {
487                                 // do not use foreach as a manifest could contain manifests...
488                                 for (int i=0; i < manifests.Count; i++) {
489                                         Manifest manifest = new Manifest ((manifests [i] as XmlDocument).DocumentElement);
490                                         if (! CheckReferenceIntegrity (manifest.References))
491                                                 return null;
492                                 }
493                         }
494                         return key;
495                 }
496
497                 // Is the signature (over SignedInfo) valid ?
498                 private bool CheckSignatureWithKey (AsymmetricAlgorithm key) 
499                 {
500                         if (key == null)
501                                 return false;
502
503                         SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (m_signature.SignedInfo.SignatureMethod);
504                         if (sd == null)
505                                 return false;
506
507                         AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm);
508                         if (verifier == null)
509                                 return false;
510
511                         try {
512                                 verifier.SetKey (key);
513                                 verifier.SetHashAlgorithm (sd.DigestAlgorithm);
514
515                                 HashAlgorithm hash = GetHash (sd.DigestAlgorithm);
516                                 // get the hash of the C14N SignedInfo element
517                                 MemoryStream ms = (MemoryStream) SignedInfoTransformed ();
518
519                                 byte[] digest = hash.ComputeHash (ms);
520                                 return verifier.VerifySignature (digest, m_signature.SignatureValue);
521                         }
522                         catch {
523                                 // e.g. SignatureMethod != AsymmetricAlgorithm type
524                                 return false;
525                         } 
526                 }
527
528                 private bool Compare (byte[] expected, byte[] actual) 
529                 {
530                         bool result = ((expected != null) && (actual != null));
531                         if (result) {
532                                 int l = expected.Length;
533                                 result = (l == actual.Length);
534                                 if (result) {
535                                         for (int i=0; i < l; i++) {
536                                                 if (expected[i] != actual[i])
537                                                         return false;
538                                         }
539                                 }
540                         }
541                         return result;
542                 }
543
544                 public bool CheckSignature (KeyedHashAlgorithm macAlg) 
545                 {
546                         if (macAlg == null)
547                                 throw new ArgumentNullException ("macAlg");
548
549                         pkEnumerator = null;
550
551                         // Is the signature (over SignedInfo) valid ?
552                         Stream s = SignedInfoTransformed ();
553                         if (s == null)
554                                 return false;
555
556                         byte[] actual = macAlg.ComputeHash (s);
557                         // HMAC signature may be partial
558                         if (m_signature.SignedInfo.SignatureLength != null) {
559                                 int length = actual.Length;
560                                 try {
561                                         // SignatureLength is in bits
562                                         length = (Int32.Parse (m_signature.SignedInfo.SignatureLength) >> 3);
563                                 }
564                                 catch {
565                                 }
566
567                                 if (length != actual.Length) {
568                                         byte[] trunked = new byte [length];
569                                         Buffer.BlockCopy (actual, 0, trunked, 0, length);
570                                         actual = trunked;
571                                 }
572                         }
573
574                         if (Compare (m_signature.SignatureValue, actual)) {
575                                 // some parts may need to be downloaded
576                                 // so where doing it last
577                                 return CheckReferenceIntegrity (m_signature.SignedInfo.References);
578                         }
579                         return false;
580                 }
581
582 #if NET_2_0
583                 [MonoTODO]
584                 public bool CheckSignature (X509CertificateEx certificate, bool verifySignatureOnly)
585                 {
586                         throw new NotImplementedException ();
587                 }
588 #endif
589
590                 public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey) 
591                 {
592                         signingKey = CheckSignatureInternal (null);
593                         return (signingKey != null);
594                 }
595
596                 public void ComputeSignature () 
597                 {
598                         if (key != null) {
599                                 // required before hashing
600                                 m_signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
601                                 DigestReferences ();
602
603                                 AsymmetricSignatureFormatter signer = null;
604                                 // in need for a CryptoConfig factory
605                                 if (key is DSA)
606                                         signer = new DSASignatureFormatter (key);
607                                 else if (key is RSA) 
608                                         signer = new RSAPKCS1SignatureFormatter (key);
609
610                                 if (signer != null) {
611                                         SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (m_signature.SignedInfo.SignatureMethod);
612
613                                         HashAlgorithm hash = GetHash (sd.DigestAlgorithm);
614                                         // get the hash of the C14N SignedInfo element
615                                         byte[] digest = hash.ComputeHash (SignedInfoTransformed ());
616
617                                         signer.SetHashAlgorithm ("SHA1");
618                                         m_signature.SignatureValue = signer.CreateSignature (digest);
619                                 }
620                         }
621                 }
622
623                 public void ComputeSignature (KeyedHashAlgorithm macAlg) 
624                 {
625                         if (macAlg == null)
626                                 throw new ArgumentNullException ("macAlg");
627
628                         if (macAlg is HMACSHA1) {
629                                 DigestReferences ();
630
631                                 m_signature.SignedInfo.SignatureMethod = XmlDsigHMACSHA1Url;
632                                 m_signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ());
633                         }
634                         else 
635                                 throw new CryptographicException ("unsupported algorithm");
636                 }
637
638                 public virtual XmlElement GetIdElement (XmlDocument document, string idValue) 
639                 {
640                         // this works only if there's a DTD or XSD available to define the ID
641                         XmlElement xel = document.GetElementById (idValue);
642                         if (xel == null) {
643                                 // search an "undefined" ID
644                                 xel = (XmlElement) document.SelectSingleNode ("//*[@Id='" + idValue + "']");
645                         }
646                         return xel;
647                 }
648
649                 // According to book ".NET Framework Security" this method
650                 // iterates all possible keys then return null
651                 protected virtual AsymmetricAlgorithm GetPublicKey () 
652                 {
653                         if (m_signature.KeyInfo == null)
654                                 return null;
655
656                         if (pkEnumerator == null) {
657                                 pkEnumerator = m_signature.KeyInfo.GetEnumerator ();
658                         }
659
660                         if (pkEnumerator.MoveNext ()) {
661                                 AsymmetricAlgorithm key = null;
662                                 KeyInfoClause kic = (KeyInfoClause) pkEnumerator.Current;
663
664                                 if (kic is DSAKeyValue)
665                                         key = DSA.Create ();
666                                 else if (kic is RSAKeyValue) 
667                                         key = RSA.Create ();
668
669                                 if (key != null) {
670                                         key.FromXmlString (kic.GetXml ().InnerXml);
671                                         return key;
672                                 }
673                         }
674                         return null;
675                 }
676
677                 public XmlElement GetXml () 
678                 {
679                         return m_signature.GetXml ();
680                 }
681
682                 public void LoadXml (XmlElement value) 
683                 {
684                         if (value == null)
685                                 throw new ArgumentNullException ("value");
686
687                         signatureElement = value;
688                         m_signature.LoadXml (value);
689                 }
690
691 #if NET_1_1
692                 [ComVisible (false)]
693                 public XmlResolver Resolver {
694                         set { xmlResolver = value; }
695                 }
696 #endif
697         }
698 }