2

I need to create a digital signature for an XML with ISO20022 standard.

The example shows 3 references, one of which is for KeyInfo element (the one with #_33d232d2-4591-4b49-b28d-3cb825fbeaa4 URI).

<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="#_33d232d2-4591-4b49-b28d-3cb825fbeaa4">
        <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <ds:DigestValue>h9toHGSlK/x1zE7egK0yEj06W2D9wAEK/VAuiwU8+R8=</ds:DigestValue>
    </ds:Reference>
    <ds:Reference Type="http://uri.etsi.org/01903/v1.3.2#SignedProperties" URI="#_aba0ee84-5f37-499e-a8e8-caa7f398341c-signedprops">
        <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <ds:DigestValue>Ot7tqqOtgtguRadTQi0fh5FU3XL/4/mHIv7Eoy67t/s=</ds:DigestValue>
    </ds:Reference>
    <ds:Reference>
        <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <ds:DigestValue>1ZZln0/NzN/eB1wIrxyp/c3SOjKWnk00Lh1bKTXlTAE=</ds:DigestValue>
    </ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>VRn+Q7K6snvKrFPwtH302iKPjAx1k97TKIvjysdH+/I8EMyzWg20gZ1fO1gjKk245nfzXIsiuoVIZJtBKNSE9Tp+VXegJxyAoXx1bz8fMZIbdjjhXaYzdx2yCGh9Fllrbg+y9RZy9VvG7sLQeu91gOge7GHNIxO6jck96yVsY8k=</ds:SignatureValue>
<ds:KeyInfo Id="_33d232d2-4591-4b49-b28d-3cb825fbeaa4">
    <ds:X509Data>
        <ds:X509IssuerSerial>
            <ds:X509IssuerName>C=SE, O=CMA Small Systems AB, CN=Test CA</ds:X509IssuerName>
            <ds:X509SerialNumber>12345678</ds:X509SerialNumber>
        </ds:X509IssuerSerial>
    </ds:X509Data>
</ds:KeyInfo>
<ds:Object>
    <xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#">
        <xades:SignedProperties Id="_aba0ee84-5f37-499e-a8e8-caa7f398341csignedprops">
            <xades:SignedSignatureProperties>
                <xades:SigningTime>2019-08-23T19:01:41+12:00</xades:SigningTime>
                </xades:SignedSignatureProperties>
            </xades:SignedProperties>
        </xades:QualifyingProperties>
    </ds:Object>
</ds:Signature>

However when I try to do the same with .NET 6 I get the following exception

System.Security.Cryptography.CryptographicException: Malformed reference element.
   at System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList)
   at System.Security.Cryptography.Xml.Reference.UpdateHashValue(XmlDocument document, CanonicalXmlNodeList refList)
   at System.Security.Cryptography.Xml.SignedXml.BuildDigestedReferences()
   at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()

Inspecting the SignedXml class, it seems that KeyInfo is missing from the CanonicalXmlNodeList in the BuildDigestedReferences method. Is there any way around this?


This is my code for signing the XML

