Problem with custom namespace with SignedXml

Aziz Mirzaev picture Aziz Mirzaev · Mar 31, 2011 · Viewed 10.7k times · Source

The problem occurs when I sign XML documents containing namespace prefixes and namespace references and then validate it. The validation always fails (returns false) in this case. When I remove the namespace prefixes and namespace references from the XML, signing and validating works fine.

Could you help me ?

Here is my code :

Inherited class from SignedXml

namespace Xmldsig
{
    using System;
    using System.Security.Cryptography;
    using System.Security.Cryptography.Xml;
    using System.Xml;

    public sealed class SignaturePropertiesSignedXml : SignedXml
    {
        private XmlDocument doc;
        private XmlElement signaturePropertiesRoot;
        private XmlElement qualifyingPropertiesRoot;

        private string signaturePropertiesId;

        public SignaturePropertiesSignedXml(XmlDocument doc)
            : base(doc)
        {
            return;
        }

        public SignaturePropertiesSignedXml(XmlDocument doc, string signatureId, string propertiesId)
            : base(doc)
        {
            this.signaturePropertiesId = propertiesId;
            this.doc = null;
            this.signaturePropertiesRoot = null;
            if (string.IsNullOrEmpty(signatureId))
            {
                throw new ArgumentException("signatureId cannot be empty", "signatureId");
            }
            if (string.IsNullOrEmpty(propertiesId))
            {
                throw new ArgumentException("propertiesId cannot be empty", "propertiesId");
            }

            this.doc = doc;
            base.Signature.Id = signatureId;

            this.qualifyingPropertiesRoot = doc.CreateElement("QualifyingProperties", "http://www.w3.org/2000/09/xmldsig#");
            this.qualifyingPropertiesRoot.SetAttribute("Target", "#" + signatureId);

            this.signaturePropertiesRoot = doc.CreateElement("SignedProperties", "http://www.w3.org/2000/09/xmldsig#");
            this.signaturePropertiesRoot.SetAttribute("Id", propertiesId);


            qualifyingPropertiesRoot.AppendChild(signaturePropertiesRoot);
            DataObject dataObject = new DataObject
            {
                Data = qualifyingPropertiesRoot.SelectNodes("."),
                Id = "idObject"
            };
            AddObject(dataObject);


        }

        public void AddProperty(XmlElement content)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }

            XmlElement newChild = this.doc.CreateElement("SignedSignatureProperties", "http://www.w3.org/2000/09/xmldsig#");

            newChild.AppendChild(content);
            this.signaturePropertiesRoot.AppendChild(newChild);
        }

        public override XmlElement GetIdElement(XmlDocument doc, string id)
        {
            if (String.Compare(id, signaturePropertiesId, StringComparison.OrdinalIgnoreCase) == 0)
                return this.signaturePropertiesRoot;            

            if (String.Compare(id, this.KeyInfo.Id, StringComparison.OrdinalIgnoreCase) == 0)
                return this.KeyInfo.GetXml();

            return base.GetIdElement(doc, id);
        }
    }
}

The class which signing xml

