I'm trying to use SignedXml in .Net with ECDsa (ECDsaCng) and SHA256 and I'm getting an "CryptographicException" with message "Failed to create signing key.". Does someone knows how to achieve that goal or if I should know relevant information about .net support for ecdsa? thank you in advance.
public XmlDocument SignXml(XmlDocument xmlDoc)
{
CngKey cngKey = CngKey.Create(CngAlgorithm.ECDsaP256);
ecDsaCng = new ECDsaCng(cngKey);
ecDsaCng.HashAlgorithm = CngAlgorithm.ECDsaP256;
ecDsaCng.KeySize = 256;
xmlDoc.PreserveWhitespace = true;
SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = ecDsaCng;
Reference reference = new Reference();
reference.Uri = "";
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
signedXml.AddReference(reference);
signedXml.ComputeSignature();
XmlElement xmlDigitalSignature = signedXml.GetXml();
xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
return xmlDoc;
}
Error appears in "signedXml.ComputeSignature();" method call.
Have a look in the assembly
[System.Security, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a]
in the method
public void ComputeSignature()
Only RSA and DSA keys are accepted. It is a shame that it isn't specified somewhere on the MSDN. You will have to pass on the SignedXml class.
Related
I'm trying to sign an envelope using a local machine certificate, when I send the request it's ignored "for invalid signature".
I have an old code that is working just fine on .Net Framework using X509Certificate (Microsoft.Web.Services.Security.X509)
X509Certificate cert = this.GetX509Certificate;
SignedXml signedXml = new SignedXml(envelope);
signedXml.SigningKey = cert.Key; // This is working
Now I need to re-write the whole thing in .Net Core, and I'm using X509Certificate2 (System.Security.Cryptography.X509Certificates)
the cert.Key doesn't exist. So I'm using the following:
X509Certificate2 cert = this.GetX509Certificate;
SignedXml signedXml = new SignedXml(envelope);
Reference bodyReference = new Reference();
bodyReference.Uri = "#Body";
bodyReference.DigestMethod = SignedXml.XmlDsigSHA1Url;
XmlDsigExcC14NTransform trans = new XmlDsigExcC14NTransform();
bodyReference.AddTransform(trans);
signedXml.AddReference(bodyReference);
signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
signedXml.SigningKey = cert.GetRSAPrivateKey() // This is not working
Could the CspKeyContainerInfo difference be the issue here? and if so how can I change that?
I've also re-set the values for DigestMethod, SignatureMethod and CanonicalizationMethod to match the methods under the working code in .Net Framework.
I'm trying to sign an XML using a public key. I'm loading the certificate from a base64 string, provided by the SP. The certificate loads fine, and the public key seems to have information in it, but when I try to use it, I get the following error:
Keyset does not exist
Here's the actual code... any suggestions?? Thanks a lot!
var signingCertificateX509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(Convert.FromBase64String(base64EncryptingCertificate), "password", X509KeyStorageFlags.PersistKeySet);
SignedXml signedXml = new SignedXml(doc);
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
signedXml.SigningKey = signingCertificateX509.PublicKey.Key;
I have used below methods from MSDN for signing and verifying an XML file.
The problem is that I am not able to verify the signed XML file. The SignedXML.CheckSignature() method always returns false, and not even throwing any exception on what went wrong.
Method used to sign XML file
public static void SignXMLFile(XmlDocument xmlDoc, string XmlSigFileName)
{
// Create a SignedXml object.
SignedXml signedXml = new SignedXml(xmlDoc);
string certPath = #"path to PFX file.pfx";
X509Certificate2 cert = new X509Certificate2(certPath, "password", X509KeyStorageFlags.Exportable);
var exportedKeyMaterial = cert.PrivateKey.ToXmlString(true);
var Key = new RSACryptoServiceProvider(new CspParameters(24));
Key.PersistKeyInCsp = false;
Key.FromXmlString(exportedKeyMaterial);
// Assign the key to the SignedXml object.
signedXml.SigningKey = Key;
//// Create a reference to be signed.
//Reference reference = new Reference(System.IO.File.Open(#"D:\test.docx",System.IO.FileMode.Open));
//// Add the passed URI to the reference object.
//reference.Uri = URIString;
//// Add the reference to the SignedXml object.
//signedXml.AddReference(reference);
// Create a reference to be signed.
Reference reference = new Reference();
// Add the passed URI to the reference object.
reference.Uri = "";
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
//Save the public key into the KeyValue node of the Signature
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new RSAKeyValue(Key));
signedXml.KeyInfo = keyInfo;
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Save the signed XML document to a file specified
//using the passed string.
XmlTextWriter xmltw = new XmlTextWriter(XmlSigFileName, new UTF8Encoding(false));
xmlDigitalSignature.WriteTo(xmltw);
xmltw.Close();
}
Method used to Verify signature of a XML file
// Verify the signature of an XML file and return the result.
public static Boolean VerifyXmlFile(String Name)
{
// Check the arguments.
if (Name == null)
throw new ArgumentNullException("Name");
// Create a new XML document.
XmlDocument xmlDocument = new XmlDocument();
// Format using white spaces.
xmlDocument.PreserveWhitespace = true;
// Load the passed XML file into the document.
xmlDocument.Load(Name);
// Create a new SignedXml object and pass it
// the XML document class.
SignedXml signedXml = new SignedXml(xmlDocument);
// Find the "Signature" node and create a new
// XmlNodeList object.
XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature");
// Load the signature node.
signedXml.LoadXml((XmlElement)nodeList[0]);
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigBase64TransformUrl;
X509Certificate2 cert = new X509Certificate2(#"path to PFX file.pfx", "password");
// Check the signature and return the result.
return signedXml.CheckSignature(cert, true);
}
I tried with all the suggesstions from stackoverflow, but no luck. Any help here is much appreciated. Thanks.
Based on data from the comments, your problem is you were trying to use an external (detached) signature, despite the code that you showed using a Uri of "", which represents "this entire document".
In the Remarks section for SignedXml on MSDN we get a couple of gems:
There is also a fourth kind of signature called an external detached signature which is when the data and signature are in separate XML documents. External detached signatures are not supported by the SignedXml class.
...
The URI attribute of the <Reference> element
...
Setting the URI attribute to an empty string indicates that the root element of the document is being signed, a form of enveloped signature.
...
Anything else is considered an external resource detached signature and is not supported by the SignedXml class.
And then there's an entire section called "The problem with external references". That section gives a link to After you apply security update 3141780, .NET Framework applications encounter exception errors or unexpected failures while processing files that contain SignedXml.
That link talks about a registry key (which I'm not going to post here, feel free to follow it) which says how to opt out of that particular portion of the MS16-035 security fixes. It also carries with it a note:
Warning Enabling this registry key could allow security vulnerabilities including Denial of Service, Distributed Reflection Denial of Service, Information Disclosure, Signature Bypass, and Remote Code Execution.
So, if you need to do it... be careful.
We are signing a xml elements using SignedXml class. The requirement is add signature node in Ws Security to communicate with the service.
We could able to sign the elements and verified those. In this case the xml Sample code is as below
X509Certificate2 certificate = new X509Certificate2(CertPath, CertPassword);
// Create a new XML document.
XmlDocument doc = new XmlDocument();
// Format the document to ignore white spaces.
doc.PreserveWhitespace = false;
// Load the passed XML file using it's name.
doc.Load(new XmlTextReader(FileName));
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = certificate.PrivateKey;
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "#" + elementID;
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform envTransform = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(envTransform);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Create a new KeyInfo object.
KeyInfo keyInfo = new KeyInfo();
// Load the certificate into a KeyInfoX509Data object
// and add it to the KeyInfo object.
keyInfo.AddClause(new KeyInfoX509Data(certificate));
// Add the KeyInfo object to the SignedXml object.
signedXml.KeyInfo = keyInfo;
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
by using this Signature element generated as <signature> .... </signature> . but we want to generate it as <ds:signature> .... </ds:signature>
Tried to set the prefix explicitly but then signature is not validated after.
Could you please guide how can we achieve this?
As this is a generic enough question, this post might be helpful to someone looking for the same information.
For the specific project I'm working on, IRS A2A submission, it is what I used when thinking that I needed to add the prefixes to the Signature element. However, in discussion with some other SO users, I have abandoned the idea of adding the prefixes to the Signature element as it is not actually necessary.
I'm trying to sign Xml (actually SOAP xml) in C# using the SignedXml class, the signing stage passes successfully, but when I try to verify the signature it tells me that it is not valid.
The only change I have done from the example in MSDN that I used XmlDsigExcC14NTransform instead of the transform XmlDsigEnvelopedSignatureTransform.
If I use XmlDsigEnvelopedSignatureTransform I will get a valid signature.
Here is my signing code:
private static XmlDocument SignXml(XmlDocument doc)
{
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = Certificate.PrivateKey;
Reference reference = new Reference();
reference.Uri = "";
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
//XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform();
reference.AddTransform(env);
signedXml.AddReference(reference);
signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();
doc.DocumentElement.AppendChild(signature);
doc.Save(SignedXmlPath);
return doc;
}
The above code will give me a valid signature, but if I use
XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform();
instead of
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
I will get an invalid signature.
Here is my verification code:
private static bool Verify(XmlDocument doc)
{
SignedXml signedDoc = new SignedXml(doc);
XmlNodeList nodeList = doc.GetElementsByTagName(Constants.SignatureElement);
signedDoc.LoadXml((XmlElement)nodeList[0]);
return signedDoc.CheckSignature((RSA)Certificate.PublicKey.Key);
}
Can anyone tell me how can I sign with transform algorithm of http://www.w3.org/2001/10/xml-exc-c14n#
Thanks in advance.
You are gonna need the XmlDsigEnvelopedSignatureTransform in your case because you are adding the signature inside the element you are signing.
XmlDsigEnvelopedSignatureTransform will tell the SignedXml class to remove the signature from the signature node itself before testing it's validity. This is needed because you added that element after calculating the signature.
You can add more then one transform by calling the AddTransform again like this:
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
XmlDsigExcC14NTransform c14n = new XmlDsigExcC14NTransform();
reference.AddTransform(env);
reference.AddTransform(c14n);
However i think what you actually want to do instead of my example above is set the CanonicalizationMethod to c14n:
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
- or -
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;