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