English 中文(简体)
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?

问题回答

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

It seems to me, that you sign the everrything in the xml:reference.Uri = "";

Purhaps you break the signature when you insert it in the document or insert it in a wrong way.

Also be aware of namespaces and whitespace some of it is also signed which can cause problems later on when working on the signed document.

Try to replace

dataObject.Data = Doc.ChildNodes;

with this:

dataObject.Data = Doc.GetElementsByTagName("YourRootNodeNameHere");

I am having the same problem today, one of my co-worker suspects that it s maybe because .net xml C14 transform doesn t follow the XML standard which is implemented in xmlsec. I will try to do the transform on my own and let you know if that s really the issue.

I had the same problem when verifying the signed XML with XML-Signer. It got solved when I added: doc.PreserveWhitespace = False before loading the XML Document to be signed

A good example can be found here: http://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.signature(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签