xml signature DS prefix?
Asked Answered
W

6

9

Is there a way to sign an XML file with RSA and to have the namespace prefix "ds:Signature" instead of "Signature"? I spent many hourstrying to solve this and from what I can see there is no solution.

It seems that it is hard-coded in the class System.Security.Cryptography.Xml.Signature.

XmlElement element = document.CreateElement("Signature", "http://www.w3.org/2000/09/xmldsig#");

If anyone knows a solution, I need to sign it like that cause the software importing it verifies it with "ds:signature", so with "ds" prefix the software verifies it like this:

    public static bool VerifySignature(XmlDocument doc, RSA key, string prefix)
    {
        SignedXml xml = new SignedXml(doc);
        string str = "Signature";
        if (!string.IsNullOrEmpty(prefix))
        {
            str = string.Format("{0}:{1}", prefix, str);
        }
        XmlNodeList elementsByTagName = doc.GetElementsByTagName(str);
        xml.LoadXml((XmlElement)elementsByTagName[0]);
        return xml.CheckSignature(key);
    }

  VerifySignature(xmlDoc, rsa, "ds");

normally it signs like this:

<kk>blabla<Signature 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/2000/09/xmldsig#rsa-sha1" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</DigestValue></Reference></SignedInfo><SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</SignatureValue></Signature></kk>

and I need it to do it like this:

<kk>blabla<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><ds:DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</ds:SignatureValue></ds:Signature></kk>
Wilkins answered 31/8, 2012 at 16:27 Comment(11)
Can't you modify XML DOM contents after signing everything and combining it into one DOM tree?Banville
if you modify any of the xml content after signing the signaturevalue will not match...Wilkins
the point is to modify the signature node, not the signed one.Banville
i think you don't understant. even if you add a whitespace in the signed xml text it cannot be verified . check first post, i added detailsWilkins
we offer .NET components for XMLDSig, XAdES and XMLEnc, so I think I know a fact or two about XML ;). You sign something (no matter what) and have a signature block. The signature block is not signed - the data is. If you modify the signature block, this does not invalidate the signature, made over data, as the data is not modified. I don't know .NET XML stuff, though, - maybe it's validation code that is makes checks in a wrong way.Banville
i guess it's only in .net the problem, if i sign <kk>blabla</kk> with signxml (like in my first post) and after i add "ds" prefix to elements it fails verification , because the hash differs on the modified xml ...Wilkins
That's my point - maybe you modify all elements and not just those elements that are included in signature.Banville
possible duplicate of .NET Signed XML PrefixBanville
look above to see exactly how i modify it , before without ds:, after with.Wilkins
Here you can find the solution: https://mcmap.net/q/909036/-net-signed-xml-prefixCineraria
@Cineraria , that solution is not valid, it will fail verification, you didn't understood the initial question. i already gave final solution.Wilkins
S
8

if anyone know a solution, i need to sign it like that cause the software importing it verifies it with "ds:signature" , so with "ds" prefix

The prefix should be unimportant - all that should matter is what namespace the element is in. It shouldn't matter how that namespace is expressed. If it does, that shows brokenness in the verifying code, I'd say.

However, if you really want to do this, is there any reason you don't want to just replace the element with one with the same contents, but using the prefix you want? It shouldn't be hard to do that in LINQ to XML.

Shingles answered 31/8, 2012 at 16:40 Comment(1)
i edited first post to see how the software that receives the XML verifies it... , you can't replace the elements prefix , because then the signaturevalue would differ...Wilkins
W
8

i found the solution here

