none
Verify signed XML with xpath transformation RRS feed

  • Question

  • The SignedXML.CheckSignature() method returns false for signed XML were an xpath transformation is used to only sign a part of the XML file. According to Microsoft this is due to a security reason described in Security Bulletin MS16-035

    Since I do have several XML reports which used this method to sign the result part of the XML I need a way to validate the signature of those XMLs.

    Is there any known way to verify the signature of existing XML file in another safe way? The MSDN examples from MS still uses the no longer supported method.

    The code below shows an example to create, sign and verify a simple XML. As long as the full XML is signed (no xpath is used) everything works fine. The example with an xpath transformation the verification fails.

    Thanks for any thoughts or hints.

    namespace SignXML {
    
      
      public class SignVerifyEnvelope {
    
        private const string m_sPrivateKey = @"<RSAKeyValue>
            <Modulus>q+usJenCg9biPEwkoKjdJv5CnlAICw65YQKZToQFYVjm+y8sE6lXPr3BStZs0lJnKuKxfiqSd2JzHITm0nMd+5BvNU1nlOwVRRujl6lyic1V0jhkHa6RJVNJYBwNhB8tUgjvoopen836w9eV6NrPby5HSAe7fYlnqzqbxUbKLRM=</Modulus>
            <Exponent>AQAB</Exponent>
            <P>wj939t5Kl12mLqwnMCGCalCckZjDig2sH0puYr/8xLrO6iXEBPxvSOn8lDYm5QecIkELH44qi+EzSOnOaHM3ow==</P>
            <Q>4pMh/40d+dF+kqHkCfKeskYUiaO2BRX1aM4haAA4Wazh7eB3m3xgURJCGToQq/5PF9JXevsu/x+cMAiFXa9L0Q==</Q>
            <DP>lS/l1Sa5JzP31FTH7EFwRnMPLQuk8ry5e3bQ/OrzGa1YCaFjwWiXl+qN4M8OZEzJ780A3AV2S+R0XQWlT7dd/Q==</DP>
            <DQ>kdsfvtBMZL7dGW4IS/MZADCnFfgCvAFny0EvNjE2FVx60NR4iuqlYms7i9vaUa5TokUxNaUvmuLxe+ZwXF/1kQ==</DQ>
            <InverseQ>pWCnOQVvY/O5E2aR2D10HCbj+2zdKQwSaTdg2auy+vpJ+p5qfxuyMemIiRngN0XBWs26i2DH/16S2yAyrh9WaQ==</InverseQ>
            <D>G8o5sTCLJR08GWZu2UV9PMtcNhOrvoB/zhhJyHD7dFPsP8LNS37+albpWCaMaqcFTt+xXzqUeGstPk6WsoKx4BnfiRX+J1FTjPvtLjPmZ+KCKAvQF5iORFU5ARjTTVJLn+sJyifu4mvf2Di1j7s6BsltBh3+PTvnS93kfO4WAWE=</D>
          </RSAKeyValue>";
    
        private const string m_sPublicKey = @"<RSAKeyValue>
            <Modulus>q+usJenCg9biPEwkoKjdJv5CnlAICw65YQKZToQFYVjm+y8sE6lXPr3BStZs0lJnKuKxfiqSd2JzHITm0nMd+5BvNU1nlOwVRRujl6lyic1V0jhkHa6RJVNJYBwNhB8tUgjvoopen836w9eV6NrPby5HSAe7fYlnqzqbxUbKLRM=</Modulus>
            <Exponent>AQAB</Exponent>
          </RSAKeyValue>";
    
        public static void Main(String[] args) {
          try {
            const string unsignedXmlFile = "Example.xml";
            const string completeSignedXmlFile = "FullySignedExample.xml";
            const string partiallySignedXmlFile = "PartiallySignedExample.xml";
    
            // Generate a signing key.
            RSACryptoServiceProvider Key = new RSACryptoServiceProvider();
            Key.FromXmlString(m_sPrivateKey);
    
            // Create an XML file to sign.
            CreateSomeXml(unsignedXmlFile);
    
            // Sign the complete XML according to MSDN
            Console.WriteLine("Sign complete XML");
            SignXmlFile(unsignedXmlFile, completeSignedXmlFile, Key, true/*Sign Complete XML*/);
            SignXmlFile(unsignedXmlFile, partiallySignedXmlFile, Key, false/*Sign Partial XML*/);
    
            // Verify the signature of the fully signed XML
            AnalyzeResult(VerifySignature(completeSignedXmlFile, Key));
            // Verify the signature of the partially signed XML
            AnalyzeResult(VerifySignature(partiallySignedXmlFile, Key));
          }
          catch (CryptographicException e) {
            Console.WriteLine(e.Message);
          }
          Console.ReadKey();
        }
    
        /// <summary>
        /// Sign an XML file and save the signature in a new file.
        /// 
        /// Complete XML according to MSDN example:
        /// https://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.xmldsigenvelopedsignaturetransform(v=vs.110).aspx
        /// 
        /// Sign only a sub element of the XML by using xpath. According to MSDN example:
        /// https://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.xmldsigxpathtransform.xmldsigxpathtransform(v=vs.110).aspx
        /// 
        /// </summary>
        private static void SignXmlFile(string FileName, string SignedFileName, RSA Key, bool SignCompleteXml) {
          // Create a new XML document.
          XmlDocument doc = new XmlDocument();
          // use same settings as for the signature calculation
          doc.PreserveWhitespace = true;
    
          // Load the passed XML file using it's name.
          doc.Load(new XmlTextReader(FileName));
    
          // Create a SignedXml object.
          SignedXml signedXml = new SignedXml(doc);
    
          // Add the key to the SignedXml document. 
          signedXml.SigningKey = Key;
    
          // Create a reference to be signed: Sign the complete XML
          Reference reference = new Reference();
          reference.Uri = "";
    
          if (SignCompleteXml) {
            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
            reference.AddTransform(env);
          }
          else {
            string XPathString = "ancestor-or-self::FirstElement";
            XmlDsigXPathTransform XPathTransform = CreateXPathTransform(XPathString);
            reference.AddTransform(XPathTransform);
          }
    
          // Add the reference to the SignedXml object.
          signedXml.AddReference(reference);
    
          // Add an RSAKeyValue KeyInfo (optional; helps recipient find key to validate).      
          KeyInfo keyInfo = new KeyInfo();
          keyInfo.AddClause(new RSAKeyValue((RSA)Key));
          signedXml.KeyInfo = keyInfo;
    
          // Compute the signature.
          signedXml.ComputeSignature();
    
          // Get the XML representation of the signature and save
          // it to an XmlElement object.
          XmlElement xmlDigitalSignature = signedXml.GetXml();
    
          // Append the element to the XML document.
          doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
    
          // Save the signed XML document to a file specified
          // using the passed string.
          XmlTextWriter xmltw = new XmlTextWriter(SignedFileName, new UTF8Encoding(false));
          doc.WriteTo(xmltw);
          xmltw.Close();
        }
    
        /// <summary>
        /// Create XML DSIG object for xpath transformation
        /// </summary>
        static private XmlDsigXPathTransform CreateXPathTransform(string XPathString) {
          // Create a new XMLDocument object.
          XmlDocument doc = new XmlDocument();
          doc.PreserveWhitespace = true;
    
          // Create a new XmlElement.
          XmlElement xPathElem = doc.CreateElement("XPath");
    
          // Set the element text to the value
          // of the XPath string.
          xPathElem.InnerText = XPathString;
    
          // Create a new XmlDsigXPathTransform object.
          XmlDsigXPathTransform xForm = new XmlDsigXPathTransform();
    
          // Load the XPath XML from the element. 
          xForm.LoadInnerXml(xPathElem.SelectNodes("."));
    
          return xForm;
        }
    
        /// <summary>
        /// Verify the embedded signature according to the MSDN example
        /// 
        /// The verification returns false in case of untrusted transformations:
        /// https://technet.microsoft.com/en-us/library/security/ms16-035.aspx
        /// </summary>
        /// <returns></returns>
        private static bool VerifySignature(string Filename, RSA Key) {
          Console.WriteLine("Verify signature of {0}", Filename);
    
          // Load signature
          XmlDocument signedDocument = new XmlDocument();
          signedDocument.PreserveWhitespace = true;
    
          // Load the passed XML file into the document. 
          signedDocument.Load(Filename);
    
          // Find the "Signature" node and create a new
          // XmlNodeList object.
          XmlNodeList nodeList = signedDocument.GetElementsByTagName("Signature");
          XmlNode signatureNode = nodeList[0];
    
          // Create a new SignedXml object and pass it the XML document class.
          SignedXml signedXml = new SignedXml(signedDocument);
          bool bTest = signedXml.CheckSignature();
    
          // Load the signature node.
          signedXml.LoadXml((XmlElement)signatureNode);
    
          // Check the signature and return the result.
          return signedXml.CheckSignature(Key);
        }
    
        /// <summary>
        /// Create the following XML:
        ///   <MyXML>
        ///    <FirstElement>This tag includes some data</FirstElement>
        ///    <SecondElement>This tag includes some other data</SecondElement>
        ///  </MyXML>
        /// </summary>
        private static void CreateSomeXml(string FileName) {
          // Create a new XmlDocument object.
          XmlDocument document = new XmlDocument();
          document.PreserveWhitespace = true;
    
          // Create a new XmlNode object.
          XmlNode node = document.CreateNode(XmlNodeType.Element, "", "MyXML", "");
    
          // Append the node to the document.
          document.AppendChild(node);
    
          // Create a first XML node
          XmlNode subnodeA = document.CreateNode(XmlNodeType.Element, "", "FirstElement", "");
          subnodeA.InnerText = "This tag includes some data";
    
          // Create a first XML node
          XmlNode subnodeB = document.CreateNode(XmlNodeType.Element, "", "SecondElement", "");
          subnodeB.InnerText = "This tag includes some other data";
    
          // Append the node to the document.
          document.DocumentElement.AppendChild(subnodeA);
          document.DocumentElement.AppendChild(subnodeB);
    
          // Save the XML document to the file name specified.
          XmlTextWriter xmltw = new XmlTextWriter(FileName, new UTF8Encoding(false));
          xmltw.Formatting = Formatting.Indented;
          document.WriteTo(xmltw);
          xmltw.Close();
        }
    
        private static void AnalyzeResult(bool result) {
          if (result) {
            Console.WriteLine("The XML signature is valid.");
          }
          else {
            Console.WriteLine("The XML signature is not valid.");
          }
        }
    
      }
    
    }
    
    Sunday, September 25, 2016 5:28 PM

