1

Signing an xml document using c# I continuously have signature verification issues because of the special characters, in concrete this one 'º' commonly used for addresses in Spain.

The encoding for this xml file is "ISO-8859-1", however if I use UTF-8 it works fine.

The method I use for signing is this one:

    public static string SignXml(XmlDocument Document, X509Certificate2 cert)
    {
        SignedXml signedXml = new SignedXml(Document);
        signedXml.SigningKey = cert.PrivateKey;

        // Create a reference to be signed.
        Reference reference = new Reference();
        reference.Uri = "";

        // Add an enveloped transformation to the reference.            
        XmlDsigEnvelopedSignatureTransform env =
           new XmlDsigEnvelopedSignatureTransform(true);
        reference.AddTransform(env);

        XmlDsigC14NTransform c14t = new XmlDsigC14NTransform();
        reference.AddTransform(c14t);

        KeyInfo keyInfo = new KeyInfo();
        KeyInfoX509Data keyInfoData = new KeyInfoX509Data(cert);
        keyInfo.AddClause(keyInfoData);
        signedXml.KeyInfo = keyInfo;

        // Add the reference to the SignedXml object.
        signedXml.AddReference(reference);

        // Compute the signature.
        signedXml.ComputeSignature();

        // Get the XML representation of the signature and save 
        // it to an XmlElement object.
        XmlElement xmlDigitalSignature = signedXml.GetXml();

        Document.DocumentElement.AppendChild(
            Document.ImportNode(xmlDigitalSignature, true));

        return Document.OuterXml;
    }

Taken from: http://www.wiktorzychla.com/2012/12/interoperable-xml-digital-signatures-c_20.html

And this is how I call it:

    static void Main(string[] args)
    {
        string path = ".\\DATOS\\Ejemplo.xml";
        string signedDocString = null;
        XmlDocument entrada = new XmlDocument();
        entrada.Load(path);

        X509Certificate2 myCert = null;
        X509Store store = new X509Store(StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);
        var certificates = store.Certificates;
        foreach (var certificate in certificates)
        {
            if (certificate.Subject.Contains("XXXX"))
            {
                Console.WriteLine(certificate.Subject);
                myCert = certificate;
                break;
            }
        }

        if (myCert != null)
        {
            signedDocString = SignXml(entrada, myCert);
        }

        if (VerifyXml(signedDocString))
        {
            Console.WriteLine("VALIDO");               
        }

        else
        {
            Console.WriteLine("NO VALIDO");
        }
        Console.ReadLine();
    }

The xml document must use the encoding ISO-8859-1, this is not optional. And I cannot suppress the special characters.

Any suggestion about how to handle this?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Michael Knight
  • 648
  • 2
  • 10
  • 26
  • 1
    Does the XML document correctly *identify* its encoding as ISO-8859-1? Is it working *until* the verification, and it's just not verifying correctly? What is the `VerifyXml` method? – Jon Skeet May 06 '14 at 18:01
  • Can you try instead of `entrada.Load(path);` do `entrada.Load(XmlReader.Create(new StreamReader(path,Encoding.GetEncoding("Windows-1252"))));` – rene May 06 '14 at 18:17
  • The header of the document is this . If I modify it to UTF-8 it works. VerifyXml is a method to verify the signature is right. The signature is made, but the verification returns "NOT VALID" when the encoding is ISO-8859-1. – Michael Knight May 06 '14 at 18:20
  • @rene I tried your tip and even using ISO-8859-1 as encoding. No success in both cases. – Michael Knight May 06 '14 at 18:24
  • 1
    @MichaelKnight: that is kind of strong requirement to have your XML encoded in ISO-8859-1, when most known parsers just honor the encoding information provided in the preamble. But if thi is so, I see no other solution than just skipping any special characters. – Wiktor Zychla May 06 '14 at 18:34
  • You can configure the encoding when you write out the XML, but you can't use `.OuterXML` to do it. Try http://stackoverflow.com/a/863501/298754 or http://stackoverflow.com/a/427737/298754 – Bobson May 06 '14 at 19:43
  • @Bobson I think my problem is different. The XML already exists and I try to sign it without success because of the reported issue. Actually, the document cannot be modified once the signature is done. – Michael Knight May 07 '14 at 08:38

1 Answers1

1

my fault.

In the verification method I was using utf8:

public bool VerifyXml( string SignedXmlDocumentString )
{
    byte[] stringData = Encoding.UTF8.GetBytes( SignedXmlDocumentString );
    using ( MemoryStream ms = new MemoryStream( stringData ) )
        return VerifyXmlFromStream( ms );
}

In that step I was altering the encoding and so the document content wasn't the same as the original one. Quite newbie error.

The solution:

        public static bool VerifyXml(XmlDocument SignedXmlDocument)
    {
        byte[] stringData = Encoding.Default.GetBytes(SignedXmlDocument.OuterXml);
        using (MemoryStream ms = new MemoryStream(stringData))
            return VerifyXmlFromStream(ms);
    }
Michael Knight
  • 648
  • 2
  • 10
  • 26
  • I *thought* you had to be using `Encoding` wrong somewhere... Although I didn't expect it to be like this. Glad you found it! – Bobson May 07 '14 at 19:18