namespace Xmldsig
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml;
    using System.IO;
    using System.Security.Cryptography;
    using System.Security.Cryptography.Xml;
    using System.Security.Cryptography.X509Certificates;
    using Security.Cryptography;
    using System.Security.Principal;
    using System.Collections;

    public class VerifyResult
    {
        public string Timestamp { get; set; }
        public X509Certificate2 SigningCertificate { get; set; }
    }

    public class XmldsigClass
    {
        public static XmlDocument SignDocument(XmlDocument doc, string RefUri)
        {
            string idSignProperties = "idSignedProperties";
            SignaturePropertiesSignedXml signer = new SignaturePropertiesSignedXml(doc, "Uzb_sig_v001", idSignProperties);

            X509Certificate2 cert = GetCertificate();

            RSA key = (RSACryptoServiceProvider)cert.PrivateKey;
            signer.SigningKey = key;
            signer.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
            signer.SignedInfo.SignatureMethod = @"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

            // create a timestamp property
            XmlElement timestamp = doc.CreateElement("SigningTime", SignedXml.XmlDsigNamespaceUrl);
            timestamp.InnerText = DateTimeToCanonicalRepresentation();
            signer.AddProperty(timestamp);


            var certificateKeyInfo = new KeyInfo();
            certificateKeyInfo.Id = "idKeyInfo";
            certificateKeyInfo.AddClause(new KeyInfoX509Data(cert));
            signer.KeyInfo = certificateKeyInfo;

            Reference reference = new Reference(RefUri);
            reference.DigestMethod = @"http://www.w3.org/2001/04/xmlenc#sha256";
            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            signer.AddReference(reference);


            Reference propertiesRefki = new Reference();
            propertiesRefki.Uri = "#idKeyInfo";
            propertiesRefki.DigestMethod = @"http://www.w3.org/2001/04/xmlenc#sha256";
            signer.AddReference(propertiesRefki);

            Reference reference2 = new Reference();
            reference2.Uri = "#" + idSignProperties;
            reference2.DigestMethod = @"http://www.w3.org/2001/04/xmlenc#sha256";
            signer.AddReference(reference2);


            signer.ComputeSignature();            

            doc.DocumentElement.AppendChild(signer.GetXml());

            return doc;
        }


        public static bool CheckSignature(XmlDocument xmlDoc)
        {
            SignedXml signedXml = new SignedXml(xmlDoc);
            XmlNodeList elementsByTagName = xmlDoc.GetElementsByTagName("Signature");
            signedXml.LoadXml((XmlElement)elementsByTagName[0]);
            bool sigCheck = signedXml.CheckSignature();

            return sigCheck;
        }

        private static X509Certificate2 GetCertificate()
        {

            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2 card = null;
            foreach (X509Certificate2 cert in store.Certificates)
            {
                if (!cert.HasPrivateKey) continue;
                AsymmetricAlgorithm aa = cert.PrivateKey;
                ICspAsymmetricAlgorithm caa = aa as ICspAsymmetricAlgorithm;
                if (caa == null) continue;
                if (caa.CspKeyContainerInfo.HardwareDevice)
                {
                    card = cert;
                    break;
                }
            }
            store.Close();

            return card;
        }

        private static string DateTimeToCanonicalRepresentation()
        {
            var ahora = DateTime.Now.ToUniversalTime();
            return ahora.Year.ToString("0000") + "-" + ahora.Month.ToString("00") +
                   "-" + ahora.Day.ToString("00") +
                   "T" + ahora.Hour.ToString("00") + ":" +
                   ahora.Minute.ToString("00") + ":" + ahora.Second.ToString("00") +
                   "Z";
        }
    }    
}

And here I'm calling signing method

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.PreserveWhitespace = true;
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            xmlDoc.Load(openFileDialog1.FileName);

            XmlDocument resxml = Xmldsig.XmldsigClass.SignDocument(xmlDoc, "#Uzb_doc_v001");


            var name = openFileDialog1.FileName + ".xml";
            xmlDoc.Save(name);

            var bytes = System.IO.File.ReadAllBytes(name);
            System.IO.File.WriteAllBytes(name, bytes.Skip(3).ToArray());
            MessageBox.Show("Signed");
        }

And for verification

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.PreserveWhitespace = true;
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            xmlDoc.Load(openFileDialog1.FileName);

            bool b = Xmldsig.XmldsigClass.CheckSignature(xmlDoc);

            MessageBox.Show(b.ToString());
        }

Here's my signed xml

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<uz:CreditcardEnveloppe xmlns:uz="http://aaaa.com/CreditcardEnveloppe/transport" Id="Uzb_doc_v001" Version="1.0">
  <uz:creditcard>
    <uz:number>19834209</uz:number>
    <uz:expiry>02/02/2002</uz:expiry>
  </uz:creditcard>
  <Signature Id="sig_v001" xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
      <Reference URI="#Uzb_doc_v001">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
        <DigestValue>RnpNXyhxQcjr/SWqVlWY31S1xpj2opZhlsT4d1iyBKI=</DigestValue>
      </Reference>
      <Reference URI="#idKeyInfo">
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
        <DigestValue>O0Z2BU5ODwgOZOhpFvkcSaO/jmWFykBwnxMUD5a5SwM=</DigestValue>
      </Reference>
      <Reference URI="#idSignedProperties">
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
        <DigestValue>UVjk8Jkq0Y6OxFqiB4q/4vqli/KJT5pYEPzlNTkfIhY=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>prOZXn..</SignatureValue>
    <KeyInfo Id="idKeyInfo">
      <X509Data>
        <X509Certificate>MIIE7TCCA9WgAwIBAgISESBS...</X509Certificate>
      </X509Data>
    </KeyInfo>
    <Object Id="idObject">
      <QualifyingProperties Target="#sig_v001">
        <SignedProperties Id="idSignedProperties">
          <SignedSignatureProperties>
            <SigningTime>2011-03-30T06:01:48Z</SigningTime>
          </SignedSignatureProperties>
        </SignedProperties>
      </QualifyingProperties>
    </Object>
  </Signature>
</uz:CreditcardEnveloppe>

Thank you beforehand!

Answer

mustafa picture mustafa · Aug 24, 2011

Adding XmlDsigExcC14NTransform to all of your qualifying references solves the problem. I think there is something wrong in .NET Framework that causes this problem.

reference2.AddTransform(new XmlDsigExcC14NTransform() );