Reputation: 73
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?
Upvotes: 5
Views: 17895
Reputation: 11
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
Upvotes: 1
Reputation: 71
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.
Upvotes: 1
Reputation: 215
Try to replace
dataObject.Data = Doc.ChildNodes;
with this:
dataObject.Data = Doc.GetElementsByTagName("YourRootNodeNameHere");
Upvotes: 1
Reputation: 1871
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.
Upvotes: 1
Reputation: 120917
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]);
}
Upvotes: 3