c # Consume web service electronic invoicing

3

I am something new in this consume webservices , I have to send a file to a web service and receive an answer, my problem is that I could not generate a SOAP Header valid.

The web service to consume is this:

https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica/facturaElectronica.wsdl

the specifications in the document are that the header should look like this:

Sample request using Base64

POST /B2BIntegrationEngine/FacturaElectronica HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
Content-Length: 3342
Host: 192.168.250.65:9080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rep="http://www.dian.gov.co/servicios/facturaelectronica/ReportarFactura">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>8ac82326-3016-430f-8d69-9efc4bcefd8f</wsse:Username>
<wsse:Password>6361b7b5322acb07ced00a35a85a4cc5183da3a42ede0b07f578067a18425a55</wsse:Password>
<wsse:NonceEncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FmbZRkx1jh2A+imgjD2fLQ==</wsse:Nonce>
<wsu:Created>2015-10-06T12:00:33.762Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>

But as much as I try, the only thing I could get is this:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken u:Id="uuid-c635f931-8274-4d3d-82a1-460a2f04af73-1">
<o:Username>22ffb485-64bc-4619-8a63-340de2dd7eec</o:Username>
<o:Password>+eho+ADhrJkwZtKGabXIZOEX/0YVa206TmKnQSwt+qE=</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>

This is what I am doing:

const string apiUrl = "https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica",
        apiUserName = "8ac82326-3016-430f-8d69-9efc4bcefd8f";

        string apiPassword = "6361b7b5322acb07ced00a35a85a4cc5183da3a42ede0b07f578067a18425a55";

        EndpointAddress endpointAddress = new EndpointAddress(new Uri(apiUrl));

        SecurityBindingElement securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
        securityElement.AllowInsecureTransport = false;
        securityElement.EnableUnsecuredResponse = true;
        securityElement.IncludeTimestamp = false;

        TextMessageEncodingBindingElement encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
        HttpsTransportBindingElement transportElement = new HttpsTransportBindingElement();

        CustomBinding binding = new CustomBinding(securityElement, encodingElement, transportElement);

        //basic prueba
        BasicHttpSecurityMode securityMode = new BasicHttpSecurityMode();
        BasicHttpBinding binding2 = new BasicHttpBinding(securityMode);

        // Then assign username and password based on the proxy. For example -
        //var remoteAddress = new EndpointAddress("");
        WSDian.facturaElectronicaPortNameClient service = new WSDian.facturaElectronicaPortNameClient(binding, endpointAddress);
        //WSDian.facturaElectronicaPortNameClient service = new WSDian.facturaElectronicaPortNameClient(binding2, endpointAddress);
        service.ClientCredentials.UserName.UserName = apiUserName;
        service.ClientCredentials.UserName.Password = apiPassword;
        var Obtnonce = GetNonce();
        byte[] archivo = FileToByteArray(@"c:\temp\ws_f0816002834000000000A.zip");

        service.Open();

        WSDian.EnvioFacturaElectronica enviofactura = new WSDian.EnvioFacturaElectronica();

        enviofactura.NIT = "816002834";
        enviofactura.InvoiceNumber = "10";
        enviofactura.IssueDate = Convert.ToDateTime("2018-01-01 05:00:00");
        enviofactura.Document = archivo;

        //temporal

        WSDian.EnvioFacturaElectronicaPeticion envioFacturaElectronicaPeticion = new WSDian.EnvioFacturaElectronicaPeticion();
        WSDian.EnvioFacturaElectronicaRespuesta envioFacturaElectronicaRespuesta = new WSDian.EnvioFacturaElectronicaRespuesta();
        WSDian.AcuseRecibo acuseRecibo = new WSDian.AcuseRecibo();
        WSDian.ReceivedInvoice receivedInvoice = new WSDian.ReceivedInvoice();
        WSDian.facturaElectronicaPortNameClient facturaElectronicaPortNameClient = new WSDian.facturaElectronicaPortNameClient();           

        envioFacturaElectronicaPeticion.EnvioFacturaElectronicaPeticion1 = enviofactura;
        var versionSoap = acuseRecibo.GetType();
        acuseRecibo = service.EnvioFacturaElectronica(envioFacturaElectronicaPeticion.EnvioFacturaElectronicaPeticion1);

