简体   繁体   中英

Create Digital Signature for XML with reference to KeyInfo

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();
}

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM