Can I sign XML using public key? - c#

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;

Related

X509Certificate2 Private Key

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.

XAdES SignedXml.CheckSignature() always returns false

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.

How to add prefix to Signature element in C#

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.

Sign XML with ECDsa and SHA256 in .Net

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.

Verifying XMLSignature signed with ECDSA (with SHA256) in C# using BouncyCastle throws InvalidCastException

I have to verify an XmlSignature that was signed using the http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256 algorithm. As this algorithm is not supported using the native .NET SignedXml class I've implemented the check using BouncyCastle.
My implementation works as follows:
// read certificate
var bytes = Convert.FromBase64String("...");
var cert = new X509CertificateParser().ReadCertificate(bytes);
var ecPublicKeyParameters = (ECPublicKeyParameters)cert.GetPublicKey();
// load signed XmlDocument
var xDoc = new XmlDocument();
xDoc.Load("Response_Success.xml");
// get signature value
var nav = xDoc.CreateNavigator();
nav.MoveToFollowing("SignatureValue", "http://www.w3.org/2000/09/xmldsig#");
var signatureAsString = Regex.Replace(nav.InnerXml.Trim(), #"\s", "");
var signatureValue = Convert.FromBase64String(signatureAsString);
// get and canonicalize signed info
var signedInfo = xDoc.GetElementsByTagName("SignedInfo", "http://www.w3.org/2000/09/xmldsig#")[0];
// move used NS from the document root element to the SignedInfo element
var ns = RetrieveNameSpaces((XmlElement)signedInfo);
InsertNamespacesIntoElement(ns, (XmlElement)signedInfo);
// apply an XmlDsigC14NTransformation
var signedInfoStream = canonicalizeNode(signedInfo);
// hash signed info
var hashAlgorithm = SHA256.Create();
var hashedSignedInfo = hashAlgorithm.ComputeHash(signedInfoStream);
// check signature
var signer = SignerUtilities.GetSigner("ECDSA");
signer.Init(false, ecPublicKeyParameters);
signer.BlockUpdate(hashedSignedInfo, 0, hashedSignedInfo.Length);
var isSignatureValid = signer.VerifySignature(signatureValue);
The error occurs in the very last statment and reads
System.InvalidCastException: Unable to cast object of type 'Org.BouncyCastle.Asn1.DerApplicationSpecific' to type 'Org.BouncyCastle.Asn1.Asn1Sequence'.
As the XmlSignature is most probably valid (created by an officially recognized association using a Java application) I'm pretty sure the mistake is in the preceding code block.
Can anyone give me a hint how to proceed?
Thanks,
Philipp
Is you encoding right? You might want to have a look at the following:
Digital Signature Verification using BouncyCastle - ECDSA with SHA 256, C#

Categories