Hi,
I'm building code in C# that later I will integrate it in a D365FO project. This code needs to sign an XMLDocument with a valid certificate I have and then later I have to send it to a web service. The problem is that when I get to this line: signedXml.ComputeSignature(); There is an error message that says that the algorithm use is not valid. The algorithm that I use is RSA-SHA256 and I already checked with windows certificates and indeed that algorithm is part of the certificate properties.
Here is the full code (I hid some of the urls and file info):
using System; using System.Text; using System.Xml; using Console = System.Console; using System.IO; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; using System.Security.Cryptography.Xml; namespace FirmaElectronica { class Program { static void Main(string[] args) { // Definimos los parámetros de la petición string urlServicio = "https://url.com"; string archivoXML = "C:\\Users\\...\\file.xml"; string claveAcceso = "0000000000000000000000000000000000000000000000000"; string rutaArchivoConfiguracion = "C:\\Users\\....\\file.txt"; string clave = ""; // Método para guardar clave en la variable if (File.Exists(rutaArchivoConfiguracion)) { string[] lineas = File.ReadAllLines(rutaArchivoConfiguracion); foreach (string linea in lineas) { if (linea.StartsWith("clave=")) { clave = linea.Substring(6); break; } } } if (string.IsNullOrEmpty(clave)) { throw new Exception("No se encontró la clave en el archivo de configuración."); } // Leemos el archivo XML string xml = System.IO.File.ReadAllText(archivoXML, Encoding.UTF8); // Convertimos la cadena a un objeto XmlDocument XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); // Leemos el certificado desde el archivo .p12 y proporcionamos la clave X509Certificate2 certificate = new X509Certificate2("C:\\Users\\....\\file.p12", clave, X509KeyStorageFlags.Exportable); // Creamos el objeto SignedXml y lo configuramos SignedXml signedXml = new SignedXml(xmlDoc); signedXml.SigningKey = certificate.PrivateKey; signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; // Creamos una referencia al objeto SignedXml Reference reference = new Reference(); reference.Uri = ""; // Agregamos el transform para la referencia XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(env); // Agregamos la referencia al objeto SignedXml signedXml.AddReference(reference); // Creamos el objeto KeyInfo y lo configuramos KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new KeyInfoX509Data(certificate)); // Agregamos el objeto KeyInfo al objeto SignedXml signedXml.KeyInfo = keyInfo; // Especificamos el algoritmo de firma signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA256Url; // Firmamos el XML signedXml.ComputeSignature(); //HERE IT STOPS // Obtenemos la representación del XML firmado como objeto XmlElement XmlElement xmlDigitalSignature = signedXml.GetXml(); // Agregamos el elemento firmado al XML original xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true)); // Convertimos el XML firmado a cadena string xmlFirmado = xmlDoc.OuterXml; // Armamos el cuerpo del mensaje SOAP string cuerpoMensaje = "" "" "" "" "" claveAcceso "" "" xmlFirmado "" "" "" ""; // Creamos la petición HTTP HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlServicio); request.Method = "POST"; request.ContentType = "text/xml;charset=UTF-8"; request.Headers.Add("SOAPAction", ""); // Establecemos el tamaño del contenido que se va a enviar en la petición request.ContentLength = cuerpoMensaje.Length; // Escribimos el cuerpo del mensaje SOAP en el stream de la petición using (Stream stream = request.GetRequestStream()) { using (StreamWriter sw = new StreamWriter(stream)) { sw.Write(cuerpoMensaje); } } // Obtenemos la respuesta del servicio web using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { // Leemos la respuesta using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) { string responseXml = reader.ReadToEnd(); // Parseamos la respuesta como XML XmlDocument xmlDocResponse = new XmlDocument(); xmlDocResponse.LoadXml(responseXml); // Extraemos el estado de la autorización string estado = xmlDocResponse.GetElementsByTagName("estado")[0].InnerText; if (estado == "AUTORIZADO") { // El comprobante fue autorizado Console.WriteLine("El comprobante fue autorizado"); } else { // El comprobante fue rechazado o está en proceso Console.WriteLine("El comprobante fue rechazado o está en proceso"); } } } } } }
Thanks in advance.