Skip to main content

Notifications

Dynamics 365 Community / Forums / Finance forum / Problem signing XML th...
Finance forum
Answered

Problem signing XML that would later be use in a D365FO project

Posted on by 148

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.

  • Anton Venter Profile Picture
    Anton Venter 6,730 Super User on at
    RE: Problem signing XML that would later be use in a D365FO project

    You are welcome :-)

  • Daniel Mora Profile Picture
    Daniel Mora 148 on at
    RE: Problem signing XML that would later be use in a D365FO project

    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.

  • Verified answer
    Anton Venter Profile Picture
    Anton Venter 6,730 Super User on at
    RE: Problem signing XML that would later be use in a D365FO project

    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

  • Daniel Mora Profile Picture
    Daniel Mora 148 on at
    RE: Problem signing XML that would later be use in a D365FO project

    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

  • Martin Dráb Profile Picture
    Martin Dráb 225,425 Super User on at
    RE: Problem signing XML that would later be use in a D365FO project

    What is the type of the exception and the exact error message?

Helpful resources

Quick Links

Community Spotlight of the Month

Kudos to Mohamed Amine Mahmoudi!

Blog subscriptions now enabled!

Follow your favorite blogs

TechTalk: How Dataverse and Microsoft Fabric powers ...

Explore the latest advancements in data export and integration within ...

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 284,876 Super User

#2
Martin Dráb Profile Picture

Martin Dráb 225,425 Super User

#3
nmaenpaa Profile Picture

nmaenpaa 101,146

Leaderboard

Featured topics

Product updates

Dynamics 365 release plans