using System;
using System.Reflection;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace mysign
{
public class PrefixedSignedXML : SignedXml
{
    public PrefixedSignedXML(XmlDocument document)
        : base(document)
    { }

    public PrefixedSignedXML(XmlElement element)
        : base(element)
    { }

    public PrefixedSignedXML()
        : base()
    { }

    public void ComputeSignature(string prefix)
    {
        this.BuildDigestedReferences();
        AsymmetricAlgorithm signingKey = this.SigningKey;
        if (signingKey == null)
        {
            throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
        }
        if (this.SignedInfo.SignatureMethod == null)
        {
            if (!(signingKey is DSA))
            {
                if (!(signingKey is RSA))
                {
                    throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
                }
                if (this.SignedInfo.SignatureMethod == null)
                {
                    this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
                }
            }
            else
            {
                this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
            }
        }
        SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
        if (description == null)
        {
            throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
        }
        HashAlgorithm hash = description.CreateDigest();
        if (hash == null)
        {
            throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
        }
        this.GetC14NDigest(hash, prefix);
        this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
    }

    public XmlElement GetXml(string prefix)
    {
        XmlElement e = this.GetXml();
        SetPrefix(prefix, e);
        return e;
    }

    //Invocar por reflexión al método privado SignedXml.BuildDigestedReferences
    private void BuildDigestedReferences()
    {
        Type t = typeof(SignedXml);
        MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
        m.Invoke(this, new object[] { });
    }

    private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
    {
        //string securityUrl = (this.m_containingDocument == null) ? null : this.m_containingDocument.BaseURI;
        //XmlResolver xmlResolver = new XmlSecureResolver(new XmlUrlResolver(), securityUrl);
        XmlDocument document = new XmlDocument();
        document.PreserveWhitespace = true;
        XmlElement e = this.SignedInfo.GetXml();
        document.AppendChild(document.ImportNode(e, true));
        //CanonicalXmlNodeList namespaces = (this.m_context == null) ? null : Utils.GetPropagatedAttributes(this.m_context);
        //Utils.AddNamespaces(document.DocumentElement, namespaces);

        Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
        //canonicalizationMethodObject.Resolver = xmlResolver;
        //canonicalizationMethodObject.BaseURI = securityUrl;
        SetPrefix(prefix, document.DocumentElement); //establecemos el prefijo antes de se que calcule el hash (o de lo contrario la firma no será válida)
        canonicalizationMethodObject.LoadInput(document);
        return canonicalizationMethodObject.GetDigestedOutput(hash);
    }

    private void SetPrefix(string prefix, XmlNode node)
    {
        foreach (XmlNode n in node.ChildNodes)
            SetPrefix(prefix, n);
        node.Prefix = prefix;
    }
}
}
Wilkins answered 9/9, 2012 at 21:46 Comment(1)
this didn't work for me, signature block ended up the same without the prefixRifling
P
2

I tried these solutions, but they didn't worked out. However, by looking at .NET source code (http://referencesource.microsoft.com/) we can see that this can be acomplished easily by providing a derived XmlDocument class to SignedXml, where namespace can be added. However, having the "ds" prefix within 'SignedInfo' and descendants will cause siganture to fail. Here is the best I could do without breaking the signature:


XmlDsigDocument.cs

using System;
using System.Collections.Generic;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;

namespace CustomSecurity
{
class XmlDsigDocument : XmlDocument
{
    // Constants
    public const string XmlDsigNamespacePrefix = "ds";

    /// <summary>
    /// Override CreateElement function as it is extensively used by SignedXml
    /// </summary>
    /// <param name="prefix"></param>
    /// <param name="localName"></param>
    /// <param name="namespaceURI"></param>
    /// <returns></returns>
    public override XmlElement CreateElement(string prefix, string localName, string namespaceURI)
    {
        // CAntonio. If this is a Digital signature security element, add the prefix. 
        if (string.IsNullOrEmpty(prefix))
        {
            // !!! Note: If you comment this line, you'll get a valid signed file! (but without ds prefix)
            // !!! Note: If you uncomment this line, you'll get an invalid signed file! (with ds prefix within 'Signature' object)
            //prefix = GetPrefix(namespaceURI);

            // The only way to get a valid signed file is to prevent 'Prefix' on 'SignedInfo' and descendants.
            List<string> SignedInfoAndDescendants = new List<string>();
            SignedInfoAndDescendants.Add("SignedInfo");
            SignedInfoAndDescendants.Add("CanonicalizationMethod");
            SignedInfoAndDescendants.Add("InclusiveNamespaces");
            SignedInfoAndDescendants.Add("SignatureMethod");
            SignedInfoAndDescendants.Add("Reference");
            SignedInfoAndDescendants.Add("Transforms");
            SignedInfoAndDescendants.Add("Transform");
            SignedInfoAndDescendants.Add("InclusiveNamespaces");
            SignedInfoAndDescendants.Add("DigestMethod");
            SignedInfoAndDescendants.Add("DigestValue");
            if (!SignedInfoAndDescendants.Contains(localName))
            {
                prefix = GetPrefix(namespaceURI);
            }
        }

        return base.CreateElement(prefix, localName, namespaceURI);
    }

