Question:
I am developing an application which is responsible for generating xml documents and it signs them with a simple Xades Enveloped signature,
The process I have to sign is as follows:
- I generate the xml file.
- Change to utf-8 the file.
- I sign this file, it is saved as utf-8 with boom (or something like that)
- I convert that signed file to utf-8
So far the code I use to sign the documents is this
private static void FirmarDocumento(string pathXmlDocument, string pathCert, string passCert, string pathXmlSignet)
{
XmlDocument documentXml = new XmlDocument();
documentXml.Load(pathXmlDocument);
SignedXml firmado = new SignedXml(documentXml);
var cert = RSA_helper.GetX509Certificate(pathCert, passCert);
firmado.SigningKey = (RSA)cert.PrivateKey;
firmado.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
//digest info
Reference reference = new Reference();
reference.Uri = "#Documento101";
reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
firmado.AddReference(reference);
// with the public key will be added in the signature part.
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new RSAKeyValue((RSA)cert.PrivateKey));
keyInfo.AddClause(new KeyInfoX509Data(cert));
firmado.KeyInfo = keyInfo;
firmado.ComputeSignature();
XmlElement xmlDigitalSignature = firmado.GetXml();
// buscamos el ultimo elemento del documento para agregarle la firma
XmlElement elemento = (XmlElement)documentXml.SelectSingleNode(@"//Document/Content/adu/TmstFirma[last()]");
XmlNode parent = elemento.ParentNode;
parent.InsertAfter(xmlDigitalSignature, elemento);
documentXml.Save(pathXmlSignet);
}
Once the file is generated, I pass it through a function that is in charge of leaving it in UFT-8 (they are asking me like this):
private static void ConvertirUTF8(String sourcePath)
{
File.SetAttributes(sourcePath, FileAttributes.Normal);
var sinUtf8Bom = new System.Text.UTF8Encoding(false);
var contenido = File.ReadAllLines(sourcePath);
File.SetAttributes(sourcePath, FileAttributes.Normal);
System.IO.File.WriteAllLines(sourcePath, contenido, sinUtf8Bom);
}
If I check the encoding I get UTF-8, but if I later want to validate the file with its signature, it is not passing it.
I think it's because since I've already generated the file, I'm manipulating it later by converting it to utf-8, and when I want to validate the signature I get rejected.
What do I need in my SignarDocumento function so that the files that I sign come out in UTF-8? so I could remove this function to convert. I'm looking but so far I haven't found anything.
Someone knows?
Thank you so much
Answer:
So it soon occurs to me that you convert your file to the encoding you want before signing it.
In general, the document signing process generally serves (among other things) to ensure that the document has not been altered after signing, and changing its encoding is an alteration.
Therefore I would try, taking your same code:
private static void FirmarDocumento(string pathXmlDocument, string pathCert, string passCert, string pathXmlSignet)
{
XmlDocument documentXml = new XmlDocument();
ConvertirUTF8(String sourcePath) //Conviertelo antes
documentXml.Load(pathXmlDocument);
SignedXml firmado = new SignedXml(documentXml);
...
I have had that same requirement from a client and when I save the file for the first time I do so without the BOM. The code I use is:
string rutaDelDoc = @"C:\temp\datos.xml";
string XML = "contenido del XML, yo lo obtengo serializando un objeto";
// y así lo guardo
Encoding UTF8SinBOM = new UTF8Encoding(false);
File.WriteAllText(rutaDelDoc, XML, UTF8SinBOM);
...
Additionally in English SO, they propose another way to do it, with an XmlTextWriter
, I ` link to the question and the relevant code of the answer is:
var doc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("root", new XAttribute("note", "boogers"))
);
using (var writer = new XmlTextWriter(".\\ejemplo.xml", new UTF8Encoding(false)))
{
doc.Save(writer);
}
If you also want to indicate the encoding within the XML file Checking the MS documentation on XmlDocument
here I see that there is a property XmlDeclaration.Encoding
The XmlDeclaration of the current XmlDocument object determines the encoding attribute in the saved document. The value of the encoding attribute is taken from the XmlDeclaration.Encoding property. If the XmlDocument does not have an XmlDeclaration, or if the XmlDeclaration does not have an encoding attribute, the saved document will not have one either.
Basically what the documentation says is that when generating the XML, you also generate its declaration. In any case, the latter only serves to put the encoding inside the XML file, but I think it does not force it to be recorded on the disk in a certain way (in fact, you could not indicate whether or not to use BOM).