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"); } } } } } }
You are welcome :-)
Thanks Anton, I just had to change signedXml.SigningKey = certificate.PrivateKey; for signedXml.SigningKey = certificate.GetRSAPrivateKey(); because it seems is this way for .Net 4.7.1 and above.
Have a look at this article, it says SHA-256 isn't supported by X509Certificate2 and the article describes the same error as you "invalid algorithm used"
https://stackoverflow.com/questions/29005876/signedxml-compute-signature-with-sha256
Main message:
System.Security.Cryptography.CryptographicException: 'Specified algorithm is invalid.
Full details:
System.Security.Cryptography.CryptographicException
HResult=0x80090008
Message = Specified algorithm is invalid.
Origin = mscorlib
Stack trace:
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
at System.Security.Cryptography.AsymmetricSignatureFormatter.CreateSignature(HashAlgorithm hash)
at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()
at FirmaElectronica.Program.Main(String[] args) in C:\Users\......\Program.cs:line 85
What is the type of the exception and the exact error message?
André Arnaud de Cal...
292,162
Super User 2025 Season 1
Martin Dráb
230,962
Most Valuable Professional
nmaenpaa
101,156