    /// <summary>
    /// Select the standar prefix for the namespaceURI provided
    /// </summary>
    /// <param name="namespaceURI"></param>
    /// <returns></returns>
    public static string GetPrefix(string namespaceURI)
    {
        if (namespaceURI == "http://www.w3.org/2001/10/xml-exc-c14n#")
            return "ec";
        else if (namespaceURI == SignedXml.XmlDsigNamespaceUrl)
            return "ds";

        return string.Empty;
    }
}
}

This is used on the SignedXml Creation:

    // Create a new XML document.
    XmlDsigDocument doc = new XmlDsigDocument();

    // Load the passed XML file using its name.
    doc.Load(new XmlTextReader(FileName));

    // Create a SignedXml object.
    SignedXml signedXml = new SignedXml(doc);

You can see full source files at:

https://social.msdn.microsoft.com/Forums/en-US/cd595379-f66a-49c8-8ca2-62acdc58b252/add-prefixds-signedxml?forum=xmlandnetfx

Photoneutron answered 14/7, 2016 at 11:23 Comment(0)
A
0

May be correctly SetPrefix code looks like this:

    private void SetPrefix(String prefix, XmlNode node) {
        foreach (XmlNode n in node.ChildNodes)
        {
            SetPrefix(prefix, n);
            n.Prefix = prefix;
        }
    }
Austriahungary answered 14/2, 2013 at 12:20 Comment(3)
@GeorgeDima I'm playing around with this, and modifying the signature block in the xml doesn't seem to affect the signatureValue. Therefore, it should be safe to do this after the signature has been generated. Are you sure that didn't work for you?Ginaginder
yes, only my solution posted here worked for me, i mean to get it verified.Wilkins
Hi George, how your verification works? I am trying the same steps but it fails. Could you pleas post your code of signing and verification? It will help a lotWidthwise
S
0

I agree that Prefix should not be important, but...

XML becomes much easier in C# if you use XPath:

var s = signedXml.GetXml();
XmlNodeList nodes = s.SelectNodes("descendant-or-self::*");
foreach (XmlNode childNode in nodes)
{
    childNode.Prefix = "dsig";
}
Situation answered 10/1, 2017 at 21:51 Comment(0)
C
0

The code George Dima provide works.

I will explain how it works.

When you call the ComputeSignature method this will generate the Signature Value by digesting the value of the SignedInfo node.

The code provided by George Dima adds the Prefix to the SignedInfo node and its children BEFORE getting the digest value. This won't add the prefix to the whole xml structure

this is the method that generates the digest value of the signedinfo node

private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{        
    XmlDocument document = new XmlDocument();
    document.PreserveWhitespace = true;
    XmlElement e = this.SignedInfo.GetXml();
    document.AppendChild(document.ImportNode(e, true));        
    Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;

    SetPrefix(prefix, document.DocumentElement); //HERE'S WHERE THE PREFIX IS ADDED TO GET THE DIGEST VALUE
    canonicalizationMethodObject.LoadInput(document);
    return canonicalizationMethodObject.GetDigestedOutput(hash);
}

So you now have the digest value of the SignedInfo node WITH the prefix, and this value will be use to get the Signature Value, but you still DON'T have the xml with the prefix yet, so if you just do this

signedXml.GetXml();

you will get the xml without the prefix and of course because the signature value was calculated considering the ds prefix you will have an invalid signature so what you have to do is call the GetXml passing it the value of the prefix, in this case "ds" like this

signedXml.GetXml("ds");
Couple answered 8/6, 2017 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.