2005-04-23 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / SignedXml.cs
index bcbc3d7e7b00f18607c1f945af6b89130af64f74..3ce51a5ab8a2681069ab395f0214653efb0c5ddb 100644 (file)
 // SignedXml.cs - SignedXml implementation for XML Signature
 //
 // Author:
-//     Sebastien Pouliot (spouliot@motus.com)
+//     Sebastien Pouliot  <sebastien@ximian.com>
+//     Atsushi Enomoto <atsushi@ximian.com>
+//      Tim Coleman <tim@timcoleman.com>
 //
 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) Tim Coleman, 2004
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
 using System.Collections;
 using System.IO;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography;
+using System.Security.Policy;
+using System.Net;
+using System.Text;
 using System.Xml;
 
+#if NET_2_0
+using System.Security.Cryptography.X509Certificates;
+#endif
+
 namespace System.Security.Cryptography.Xml {
 
        public class SignedXml {
 
-               private Signature signature;
+               public const string XmlDsigCanonicalizationUrl                  = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
+               public const string XmlDsigCanonicalizationWithCommentsUrl      = XmlDsigCanonicalizationUrl + "#WithComments";
+               public const string XmlDsigDSAUrl                               = XmlDsigNamespaceUrl + "dsa-sha1";
+               public const string XmlDsigHMACSHA1Url                          = XmlDsigNamespaceUrl + "hmac-sha1";
+               public const string XmlDsigMinimalCanonicalizationUrl           = XmlDsigNamespaceUrl + "minimal";
+               public const string XmlDsigNamespaceUrl                         = "http://www.w3.org/2000/09/xmldsig#";
+               public const string XmlDsigRSASHA1Url                           = XmlDsigNamespaceUrl + "rsa-sha1";
+               public const string XmlDsigSHA1Url                              = XmlDsigNamespaceUrl + "sha1";
+
+#if NET_2_0
+               public const string XmlDecryptionTransformUrl                   = "http://www.w3.org/2002/07/decrypt#XML";
+               public const string XmlDsigBase64TransformUrl                   = XmlDsigNamespaceUrl + "base64";
+               public const string XmlDsigC14NTransformUrl                     = XmlDsigCanonicalizationUrl;
+               public const string XmlDsigC14NWithCommentsTransformUrl         = XmlDsigCanonicalizationWithCommentsUrl;
+               public const string XmlDsigEnvelopedSignatureTransformUrl       = XmlDsigNamespaceUrl + "enveloped-signature";
+               public const string XmlDsigExcC14NTransformUrl                  = "http://www.w3.org/2001/10/xml-exc-c14n#";
+               public const string XmlDsigExcC14NWithCommentsTransformUrl      = XmlDsigExcC14NTransformUrl + "WithComments";
+               public const string XmlDsigXPathTransformUrl                    = "http://www.w3.org/TR/1999/REC-xpath-19991116";
+               public const string XmlDsigXsltTransformUrl                     = "http://www.w3.org/TR/1999/REC-xslt-19991116";
+
+               private EncryptedXml encryptedXml;
+#endif
+
+               protected Signature m_signature;
                private AsymmetricAlgorithm key;
-               private string keyName;
+               protected string m_strSigningKeyName;
                private XmlDocument envdoc;
+               private IEnumerator pkEnumerator;
+               private XmlElement signatureElement;
+               private Hashtable hashes;
+               // FIXME: enable it after CAS implementation
+#if false //NET_1_1
+               private XmlResolver xmlResolver = new XmlSecureResolver (new XmlUrlResolver (), new Evidence ());
+#else
+               private XmlResolver xmlResolver = new XmlUrlResolver ();
+#endif
+               private ArrayList manifests;
+               
+               private static readonly char [] whitespaceChars = new char [] {' ', '\r', '\n', '\t'};
 
                public SignedXml () 
                {
-                       signature = new Signature ();
-                       signature.SignedInfo = new SignedInfo ();
+                       m_signature = new Signature ();
+                       m_signature.SignedInfo = new SignedInfo ();
+                       hashes = new Hashtable (2); // 98% SHA1 for now
                }
 
-               public SignedXml (XmlDocument document)
+               public SignedXml (XmlDocument document) : this ()
                {
-                       signature = new Signature ();
-                       signature.SignedInfo = new SignedInfo ();
+                       if (document == null)
+                               throw new ArgumentNullException ("document");
                        envdoc = document;
                }
 
@@ -39,42 +105,40 @@ namespace System.Security.Cryptography.Xml {
                {
                        if (elem == null)
                                throw new ArgumentNullException ("elem");
-                       signature = new Signature ();
-                       signature.SignedInfo = new SignedInfo ();
+                       envdoc = new XmlDocument ();
+                       envdoc.LoadXml (elem.OuterXml);
                }
 
-               public const string XmlDsigCanonicalizationUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
-               public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigCanonicalizationUrl + "#WithComments";
-               public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#";
-               public const string XmlDsigDSAUrl = XmlDsigNamespaceUrl + "dsa-sha1";
-               public const string XmlDsigHMACSHA1Url = XmlDsigNamespaceUrl + "hmac-sha1";
-               public const string XmlDsigMinimalCanonicalizationUrl = XmlDsigNamespaceUrl + "minimal";
-               public const string XmlDsigRSASHA1Url = XmlDsigNamespaceUrl + "rsa-sha1";
-               public const string XmlDsigSHA1Url = XmlDsigNamespaceUrl + "sha1";
+#if NET_2_0
+               public EncryptedXml EncryptedXml {
+                       get { return encryptedXml; }
+                       set { encryptedXml = value; }
+               }
+#endif
 
                public KeyInfo KeyInfo {
-                       get { return signature.KeyInfo; }
-                       set { signature.KeyInfo = value; }
+                       get { return m_signature.KeyInfo; }
+                       set { m_signature.KeyInfo = value; }
                }
 
                public Signature Signature {
-                       get { return signature; }
+                       get { return m_signature; }
                }
 
                public string SignatureLength {
-                       get { return signature.SignedInfo.SignatureLength; }
+                       get { return m_signature.SignedInfo.SignatureLength; }
                }
 
                public string SignatureMethod {
-                       get { return signature.SignedInfo.SignatureMethod; }
+                       get { return m_signature.SignedInfo.SignatureMethod; }
                }
 
                public byte[] SignatureValue {
-                       get { return signature.SignatureValue; }
+                       get { return m_signature.SignatureValue; }
                }
 
                public SignedInfo SignedInfo {
-                       get { return signature.SignedInfo; }
+                       get { return m_signature.SignedInfo; }
                }
 
                public AsymmetricAlgorithm SigningKey {
@@ -82,123 +146,310 @@ namespace System.Security.Cryptography.Xml {
                        set { key = value; }
                }
 
+               // NOTE: CryptoAPI related ? documented as fx internal
                public string SigningKeyName {
-                       get { return keyName; }
-                       set { keyName = value; }
+                       get { return m_strSigningKeyName; }
+                       set { m_strSigningKeyName = value; }
                }
 
                public void AddObject (DataObject dataObject) 
                {
-                       signature.AddObject (dataObject);
+                       m_signature.AddObject (dataObject);
                }
 
                public void AddReference (Reference reference) 
                {
-                       signature.SignedInfo.AddReference (reference);
+                       m_signature.SignedInfo.AddReference (reference);
                }
 
-               private Stream ApplyTransform (Transform t, XmlDocument doc
+               private Stream ApplyTransform (Transform t, XmlDocument input
                {
-                       t.LoadInput (doc);
-                       if (t is XmlDsigEnvelopedSignatureTransform) {
-                               XmlDocument d = (XmlDocument) t.GetOutput ();
+                       // These transformer modify input document, which should
+                       // not affect to the input itself.
+                       if (t is XmlDsigXPathTransform 
+                               || t is XmlDsigEnvelopedSignatureTransform
+#if NET_2_0
+                               || t is XmlDecryptionTransform
+#endif
+                       )
+                               input = (XmlDocument) input.Clone ();
+
+                       t.LoadInput (input);
+
+                       if (t is XmlDsigEnvelopedSignatureTransform)
+                               // It returns XmlDocument for XmlDocument input.
+                               return CanonicalizeOutput (t.GetOutput ());
+
+                       object obj = t.GetOutput ();
+                       if (obj is Stream)
+                               return (Stream) obj;
+                       else if (obj is XmlDocument) {
                                MemoryStream ms = new MemoryStream ();
-                               d.Save (ms);
+                               XmlTextWriter xtw = new XmlTextWriter (ms, Encoding.UTF8);
+                               ((XmlDocument) obj).WriteTo (xtw);
+
+                               xtw.Flush ();
+
+                               // Rewind to the start of the stream
+                               ms.Position = 0;
                                return ms;
                        }
-                       else
-                               return (Stream) t.GetOutput ();
+                       else if (obj == null) {
+                               throw new NotImplementedException ("This should not occur. Transform is " + t + ".");
+                       }
+                       else {
+                               // e.g. XmlDsigXPathTransform returns XmlNodeList
+                               return CanonicalizeOutput (obj);
+                       }
                }
 
-               private Stream ApplyTransform (Transform t, Stream s) 
+               private Stream CanonicalizeOutput (object obj)
                {
-                       try {
-                               t.LoadInput (s);
-                               s = (Stream) t.GetOutput ();
+                       Transform c14n = GetC14NMethod ();
+                       c14n.LoadInput (obj);
+                       return (Stream) c14n.GetOutput ();
+               }
+
+               private XmlDocument GetManifest (Reference r) 
+               {
+                       XmlDocument doc = new XmlDocument ();
+                       doc.PreserveWhitespace = true;
+
+                       if (r.Uri [0] == '#') {
+                               // local manifest
+                               if (signatureElement != null) {
+                                       XmlElement xel = GetIdElement (signatureElement.OwnerDocument, r.Uri.Substring (1));
+                                       if (xel == null)
+                                               throw new CryptographicException ("Manifest targeted by Reference was not found: " + r.Uri.Substring (1));
+                                       doc.LoadXml (xel.OuterXml);
+                                       FixupNamespaceNodes (xel, doc.DocumentElement);
+                               }
+                       }
+                       else if (xmlResolver != null) {
+                               // TODO: need testing
+                               Stream s = (Stream) xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream));
+                               doc.Load (s);
+                       }
+
+                       if (doc.FirstChild != null) {
+                               // keep a copy of the manifests to check their references later
+                               if (manifests == null)
+                                       manifests = new ArrayList ();
+                               manifests.Add (doc);
+
+                               return doc;
                        }
-                       catch (Exception e) {
-                               string temp = e.ToString (); // stop debugger
+                       return null;
+               }
+
+               private void FixupNamespaceNodes (XmlElement src, XmlElement dst)
+               {
+                       // add namespace nodes
+                       foreach (XmlAttribute attr in src.SelectNodes ("namespace::*")) {
+                               if (attr.LocalName == "xml")
+                                       continue;
+                               if (attr.OwnerElement == src)
+                                       continue;
+                               dst.SetAttributeNode (dst.OwnerDocument.ImportNode (attr, true) as XmlAttribute);
                        }
-                       return s;
                }
 
-               [MonoTODO("incomplete")]
+               [MonoTODO ("Need testing")]
                private byte[] GetReferenceHash (Reference r) 
                {
-                       XmlDocument doc = new XmlDocument ();
-                       doc.PreserveWhitespace = true;
-                       if (r.Uri == "")
+                       Stream s = null;
+                       XmlDocument doc = null;
+                       if (r.Uri == String.Empty) {
                                doc = envdoc;
+                       }
+                       else if (r.Type == XmlSignature.Uri.Manifest) {
+                               doc = GetManifest (r);
+                       }
                        else {
-                               foreach (DataObject obj in signature.ObjectList) {
-                                       if ("#" + obj.Id == r.Uri) {
-                                               doc.LoadXml (obj.GetXml ().OuterXml);
-                                               break;
+                               doc = new XmlDocument ();
+                               doc.PreserveWhitespace = true;
+                               string objectName = null;
+
+                               if (r.Uri.StartsWith ("#xpointer")) {
+                                       string uri = string.Join ("", r.Uri.Substring (9).Split (whitespaceChars));
+                                       if (uri.Length < 2 || uri [0] != '(' || uri [uri.Length - 1] != ')')
+                                               // FIXME: how to handle invalid xpointer?
+                                               uri = String.Empty;
+                                       else
+                                               uri = uri.Substring (1, uri.Length - 2);
+                                       if (uri == "/")
+                                               doc = envdoc;
+                                       else if (uri.Length > 6 && uri.StartsWith ("id(") && uri [uri.Length - 1] == ')')
+                                               // id('foo'), id("foo")
+                                               objectName = uri.Substring (4, uri.Length - 6);
+                               }
+                               else if (r.Uri [0] == '#') {
+                                       objectName = r.Uri.Substring (1);
+                               }
+                               else if (xmlResolver != null) {
+                                       // TODO: test but doc says that Resolver = null -> no access
+                                       try {
+                                               // no way to know if valid without throwing an exception
+                                               Uri uri = new Uri (r.Uri);
+                                               s = (Stream) xmlResolver.GetEntity (uri, null, typeof (Stream));
+                                       }
+                                       catch {
+                                               // may still be a local file (and maybe not xml)
+                                               s = File.OpenRead (r.Uri);
+                                       }
+                               }
+                               if (objectName != null) {
+                                       foreach (DataObject obj in m_signature.ObjectList) {
+                                               if (obj.Id == objectName) {
+                                                       XmlElement xel = obj.GetXml ();
+                                                       doc.LoadXml (xel.OuterXml);
+                                                       FixupNamespaceNodes (xel, doc.DocumentElement);
+                                                       break;
+                                               }
                                        }
                                }
                        }
 
-                       Stream s = null;
                        if (r.TransformChain.Count > 0) {               
                                foreach (Transform t in r.TransformChain) {
-                                       if (s == null)
+                                       if (s == null) {
                                                s = ApplyTransform (t, doc);
-                                       else
-                                               s = ApplyTransform (t, s);
+                                       }
+                                       else {
+                                               t.LoadInput (s);
+                                               object o = t.GetOutput ();
+                                               if (o is Stream)
+                                                       s = (Stream) o;
+                                               else
+                                                       s = CanonicalizeOutput (o);
+                                       }
                                }
                        }
-                       else
-                               s = ApplyTransform (new XmlDsigC14NTransform (), doc);
-
-                       // TODO: We should reuse the same hash object (when possible)
-                       HashAlgorithm hash = (HashAlgorithm) CryptoConfig.CreateFromName (r.DigestMethod);
-                       return hash.ComputeHash (s);
+                       else if (s == null) {
+                               // we must not C14N references from outside the document
+                               // e.g. non-xml documents
+                               if (r.Uri [0] != '#') {
+                                       s = new MemoryStream ();
+                                       doc.Save (s);
+                               }
+                               else {
+                                       // apply default C14N transformation
+                                       s = ApplyTransform (new XmlDsigC14NTransform (), doc);
+                               }
+                       }
+                       HashAlgorithm digest = GetHash (r.DigestMethod);
+                       return digest.ComputeHash (s);
                }
 
                private void DigestReferences () 
                {
                        // we must tell each reference which hash algorithm to use 
                        // before asking for the SignedInfo XML !
-                       foreach (Reference r in signature.SignedInfo.References) {
+                       foreach (Reference r in m_signature.SignedInfo.References) {
                                // assume SHA-1 if nothing is specified
                                if (r.DigestMethod == null)
                                        r.DigestMethod = XmlDsigSHA1Url;
                                r.DigestValue = GetReferenceHash (r);
                        }
                }
-               
-               private Stream SignedInfoTransformed () 
+
+               private Transform GetC14NMethod ()
                {
-                       Transform t = (Transform) CryptoConfig.CreateFromName (signature.SignedInfo.CanonicalizationMethod);
+                       Transform t = (Transform) CryptoConfig.CreateFromName (m_signature.SignedInfo.CanonicalizationMethod);
                        if (t == null)
-                               return null;
+                               throw new CryptographicException ("Unknown Canonicalization Method {0}", m_signature.SignedInfo.CanonicalizationMethod);
+                       return t;
+               }
 
-                       XmlDocument doc = new XmlDocument ();
-                       doc.LoadXml (signature.SignedInfo.GetXml ().OuterXml);
-                       return ApplyTransform (t, doc); 
+               private Stream SignedInfoTransformed () 
+               {
+                       Transform t = GetC14NMethod ();
+
+                       if (signatureElement == null) {
+                               // when creating signatures
+                               XmlDocument doc = new XmlDocument ();
+                               doc.PreserveWhitespace = true;
+                               doc.LoadXml (m_signature.SignedInfo.GetXml ().OuterXml);
+                               if (envdoc != null)
+                               foreach (XmlAttribute attr in envdoc.DocumentElement.SelectNodes ("namespace::*")) {
+                                       if (attr.LocalName == "xml")
+                                               continue;
+                                       if (attr.Prefix == doc.DocumentElement.Prefix)
+                                               continue;
+                                       doc.DocumentElement.SetAttributeNode (doc.ImportNode (attr, true) as XmlAttribute);
+                               }
+                               t.LoadInput (doc);
+                       }
+                       else {
+                               // when verifying signatures
+                               // TODO - check m_signature.SignedInfo.Id
+                               XmlElement el = signatureElement.GetElementsByTagName (XmlSignature.ElementNames.SignedInfo, XmlSignature.NamespaceURI) [0] as XmlElement;
+                               StringWriter sw = new StringWriter ();
+                               XmlTextWriter xtw = new XmlTextWriter (sw);
+                               xtw.WriteStartElement (el.Prefix, el.LocalName, el.NamespaceURI);
+
+                               // context namespace nodes (except for "xmlns:xml")
+                               XmlNodeList nl = el.SelectNodes ("namespace::*");
+                               foreach (XmlAttribute attr in nl) {
+                                       if (attr.ParentNode == el)
+                                               continue;
+                                       if (attr.LocalName == "xml")
+                                               continue;
+                                       if (attr.Prefix == el.Prefix)
+                                               continue;
+                                       attr.WriteTo (xtw);
+                               }
+                               foreach (XmlNode attr in el.Attributes)
+                                       attr.WriteTo (xtw);
+                               foreach (XmlNode n in el.ChildNodes)
+                                       n.WriteTo (xtw);
+
+                               xtw.WriteEndElement ();
+                               byte [] si = Encoding.UTF8.GetBytes (sw.ToString ());
+
+                               MemoryStream ms = new MemoryStream ();
+                               ms.Write (si, 0, si.Length);
+                               ms.Position = 0;
+
+                               t.LoadInput (ms);
+                       }
+                       // C14N and C14NWithComments always return a Stream in GetOutput
+                       return (Stream) t.GetOutput ();
                }
 
-               private byte[] Hash (string hashAlgorithm) 
+               // reuse hash - most document will always use the same hash
+               private HashAlgorithm GetHash (string algorithm) 
                {
-                       HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
-                       // get the hash of the C14N SignedInfo element
-                       return hash.ComputeHash (SignedInfoTransformed ());
+                       HashAlgorithm hash = (HashAlgorithm) hashes [algorithm];
+                       if (hash == null) {
+                               hash = HashAlgorithm.Create (algorithm);
+                               if (hash == null)
+                                       throw new CryptographicException ("Unknown hash algorithm: {0}", algorithm);
+                               hashes.Add (algorithm, hash);
+                               // now ready to be used
+                       }
+                       else {
+                               // important before reusing an hash object
+                               hash.Initialize ();
+                       }
+                       return hash;
                }
 
                public bool CheckSignature () 
                {
-                       // CryptographicException
-                       if (key == null)
-                               key = GetPublicKey ();
-                       return CheckSignature (key);
+                       return (CheckSignatureInternal (null) != null);
                }
 
-               private bool CheckReferenceIntegrity () 
+               private bool CheckReferenceIntegrity (ArrayList referenceList
                {
+                       if (referenceList == null)
+                               return false;
+
                        // check digest (hash) for every reference
-                       foreach (Reference r in signature.SignedInfo.References) {
+                       foreach (Reference r in referenceList) {
                                // stop at first broken reference
-                               if (! Compare (r.DigestValue, GetReferenceHash (r)))
+                               byte[] hash = GetReferenceHash (r);
+                               if (! Compare (r.DigestValue, hash))
                                        return false;
                        }
                        return true;
@@ -208,26 +459,77 @@ namespace System.Security.Cryptography.Xml {
                {
                        if (key == null)
                                throw new ArgumentNullException ("key");
+                       return (CheckSignatureInternal (key) != null);
+               }
 
-                       // Part 1: Are all references digest valid ?
-                       bool result = CheckReferenceIntegrity ();
-                       if (result) {
-                               // Part 2: Is the signature (over SignedInfo) valid ?
-                               SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
+               private AsymmetricAlgorithm CheckSignatureInternal (AsymmetricAlgorithm key)
+               {
+                       pkEnumerator = null;
 
-                               byte[] hash = Hash (sd.DigestAlgorithm);
-                               AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm);
+                       if (key != null) {
+                               // check with supplied key
+                               if (!CheckSignatureWithKey (key))
+                                       return null;
+                       }
+                       else {
+                               if (Signature.KeyInfo == null)
+                                       throw new CryptographicException ("At least one KeyInfo is required.");
+                               // no supplied key, iterates all KeyInfo
+                               while ((key = GetPublicKey ()) != null) {
+                                       if (CheckSignatureWithKey (key)) {
+                                               break;
+                                       }
+                               }
+                               pkEnumerator = null;
+                               if (key == null)
+                                       return null;
+                       }
+
+                       // some parts may need to be downloaded
+                       // so where doing it last
+                       if (!CheckReferenceIntegrity (m_signature.SignedInfo.References))
+                               return null;
 
-                               if (verifier != null) {
-                                       verifier.SetKey (key);
-                                       verifier.SetHashAlgorithm (sd.DigestAlgorithm);
-                                       result = verifier.VerifySignature (hash, signature.SignatureValue); 
+                       if (manifests != null) {
+                               // do not use foreach as a manifest could contain manifests...
+                               for (int i=0; i < manifests.Count; i++) {
+                                       Manifest manifest = new Manifest ((manifests [i] as XmlDocument).DocumentElement);
+                                       if (! CheckReferenceIntegrity (manifest.References))
+                                               return null;
                                }
-                               else
-                                       result = false;
                        }
+                       return key;
+               }
 
-                       return result;
+               // Is the signature (over SignedInfo) valid ?
+               private bool CheckSignatureWithKey (AsymmetricAlgorithm key) 
+               {
+                       if (key == null)
+                               return false;
+
+                       SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (m_signature.SignedInfo.SignatureMethod);
+                       if (sd == null)
+                               return false;
+
+                       AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm);
+                       if (verifier == null)
+                               return false;
+
+                       try {
+                               verifier.SetKey (key);
+                               verifier.SetHashAlgorithm (sd.DigestAlgorithm);
+
+                               HashAlgorithm hash = GetHash (sd.DigestAlgorithm);
+                               // get the hash of the C14N SignedInfo element
+                               MemoryStream ms = (MemoryStream) SignedInfoTransformed ();
+
+                               byte[] digest = hash.ComputeHash (ms);
+                               return verifier.VerifySignature (digest, m_signature.SignatureValue);
+                       }
+                       catch {
+                               // e.g. SignatureMethod != AsymmetricAlgorithm type
+                               return false;
+                       } 
                }
 
                private bool Compare (byte[] expected, byte[] actual) 
@@ -251,39 +553,61 @@ namespace System.Security.Cryptography.Xml {
                        if (macAlg == null)
                                throw new ArgumentNullException ("macAlg");
 
-                       // Part 1: Are all references digest valid ?
-                       bool result = CheckReferenceIntegrity ();
-                       if (result) {
-                               // Part 2: Is the signature (over SignedInfo) valid ?
-                               byte[] actual = macAlg.ComputeHash (SignedInfoTransformed ());
-                               result = Compare (signature.SignatureValue, actual);
+                       pkEnumerator = null;
+
+                       // Is the signature (over SignedInfo) valid ?
+                       Stream s = SignedInfoTransformed ();
+                       if (s == null)
+                               return false;
+
+                       byte[] actual = macAlg.ComputeHash (s);
+                       // HMAC signature may be partial
+                       if (m_signature.SignedInfo.SignatureLength != null) {
+                               int length = actual.Length;
+                               try {
+                                       // SignatureLength is in bits
+                                       length = (Int32.Parse (m_signature.SignedInfo.SignatureLength) >> 3);
+                               }
+                               catch {
+                               }
+
+                               if (length != actual.Length) {
+                                       byte[] trunked = new byte [length];
+                                       Buffer.BlockCopy (actual, 0, trunked, 0, length);
+                                       actual = trunked;
+                               }
                        }
-                       return result;
+
+                       if (Compare (m_signature.SignatureValue, actual)) {
+                               // some parts may need to be downloaded
+                               // so where doing it last
+                               return CheckReferenceIntegrity (m_signature.SignedInfo.References);
+                       }
+                       return false;
+               }
+
+#if NET_2_0
+               [MonoTODO]
+               public bool CheckSignature (X509Certificate2 certificate, bool verifySignatureOnly)
+               {
+                       throw new NotImplementedException ();
                }
+#endif
 
                public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey) 
                {
-                       // here's the key used for verifying the signature
-                       if (key == null)
-                               key = GetPublicKey ();
-                       signingKey = key;
-                       // we'll find the key if we haven't already
-                       return CheckSignature (key);
+                       signingKey = CheckSignatureInternal (null);
+                       return (signingKey != null);
                }
 
                public void ComputeSignature () 
                {
                        if (key != null) {
                                // required before hashing
-                               signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
+                               m_signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
                                DigestReferences ();
 
-                               SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
-
-                               // the hard part - C14Ning the KeyInfo
-                               byte[] hash = Hash (sd.DigestAlgorithm);
                                AsymmetricSignatureFormatter signer = null;
-
                                // in need for a CryptoConfig factory
                                if (key is DSA)
                                        signer = new DSASignatureFormatter (key);
@@ -291,8 +615,14 @@ namespace System.Security.Cryptography.Xml {
                                        signer = new RSAPKCS1SignatureFormatter (key);
 
                                if (signer != null) {
+                                       SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (m_signature.SignedInfo.SignatureMethod);
+
+                                       HashAlgorithm hash = GetHash (sd.DigestAlgorithm);
+                                       // get the hash of the C14N SignedInfo element
+                                       byte[] digest = hash.ComputeHash (SignedInfoTransformed ());
+
                                        signer.SetHashAlgorithm ("SHA1");
-                                       signature.SignatureValue = signer.CreateSignature (hash);
+                                       m_signature.SignatureValue = signer.CreateSignature (digest);
                                }
                        }
                }
@@ -305,8 +635,8 @@ namespace System.Security.Cryptography.Xml {
                        if (macAlg is HMACSHA1) {
                                DigestReferences ();
 
-                               signature.SignedInfo.SignatureMethod = XmlDsigHMACSHA1Url;
-                               signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ());
+                               m_signature.SignedInfo.SignatureMethod = XmlDsigHMACSHA1Url;
+                               m_signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ());
                        }
                        else 
                                throw new CryptographicException ("unsupported algorithm");
@@ -323,40 +653,61 @@ namespace System.Security.Cryptography.Xml {
                        return xel;
                }
 
+               // According to book ".NET Framework Security" this method
+               // iterates all possible keys then return null
                protected virtual AsymmetricAlgorithm GetPublicKey () 
                {
-                       AsymmetricAlgorithm key = null;
-                       if (signature.KeyInfo != null) {
-                               foreach (KeyInfoClause kic in signature.KeyInfo) {
-                                       if (kic is DSAKeyValue)
-                                               key = DSA.Create ();
-                                       else if (kic is RSAKeyValue) 
-                                               key = RSA.Create ();
-
-                                       if (key != null) {
-                                               key.FromXmlString (kic.GetXml ().InnerXml);
-                                               break;
-                                       }
+                       if (m_signature.KeyInfo == null)
+                               return null;
+
+                       if (pkEnumerator == null) {
+                               pkEnumerator = m_signature.KeyInfo.GetEnumerator ();
+                       }
+
+                       if (pkEnumerator.MoveNext ()) {
+                               AsymmetricAlgorithm key = null;
+                               KeyInfoClause kic = (KeyInfoClause) pkEnumerator.Current;
+
+                               if (kic is DSAKeyValue)
+                                       key = DSA.Create ();
+                               else if (kic is RSAKeyValue) 
+                                       key = RSA.Create ();
+
+                               if (key != null) {
+                                       key.FromXmlString (kic.GetXml ().InnerXml);
+                                       return key;
                                }
                        }
-                       return key;
+                       return null;
                }
 
                public XmlElement GetXml () 
                {
-                       return signature.GetXml ();
+                       return m_signature.GetXml (envdoc);
                }
 
                public void LoadXml (XmlElement value) 
                {
-                       signature.LoadXml (value);
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+
+                       signatureElement = value;
+                       m_signature.LoadXml (value);
+#if NET_2_0
+                       // Need to give the EncryptedXml object to the 
+                       // XmlDecryptionTransform to give it a fighting 
+                       // chance at decrypting the document.
+                       foreach (Reference r in m_signature.SignedInfo.References) {
+                               foreach (Transform t in r.TransformChain) {
+                                       if (t is XmlDecryptionTransform) 
+                                               ((XmlDecryptionTransform) t).EncryptedXml = EncryptedXml;
+                               }
+                       }
+#endif
                }
 
-#if ! NET_1_0
-               private XmlResolver xmlResolver;
-
-               [MonoTODO("property not (yet) used in class")]
-               [ComVisible(false)]
+#if NET_1_1
+               [ComVisible (false)]
                public XmlResolver Resolver {
                        set { xmlResolver = value; }
                }