Merge pull request #5260 from BrzVlad/fix-handler-block
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / SignedXml.cs
index 68adec70c92c4a7a85be16146b30fea8279307cb..84d13e83c14f5eadd2fc2cbbc3ecbffd57666cc3 100644 (file)
@@ -38,38 +38,43 @@ 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 {
-
-               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";
-               public const string XmlLicenseTransformUrl                      = "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform";
+               public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#";
+               public const string XmlDsigMinimalCanonicalizationUrl = "http://www.w3.org/2000/09/xmldsig#minimal";
+               public const string XmlDsigCanonicalizationUrl = XmlDsigC14NTransformUrl;
+               public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigC14NWithCommentsTransformUrl;
+
+               public const string XmlDsigSHA1Url = "http://www.w3.org/2000/09/xmldsig#sha1";
+               public const string XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
+               public const string XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
+               public const string XmlDsigHMACSHA1Url = "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
+
+               public const string XmlDsigSHA256Url = "http://www.w3.org/2001/04/xmlenc#sha256";
+               public const string XmlDsigRSASHA256Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
+
+               // Yes, SHA384 is in the xmldsig-more namespace even though all the other SHA variants are in xmlenc. That's the standard.
+               public const string XmlDsigSHA384Url = "http://www.w3.org/2001/04/xmldsig-more#sha384";
+               public const string XmlDsigRSASHA384Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384";
+
+               public const string XmlDsigSHA512Url = "http://www.w3.org/2001/04/xmlenc#sha512";
+               public const string XmlDsigRSASHA512Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512";
+
+               public const string XmlDsigC14NTransformUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
+               public const string XmlDsigC14NWithCommentsTransformUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
+               public const string XmlDsigExcC14NTransformUrl = "http://www.w3.org/2001/10/xml-exc-c14n#";
+               public const string XmlDsigExcC14NWithCommentsTransformUrl = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments";
+               public const string XmlDsigBase64TransformUrl = "http://www.w3.org/2000/09/xmldsig#base64";
+               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";
+               public const string XmlDsigEnvelopedSignatureTransformUrl = "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
+               public const string XmlDecryptionTransformUrl = "http://www.w3.org/2002/07/decrypt#XML";
+               public const string XmlLicenseTransformUrl = "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform";
 
                private EncryptedXml encryptedXml;
-#endif
 
                protected Signature m_signature;
                private AsymmetricAlgorithm key;
@@ -79,15 +84,11 @@ namespace System.Security.Cryptography.Xml {
                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
+               internal XmlResolver _xmlResolver = new XmlUrlResolver ();
+               private bool _bResolverSet = true;
+               internal XmlElement _context;
                private ArrayList manifests;
-#if NET_2_0
                private IEnumerator _x509Enumerator;
-#endif
 
                private static readonly char [] whitespaceChars = new char [] {' ', '\r', '\n', '\t'};
 
@@ -96,6 +97,7 @@ namespace System.Security.Cryptography.Xml {
                        m_signature = new Signature ();
                        m_signature.SignedInfo = new SignedInfo ();
                        hashes = new Hashtable (2); // 98% SHA1 for now
+                       _context = null;
                }
 
                public SignedXml (XmlDocument document) : this ()
@@ -103,6 +105,7 @@ namespace System.Security.Cryptography.Xml {
                        if (document == null)
                                throw new ArgumentNullException ("document");
                        envdoc = document;
+                       _context = document.DocumentElement;
                }
 
                public SignedXml (XmlElement elem) : this ()
@@ -110,23 +113,20 @@ namespace System.Security.Cryptography.Xml {
                        if (elem == null)
                                throw new ArgumentNullException ("elem");
                        envdoc = new XmlDocument ();
+                       _context = elem;
                        envdoc.LoadXml (elem.OuterXml);
                }
 
-#if NET_2_0
                [ComVisible (false)]
                public EncryptedXml EncryptedXml {
                        get { return encryptedXml; }
                        set { encryptedXml = value; }
                }
-#endif
 
                public KeyInfo KeyInfo {
                        get {
-#if NET_2_0
                                if (m_signature.KeyInfo == null)
                                        m_signature.KeyInfo = new KeyInfo ();
-#endif
                                return m_signature.KeyInfo;
                        }
                        set { m_signature.KeyInfo = value; }
@@ -163,6 +163,22 @@ namespace System.Security.Cryptography.Xml {
                        set { m_strSigningKeyName = value; }
                }
 
+               public XmlResolver Resolver
+               {
+                       // This property only has a setter. The rationale for this is that we don't have a good value
+                       // to return when it has not been explicitely set, as we are using XmlSecureResolver by default
+                       set
+                       {
+                               _xmlResolver = value;
+                               _bResolverSet = true;
+                       }
+               }
+
+               internal bool ResolverSet
+               {
+                       get { return _bResolverSet; }
+               }
+
                public void AddObject (DataObject dataObject) 
                {
                        m_signature.AddObject (dataObject);
@@ -170,10 +186,8 @@ namespace System.Security.Cryptography.Xml {
 
                public void AddReference (Reference reference) 
                {
-#if NET_2_0
                        if (reference == null)
                                throw new ArgumentNullException ("reference");
-#endif
                        m_signature.SignedInfo.AddReference (reference);
                }
 
@@ -183,9 +197,7 @@ namespace System.Security.Cryptography.Xml {
                        // 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 ();
 
@@ -236,13 +248,13 @@ namespace System.Security.Cryptography.Xml {
                                        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);
+                                       doc.AppendChild (doc.ImportNode (xel, true));
                                        FixupNamespaceNodes (xel, doc.DocumentElement, false);
                                }
                        }
-                       else if (xmlResolver != null) {
+                       else if (_xmlResolver != null) {
                                // TODO: need testing
-                               Stream s = (Stream) xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream));
+                               Stream s = (Stream) _xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream));
                                doc.Load (s);
                        }
 
@@ -300,12 +312,12 @@ namespace System.Security.Cryptography.Xml {
                                else if (r.Uri [0] == '#') {
                                        objectName = r.Uri.Substring (1);
                                }
-                               else if (xmlResolver != null) {
+                               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));
+                                               s = (Stream) _xmlResolver.GetEntity (uri, null, typeof (Stream));
                                        }
                                        catch {
                                                // may still be a local file (and maybe not xml)
@@ -318,7 +330,7 @@ namespace System.Security.Cryptography.Xml {
                                                if (obj.Id == objectName) {
                                                        found = obj.GetXml ();
                                                        found.SetAttribute ("xmlns", SignedXml.XmlDsigNamespaceUrl);
-                                                       doc.LoadXml (found.OuterXml);
+                                                       doc.AppendChild (doc.ImportNode (found, true));
                                                        // FIXME: there should be theoretical justification of copying namespace declaration nodes this way.
                                                        foreach (XmlNode n in found.ChildNodes)
                                                                // Do not copy default namespace as it must be xmldsig namespace for "Object" element.
@@ -329,8 +341,10 @@ namespace System.Security.Cryptography.Xml {
                                        }
                                        if (found == null && envdoc != null) {
                                                found = GetIdElement (envdoc, objectName);
-                                               if (found != null)
-                                                       doc.LoadXml (found.OuterXml);
+                                               if (found != null) {
+                                                       doc.AppendChild (doc.ImportNode (found, true));
+                                                       FixupNamespaceNodes (found, doc.DocumentElement, false);
+                                               }
                                        }
                                        if (found == null)
                                                throw new CryptographicException (String.Format ("Malformed reference object: {0}", objectName));
@@ -501,13 +515,8 @@ namespace System.Security.Cryptography.Xml {
                                if (!CheckSignatureWithKey (key))
                                        return null;
                        } else {
-#if NET_2_0
                                if (Signature.KeyInfo == null)
                                        return null;
-#else
-                               if (Signature.KeyInfo == null)
-                                       throw new CryptographicException ("At least one KeyInfo is required.");
-#endif
                                // no supplied key, iterates all KeyInfo
                                while ((key = GetPublicKey ()) != null) {
                                        if (CheckSignatureWithKey (key)) {
@@ -595,17 +604,28 @@ namespace System.Security.Cryptography.Xml {
                                return false;
 
                        byte[] actual = macAlg.ComputeHash (s);
-                       // HMAC signature may be partial
+                       // HMAC signature may be partial and specified by <HMACOutputLength>
                        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) {
+                               int length = Int32.Parse (m_signature.SignedInfo.SignatureLength);
+                               // we only support signatures with a multiple of 8 bits
+                               // and the value must match the signature length
+                               if ((length & 7) != 0)
+                                       throw new CryptographicException ("Signature length must be a multiple of 8 bits.");
+
+                               // SignatureLength is in bits (and we works on bytes, only in multiple of 8 bits)
+                               // and both values must match for a signature to be valid
+                               length >>= 3;
+                               if (length != m_signature.SignatureValue.Length)
+                                       throw new CryptographicException ("Invalid signature length.");
+
+                               // is the length "big" enough to make the signature meaningful ? 
+                               // we use a minimum of 80 bits (10 bytes) or half the HMAC normal output length
+                               // e.g. HMACMD5 output 128 bits but our minimum is 80 bits (not 64 bits)
+                               int minimum = Math.Max (10, actual.Length / 2);
+                               if (length < minimum)
+                                       throw new CryptographicException ("HMAC signature is too small");
+
+                               if (length < actual.Length) {
                                        byte[] trunked = new byte [length];
                                        Buffer.BlockCopy (actual, 0, trunked, 0, length);
                                        actual = trunked;
@@ -620,14 +640,12 @@ namespace System.Security.Cryptography.Xml {
                        return false;
                }
 
-#if NET_2_0
                [MonoTODO]
                [ComVisible (false)]
                public bool CheckSignature (X509Certificate2 certificate, bool verifySignatureOnly)
                {
                        throw new NotImplementedException ();
                }
-#endif
 
                public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey) 
                {
@@ -637,34 +655,36 @@ namespace System.Security.Cryptography.Xml {
 
                public void ComputeSignature () 
                {
-                       if (key != null) {
-                               if (m_signature.SignedInfo.SignatureMethod == null)
-                                       // required before hashing
-                                       m_signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
-                               else if (m_signature.SignedInfo.SignatureMethod != key.SignatureAlgorithm)
-                                       throw new CryptographicException ("Specified SignatureAlgorithm is not supported by the signing key.");
-                               DigestReferences ();
-
-                               AsymmetricSignatureFormatter signer = null;
-                               // in need for a CryptoConfig factory
-                               if (key is DSA)
-                                       signer = new DSASignatureFormatter (key);
-                               else if (key is RSA) 
-                                       signer = new RSAPKCS1SignatureFormatter (key);
-
-                               if (signer != null) {
-                                       SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (m_signature.SignedInfo.SignatureMethod);
-
-                                       HashAlgorithm hash = GetHash (sd.DigestAlgorithm, false);
-                                       // get the hash of the C14N SignedInfo element
-                                       byte[] digest = hash.ComputeHash (SignedInfoTransformed ());
-
-                                       signer.SetHashAlgorithm ("SHA1");
-                                       m_signature.SignatureValue = signer.CreateSignature (digest);
+                       DigestReferences ();
+
+                       if (key == null)
+                               throw new CryptographicException (SR.Cryptography_Xml_LoadKeyFailed);
+
+                       // Check the signature algorithm associated with the key so that we can accordingly set the signature method
+                       if (SignedInfo.SignatureMethod == null) {
+                               if (key is DSA) {
+                                       SignedInfo.SignatureMethod = XmlDsigDSAUrl;
+                               } else if (key is RSA) {
+                                       // Default to RSA-SHA1
+                                       SignedInfo.SignatureMethod = XmlDsigRSASHA1Url;
+                               } else {
+                                       throw new CryptographicException (SR.Cryptography_Xml_CreatedKeyFailed);
                                }
                        }
-                       else
-                               throw new CryptographicException ("signing key is not specified");
+
+                       // See if there is a signature description class defined in the Config file
+                       SignatureDescription signatureDescription = CryptoConfig.CreateFromName (SignedInfo.SignatureMethod) as SignatureDescription;
+                       if (signatureDescription == null)
+                               throw new CryptographicException (SR.Cryptography_Xml_SignatureDescriptionNotCreated);
+
+                       HashAlgorithm hashAlg = signatureDescription.CreateDigest ();
+                       if (hashAlg == null)
+                               throw new CryptographicException (SR.Cryptography_Xml_CreateHashAlgorithmFailed);
+
+                       byte[] hashvalue = hashAlg.ComputeHash (SignedInfoTransformed ());
+                       AsymmetricSignatureFormatter asymmetricSignatureFormatter = signatureDescription.CreateFormatter (key);
+
+                       m_signature.SignatureValue = asymmetricSignatureFormatter.CreateSignature (hashAlg);
                }
 
                public void ComputeSignature (KeyedHashAlgorithm macAlg) 
@@ -676,7 +696,6 @@ namespace System.Security.Cryptography.Xml {
 
                        if (macAlg is HMACSHA1) {
                                method = XmlDsigHMACSHA1Url;
-#if NET_2_0
                        } else if (macAlg is HMACSHA256) {
                                method = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
                        } else if (macAlg is HMACSHA384) {
@@ -685,7 +704,6 @@ namespace System.Security.Cryptography.Xml {
                                method = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512";
                        } else if (macAlg is HMACRIPEMD160) {
                                method = "http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160";
-#endif
                        }
 
                        if (method == null)
@@ -706,6 +724,12 @@ namespace System.Security.Cryptography.Xml {
                        if (xel == null) {
                                // search an "undefined" ID
                                xel = (XmlElement) document.SelectSingleNode ("//*[@Id='" + idValue + "']");
+                               if (xel == null) {
+                                       xel = (XmlElement) document.SelectSingleNode ("//*[@ID='" + idValue + "']");
+                                       if (xel == null) {
+                                               xel = (XmlElement) document.SelectSingleNode ("//*[@id='" + idValue + "']");
+                                       }
+                               }
                        }
                        return xel;
                }
@@ -721,7 +745,7 @@ namespace System.Security.Cryptography.Xml {
                                pkEnumerator = m_signature.KeyInfo.GetEnumerator ();
                        }
                        
-#if NET_2_0
+#if SECURITY_DEP
                        if (_x509Enumerator != null) {
                                if (_x509Enumerator.MoveNext ()) {
                                        X509Certificate cert = (X509Certificate) _x509Enumerator.Current;
@@ -745,7 +769,7 @@ namespace System.Security.Cryptography.Xml {
                                        return key;
                                }
 
-#if NET_2_0
+#if SECURITY_DEP
                                if (kic is KeyInfoX509Data) {
                                        _x509Enumerator = ((KeyInfoX509Data) kic).Certificates.GetEnumerator ();
                                        if (_x509Enumerator.MoveNext ()) {
@@ -770,7 +794,11 @@ namespace System.Security.Cryptography.Xml {
 
                        signatureElement = value;
                        m_signature.LoadXml (value);
-#if NET_2_0
+
+                       if (_context == null) {
+                               _context = value;
+                       }
+
                        // Need to give the EncryptedXml object to the 
                        // XmlDecryptionTransform to give it a fighting 
                        // chance at decrypting the document.
@@ -780,14 +808,6 @@ namespace System.Security.Cryptography.Xml {
                                                ((XmlDecryptionTransform) t).EncryptedXml = EncryptedXml;
                                }
                        }
-#endif
                }
-
-#if NET_1_1
-               [ComVisible (false)]
-               public XmlResolver Resolver {
-                       set { xmlResolver = value; }
-               }
-#endif
        }
 }