But I'm getting the error:

  

The security token could not be authenticated or authorized; nested exception is org.apache.ws.security.WSSecurityException: The security token could not be authenticated or authorized

I do not understand how to generate the SOAP header as requested, should not the wsdl generate it automatically?

Thanks for your help.

I add, this is my Web.config

?xml version="1.0" encoding="utf-8"?>
<configuration>

 <system.diagnostics>

    <trace autoflush="true" />

    <sources>
      <source name="System.Net" >
        <listeners>
          <add name="MyTraceFile"/>
          <add name="MyConsole"/>
        </listeners>
      </source>
    </sources>

    <sharedListeners>
      <add
        name="MyTraceFile"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="System.Net.trace.log" />
      <add name="MyConsole" type="System.Diagnostics.ConsoleTraceListener" />
    </sharedListeners>

    <switches>
      <add name="System.Net" value="Verbose" />
    </switches>

  </system.diagnostics> 

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.6" />
    <httpRuntime targetFramework="4.6"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="facturaElectronicaPortNameSoap11">
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://facturaelectronica.dian.gov.co:80/habilitacion/B2BIntegrationEngine/FacturaElectronica"
        binding="basicHttpBinding" bindingConfiguration="facturaElectronicaPortNameSoap11"
        contract="WSDian.facturaElectronicaPortName" name="facturaElectronicaPortNameSoap11" />
    </client>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- Para evitar revelar información de los metadatos, establezca los valores siguientes en false antes de la implementación -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- Para recibir detalles de las excepciones en los fallos, con el fin de poder realizar la depuración, establezca el valor siguiente en true. Para no revelar información sobre las excepciones, establézcalo en false antes de la implementación -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        Para examinar el directorio raíz de la aplicación web durante la depuración, establezca el valor siguiente en true.
        Establézcalo en false antes de la implementación para evitar revelar información sobre la carpeta de aplicación web.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

I update:

Perform tests with SoapUI successfully and I realized that:

  • The user is fine, is the unique identifier of the software provided by the DIAN
  • The password is the result of using SHA-256 on the password that was configured on the DIAN site.
  • Now, the biggest problem is generating the <soapenv:Header> with the <wsse:UsernameToken> that includes the <wsse:Nonce> and <wsu:Created>

    Update

    After much searching, I found this article with which I managed to get closer to the generation of <soapenv:Header> :

    link

    Now my Header looks like this:

    <s:Envelope xmln s:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <s:Header>
    <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <wsse:UsernameToken><wsse:Username><!--Removed--></wsse:Username>
    <wsse:Password><!--Removed--></wsse:Password>
    <wsse: Nonce>t21C05VjRqomc+OehZoSzwqhmaM=</wsse:Nonce>
    <wsse:Created>2018-08-28T08:03:40.749Z</wsse:Created>
    </wsse:UsernameToken>
    </o:Security>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <EnvioFacturaElectronicaPeticion xmlns="http://www.dian.gov.co/servicios/facturaelectronica/ReportarFactura">
    <NIT><!--Removed--></NIT>
    <InvoiceNumber>980000000</InvoiceNumber>
    <IssueDate>2018-08-28T19:00:00</IssueDate>
    <Document>UEsDBBQAAAAIAPBkHE3djKx2hwgAAEcdAAAeAAAAZmFjZV9mMDgxNjAwMjgzNDAwM0E2OTl.........

    Now my problem is that it generates the following error:

      

    System.Net Information: 0: [11736] Connection # 33583636 - Received status line: Version = 1.1, StatusCode = 400, StatusDescription = Bad Request.

    System.Net Error: 0: [11736] Exception in HttpWebRequest # 36620214 :: GetResponse - Error in the remote server: (400) Incorrect request ..

    Any ideas?

        
    asked by Felipe Taborda 25.08.2018 в 02:49
    source

    2 answers

    2

    Finally I managed to connect to the DIAN service and get the successful answer:

    ...<ns2:ResponseDateTime>2018-09-05T11:37:47.766-05:00</ns2:ResponseDateTime><ns2:Response>200</ns2:Response><ns2:Comments>Ejemplar recibido exitosamente pasará a verificación.</ns2:Comments></ns2:EnvioFacturaElectronicaRespuesta></SOAP-ENV:Body></SOAP-ENV:Envelope>

    I found that:

  • The user to connect is the id of the software that delivers the DIAN when registering
  • The password is the one that was configured when registering and in SHA-256
  • The nonce is not needed
  • The class to make the shipment is as follows:

    [WebMethod]
    	public string envioFacturaElectronicaSOAP()
    	{
    		const string apiUrl = "https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica";
    		byte[] archivo = FileToByteArray(@"c:\temp\ws_f0890900161000000dab3.zip");
    		EndpointAddress endpointAddress = new EndpointAddress(new Uri(apiUrl));
    		var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
    		securityElement.AllowInsecureTransport = false;
    		securityElement.EnableUnsecuredResponse = true;
    		securityElement.IncludeTimestamp = false;
    
    		var encodingElement = new //MtomMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
    								  TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
    
    		var transportElement = new HttpsTransportBindingElement();
    		var binding = new CustomBinding(securityElement, encodingElement, transportElement);
    		
    		facturaElectronicaPortNameClient Service = new facturaElectronicaPortNameClient(binding, endpointAddress);
    		Service.ClientCredentials.UserName.UserName = "MiSoftwareID";
    		Service.ClientCredentials.UserName.Password = "MiContraseñaEnSHA256";
    		
    		WSDian.EnvioFacturaElectronica enviofactura = new WSDian.EnvioFacturaElectronica();
    		enviofactura.NIT = "MiNIT";
    		enviofactura.InvoiceNumber = "MiNumeroDeFactura";
    		enviofactura.IssueDate = Convert.ToDateTime("2018-05-28 20:17:19"/*Fecha de la factura*/);
    		enviofactura.Document = Convert.FromBase64String(Convert.ToBase64String(archivo));
    
    		EnvioFacturaElectronicaPeticion envioFacturaElectronicaPeticion = new EnvioFacturaElectronicaPeticion();
    		envioFacturaElectronicaPeticion.EnvioFacturaElectronicaPeticion1 = enviofactura;
    
    		AcuseRecibo acuseRecibo = new AcuseRecibo();
    		acuseRecibo = Service.EnvioFacturaElectronica(envioFacturaElectronicaPeticion.EnvioFacturaElectronicaPeticion1);		
    
    		return acuseRecibo.Comments.ToString();
    	}

    You can also make the submission using MTOM as recommended by the DIAN, in that case the class remains as follows:

    [WebMethod]
    	public string envioFacturaElectronicaMTOM()
    	{
    		const string apiUrl = "https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica";
    		byte[] archivo = FileToByteArray(@"c:\temp\ws_f0890900162000000dad3.zip");
    		EndpointAddress endpointAddress = new EndpointAddress(new Uri(apiUrl));
    		var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
    		securityElement.AllowInsecureTransport = false;
    		securityElement.EnableUnsecuredResponse = true;
    		securityElement.IncludeTimestamp = false;
    
    		var encodingElement = new MtomMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
    								  
    		
    
    		var transportElement = new HttpsTransportBindingElement();
    		var binding = new CustomBinding(securityElement, encodingElement, transportElement);
    
    		facturaElectronicaPortNameClient Service = new facturaElectronicaPortNameClient(binding, endpointAddress);
    		Service.ClientCredentials.UserName.UserName = "MiSoftwareID";
    		Service.ClientCredentials.UserName.Password = "MiPasswordEnSHA256";
    
    		WSDian.EnvioFacturaElectronica enviofactura = new WSDian.EnvioFacturaElectronica();
    		enviofactura.NIT = "MiNIT";
    		enviofactura.InvoiceNumber = "124063277";
    		enviofactura.IssueDate = Convert.ToDateTime("2018-05-28 20:17:19");
    		enviofactura.Document = Convert.FromBase64String(Convert.ToBase64String(archivo));
    
    		EnvioFacturaElectronicaPeticion envioFacturaElectronicaPeticion = new EnvioFacturaElectronicaPeticion();
    		envioFacturaElectronicaPeticion.EnvioFacturaElectronicaPeticion1 = enviofactura;
    
    		AcuseRecibo acuseRecibo = new AcuseRecibo();
    		acuseRecibo = Service.EnvioFacturaElectronica(envioFacturaElectronicaPeticion.EnvioFacturaElectronicaPeticion1);
    
    		return acuseRecibo.Comments.ToString();
    	}

    My web.config stayed like this:

    <?xml version="1.0"?>
    <configuration>
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.6"/>
        <httpRuntime targetFramework="4.6"/>
      </system.web>
      <system.serviceModel>
        <bindings>
          <basicHttpBinding>
            <binding name="facturaElectronicaPortNameSoap11" messageEncoding="Mtom" />
          </basicHttpBinding>
        </bindings>
        <client>
          <endpoint address="https://facturaelectronica.dian.gov.co:80/habilitacion/B2BIntegrationEngine/FacturaElectronica"
            binding="basicHttpBinding" bindingConfiguration="facturaElectronicaPortNameSoap11"
            contract="WSDian.facturaElectronicaPortName" name="facturaElectronicaPortNameSoap11" />
        </client>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <!-- Para evitar revelar información de los metadatos, establezca el valor siguiente en false antes de la implementación -->
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
              <!-- Para recibir detalles de las excepciones en los fallos, con el fin de poder realizar la depuración, establezca el valor siguiente en true. Para no revelar información sobre las excepciones, establézcalo en false antes de la implementación -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <protocolMapping>
          <add binding="basicHttpsBinding" scheme="https"/>
        </protocolMapping>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
      </system.serviceModel>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <!--
            Para examinar el directorio raíz de la aplicación web durante la depuración, establezca el valor siguiente en true.
            Establézcalo en false antes de la implementación para evitar revelar información sobre la carpeta de aplicación web.
          -->
        <directoryBrowse enabled="true"/>
      </system.webServer>
    </configuration>

    Finally:

  • The project believes it in WCF using C#
  • The Microsoft.Web.Services3 package must be added per package NuGet
  • Add the web service of the DIAN as Referencia de servicio ( link )
  • The service is consumed as https , the change must be made manually, since when adding the reference to the service it takes it as http .
  • That was it.

    Now what remains is to process the response of the service that I have not yet achieved by any of the 2 methods that I exposed. But this answers the question he had asked.

    I hope this information helps.

        
    answered by 06.09.2018 / 16:02
    source
    -2

    I face the same problem, I share the information I have found so far

    By standards WSS the password is calculated like this:

    Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
    

    In the code that you expose although you specify the line var Obtnonce = GetNonce(); it is not clear if you are using the nonce and the timestamp to calculate the password_digest .

    Now, according to the technical annexes of the DIAN, you should send a sha256 ( password ) , do not specify how to use nonce and timestamp , and in fact in another webservice that exposes the DIAN called Consulta_del_Resultado_de_Transacciones is not asked to send the nonce and the createdtime , as that part is not clear, you should try the different variants.

    My recommendation would be to change the code to calculate the password taking the account the nonce and the timestamp :

    SHA-256 ( nonce + created + password )
    

    Keep in mind specifying the Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest" o Type="...#PasswordText" attribute depending on how you send the password applying or not base64 .

    I continue investigating on the subject if I find something else, here I will publish it, and also if you solve it, I would thank you for publishing it.

        
    answered by 28.08.2018 в 04:06