Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sign an XML file with a RSA key in .NET?

I am trying to sign an XML file in C# .NET 3.5 with a private RSA Key generated by OpenSSL.

Here is how I proceeded: I converted the RSA key from PEM format to XML format using the chilkat framework (www.example-code.com/csharp/cert_usePrivateKeyFromPEM.asp)

With my XML key, I am now able to use native .NET Functions, which I prefer. So I used the methods described on MSDN.

So, in the end, my source code looks like this:

RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider();

//Load the private key from xml file
XmlDocument xmlPrivateKey = new XmlDocument();
xmlPrivateKey.Load("PrivateKey.xml");
rsaProvider.FromXmlString(xmlPrivateKey.InnerXml);

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

 // Add the key to the SignedXml document.
 signedXml.SigningKey = Key;

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

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

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

 // Append the element to the XML document.
 Doc.DocumentElement.AppendChild(Doc.ImportNode(xmlDigitalSignature, true));

The Signed XML I get with this function looks OK, I have the XML element at the end of the file, like it is supposed to be:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
  <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
  <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>qoGPSbe4oR9e2XKN6MzP+7XlXYI=</DigestValue>
  </Reference>
</SignedInfo>
<SignatureValue>iPQ6IET400CXfchWJcP22p2gK6RpEc9mkSgfoA94fL5UM6+AB5+IO6BbjsNt31q6MB8hR6lAIcnjzHzc5SeXvFP8Py2bqHTYJvcSA6KcKCQl1LiDNt12UwWiKpSkus2p0LdAeeZJNy9aDxjC/blUaZEr4uPFt0kGCD7h1NQM2SY=</SignatureValue>

The problem is that when I try to verify the signature using xmlsec at this URL: http://www.aleksey.com/xmlsec/xmldsig-verifier.html. I get a message telling me the signature is invalid.

I have been looking for the error in my code for days and I can't find out. I am beginning to think that the conversion from PEM to XML file might be the problem but I don't know how to test this. Moreover, I did not find any other way to convert to key or to use directly the PEM file in .NET.

Did anyone manage to get a valid signature in .NET?

like image 584
Nicolas Riou Avatar asked Dec 01 '09 09:12

Nicolas Riou


People also ask

How XML digital signature is used for security?

An XML digital signature (XML DSIG) is an electronic, encrypted, stamp of authentication on digital information such as messages. The digital signature confirms that the information originated from the signer and was not altered in transmission.

Can XML signature be applied to the full document?

XML Signatures can be applied to any digital content (data object), including XML. An XML Signature may be applied to the content of one or more resources. Enveloped or enveloping signatures are over data within the same XML document as the signature; detached signatures are over data external to the signature element.


1 Answers

Yes, I have managed to do that. I think the problem is with your reference. The uri should point to the id of the element that the signature is for. Anyway, check the below code, hope it points you in the right direction.

/Klaus

/// <summary>
    /// Signs an XmlDocument with an xml signature using the signing certificate given as argument to the method.
    /// </summary>
    /// <param name="doc">The XmlDocument to be signed</param>
    /// <param name="id">The is of the topmost element in the xmldocument</param>
    /// <param name="cert">The certificate used to sign the document</param>
    public static void SignDocument(XmlDocument doc, string id, X509Certificate2 cert)
    {
        SignedXml signedXml = new SignedXml(doc);
        signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
        signedXml.SigningKey = cert.PrivateKey;

        // Retrieve the value of the "ID" attribute on the root assertion element.
        Reference reference = new Reference("#" + id);

        reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
        reference.AddTransform(new XmlDsigExcC14NTransform());

        signedXml.AddReference(reference);

        // Include the public key of the certificate in the assertion.
        signedXml.KeyInfo = new KeyInfo();
        signedXml.KeyInfo.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.WholeChain));

        signedXml.ComputeSignature();
        // Append the computed signature. The signature must be placed as the sibling of the Issuer element.
        XmlNodeList nodes = doc.DocumentElement.GetElementsByTagName("Issuer", Saml20Constants.ASSERTION);
        // doc.DocumentElement.InsertAfter(doc.ImportNode(signedXml.GetXml(), true), nodes[0]);            
        nodes[0].ParentNode.InsertAfter(doc.ImportNode(signedXml.GetXml(), true), nodes[0]);
    }
like image 92
Klaus Byskov Pedersen Avatar answered Sep 30 '22 00:09

Klaus Byskov Pedersen