static void SignXmlWithCert(XmlDocument doc, X509Certificate2 cert)
{
    const string signedPropsIdSuffix = "-signedprops";

    var signedXml = new SignedXml(doc)
    {
        SigningKey = cert.GetRSAPrivateKey()
    };
    signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
    signedXml.SignedInfo.SignatureMethod        = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";

    var idKeyInfo = "_" + Guid.NewGuid();
    var idKeyInfoProps = "_" + Guid.NewGuid() + signedPropsIdSuffix;
    
    #region keyinfo

    var keyInfo = new KeyInfo();
    var keydata = new KeyInfoX509Data(cert, X509IncludeOption.None);
    keydata.AddIssuerSerial(cert.Issuer, cert.SerialNumber);
    keyInfo.AddClause(keydata);
    keyInfo.Id        = idKeyInfo;
    signedXml.KeyInfo = keyInfo;

    #endregion keyinfo

    #region References
    
    var transform = new XmlDsigEnvelopedSignatureTransform() { Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#" };
    var references = new List<Reference>();
    
    // first reference
    var keyInfoReference = new Reference();
    keyInfoReference.Uri          = "#" + keyInfo.Id;
    keyInfoReference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    keyInfoReference.AddTransform(transform);
    references.Add(keyInfoReference);

    //second reference
    var signaturePropertiesReference = new Reference();
    signaturePropertiesReference.Type         = "http://uri.etsi.org/01903/v1.3.2#SignedProperties";
    signaturePropertiesReference.Uri          = "#" + idKeyInfoProps;
    signaturePropertiesReference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    signaturePropertiesReference.AddTransform(transform);
    references.Add(signaturePropertiesReference);

    //third reference
    var documentReference = new Reference();
    documentReference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    documentReference.AddTransform(transform);
    references.Add(documentReference);

    foreach (var reference in references)
    {
        signedXml.AddReference(reference);
    }

    #endregion

    #region 4. Set up <ds:Object> with <QualifiyingProperties> inside that includes SigningTime

    var URI = "http://uri.etsi.org/01903/v1.3.2#";
    var qualifyingPropertiesRoot = doc.CreateElement("xades", "QualifyingProperties", URI);
    
    var signaturePropertiesRoot = doc.CreateElement("xades", "SignedProperties", URI);
    signaturePropertiesRoot.SetAttribute("Id", idKeyInfoProps);
    
    var SignedSignatureProperties = doc.CreateElement("xades", "SignedSignatureProperties", URI);
    var timestamp = doc.CreateElement("xades", "SigningTime", URI);
    timestamp.InnerText = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:sszzz");    // primero de la lista
    
    signaturePropertiesRoot.AppendChild(SignedSignatureProperties);
    SignedSignatureProperties.AppendChild(timestamp);
    qualifyingPropertiesRoot.AppendChild(signaturePropertiesRoot);
    
    var qualifyingPropertiesObject = new DataObject
    {
        Data = qualifyingPropertiesRoot.SelectNodes("."),
        Id   = idKeyInfoProps
    };

    signedXml.AddObject(qualifyingPropertiesObject);
    #endregion

    signedXml.ComputeSignature();
}
1
  • Save you signed xml in a file so you can compare with sample. Do you see any difference that I can help fix? Commented May 10, 2022 at 11:15

1 Answer 1

4

it seems that KeyInfo is missing from the CanonicalXmlNodeList in the BuildDigestedReferences method.

That's because it's not part of the document.

Is there any way around this?

Add it to the document. Rather than try to construct it by hand I doctored up your code to make two different SignedXml objects, one which puts the nodes in to be signed and the other does the final signature.

Commented out lines of code are lines that I had to change or remove to make your snippet otherwise work.

static void SignXmlWithCert(XmlDocument doc, X509Certificate2 cert)
{
    const string signedPropsIdSuffix = "-signedprops";

    var signedXml = new SignedXml(doc)
    {
        SigningKey = cert.GetRSAPrivateKey()
    };
    signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
    signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";

    var idKeyInfo = "_" + Guid.NewGuid();
    var idKeyInfoProps = "_" + Guid.NewGuid() + signedPropsIdSuffix;

    #region keyinfo

    var keyInfo = new KeyInfo();
    var keydata = new KeyInfoX509Data(cert, X509IncludeOption.None);
    keydata.AddIssuerSerial(cert.Issuer, cert.SerialNumber);
    keyInfo.AddClause(keydata);
    keyInfo.Id = idKeyInfo;
    signedXml.KeyInfo = keyInfo;

    #endregion keyinfo

    #region References

    //var transform = new XmlDsigEnvelopedSignatureTransform() { Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#" };
    var transform = new XmlDsigExcC14NTransform();
    var references = new List<Reference>();

    // first reference
    var keyInfoReference = new Reference();
    keyInfoReference.Uri = "#" + keyInfo.Id;
    keyInfoReference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    keyInfoReference.AddTransform(transform);
    references.Add(keyInfoReference);

    //second reference
    var signaturePropertiesReference = new Reference();
    signaturePropertiesReference.Type = "http://uri.etsi.org/01903/v1.3.2#SignedProperties";
    signaturePropertiesReference.Uri = "#" + idKeyInfoProps;
    signaturePropertiesReference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    signaturePropertiesReference.AddTransform(transform);
    references.Add(signaturePropertiesReference);

    //third reference
    var documentReference = new Reference();
    documentReference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    // The code in the question didn't assign Uri, and since no transform did an inherent
    // node resolution, signing failed.
    documentReference.Uri = "";
    documentReference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
    documentReference.AddTransform(transform);
    references.Add(documentReference);

    foreach (Reference reference in references)
    {
        signedXml.AddReference(reference);
    }

    #endregion

    #region 4. Set up <ds:Object> with <QualifiyingProperties> inside that includes SigningTime

    var URI = "http://uri.etsi.org/01903/v1.3.2#";
    XmlElement qualifyingPropertiesRoot = doc.CreateElement("xades", "QualifyingProperties", URI);

    XmlElement signaturePropertiesRoot = doc.CreateElement("xades", "SignedProperties", URI);
    signaturePropertiesRoot.SetAttribute("Id", idKeyInfoProps);

    XmlElement SignedSignatureProperties = doc.CreateElement("xades", "SignedSignatureProperties", URI);
    XmlElement timestamp = doc.CreateElement("xades", "SigningTime", URI);
    timestamp.InnerText = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:sszzz");    // primero de la lista

    signaturePropertiesRoot.AppendChild(SignedSignatureProperties);
    SignedSignatureProperties.AppendChild(timestamp);
    qualifyingPropertiesRoot.AppendChild(signaturePropertiesRoot);

    var qualifyingPropertiesObject = new DataObject
    {
        Data = qualifyingPropertiesRoot.SelectNodes("."),
        //Id = idKeyInfoProps
    };

    signedXml.AddObject(qualifyingPropertiesObject);
    #endregion

    SignedXml tmp = new SignedXml(doc)
    {
        SigningKey = signedXml.SigningKey,
        KeyInfo = signedXml.KeyInfo,
    };

    foreach (DataObject obj in signedXml.Signature.ObjectList)
    {
        tmp.AddObject(obj);
    }
    
    tmp.AddReference(new Reference(""));
    tmp.ComputeSignature();

    XmlTextWriter prettyOut = new XmlTextWriter(System.Console.Out);
    prettyOut.Formatting = Formatting.Indented;

    Console.WriteLine("Original document");
    doc.WriteTo(prettyOut);
    Console.WriteLine();
    Console.WriteLine();

    XmlElement elem = tmp.GetXml();
    doc.DocumentElement.AppendChild(elem);
    Console.WriteLine("Stage 1 signed");
    doc.WriteTo(prettyOut);
    Console.WriteLine();
    Console.WriteLine();

    signedXml.ComputeSignature();
    doc.DocumentElement.RemoveChild(elem);
    doc.DocumentElement.AppendChild(signedXml.GetXml());
    Console.WriteLine("Stage 2 signed");
    doc.WriteTo(prettyOut);
}
Sign up to request clarification or add additional context in comments.

6 Comments

Now it works. But I need the Signature to be located in /DataPDU/AppHdr/Sgntr tag. Would this alter the result of the signature?
No, because the Uri="" reference uses the Enveloped transform the <ds:Signature> element can be moved around.
I'm working on a enveloping signature and I have to create a signature of xml with keyInfo reference but even with above solution I'm getting "Malformed Reference error." any IDea will be appreciated.
@bartonjs If you try to CheckSignature is always false, did you try?
@DharaVaghasiya I tryed this code and worked for the enveloped, but the method CheckSignature is always false. Does this help? stackoverflow.com/questions/9211685/…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.