Answers

  • The KB 3148821 article (https://support.microsoft.com/en-us/kb/3148821) describes how to activate the xpath transformation again. For this the .NET security settings needs to be changed in the registry.

    See also the following article to get more background information about the vulnerability of the SignedXML class:
    https://coding.abel.nu/2016/03/vulnerability-in-net-signedxml/


    • Marked as answer by Raphael____ Monday, October 3, 2016 6:17 AM
    Monday, October 3, 2016 6:17 AM

All replies

  • Hi,

    I have the same question.

    I try the code provided by MSDN article. It does not work.

    https://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.xmldsigxpathtransform(v=vs.110).aspx

    Could you provide you bugs in the link?

    http://connect.microsoft.com/

    I wanna find the solution.

    zark

    Monday, September 26, 2016 10:58 AM
  • Hi,

    I did not submitted a bug report to Microsoft. MS decided to no longer support this functionality therefore it's no really a bug.

    I would be glad if Microsoft could provide a suggestion how already existing signed XML files can be validated even with the security fix installed.

    Tuesday, September 27, 2016 7:40 AM
  • The KB 3148821 article (https://support.microsoft.com/en-us/kb/3148821) describes how to activate the xpath transformation again. For this the .NET security settings needs to be changed in the registry.

    See also the following article to get more background information about the vulnerability of the SignedXML class:
    https://coding.abel.nu/2016/03/vulnerability-in-net-signedxml/


    • Marked as answer by Raphael____ Monday, October 3, 2016 6:17 AM
    Monday, October 3, 2016 6:17 AM