Hola colegas, el día de hoy quiero compartir con ustedes una interrogante que tenía hace unas semanas y que me puso a investigar durante muchos días y horas, quemándome las pestañas e incluso hasta quitándome el sueño. Invocar un Servicio SOAP con WS-Security a través de WCF y en tiempo de ejecución, ya que para mi caso particular, necesito que tanto el usuario y contraseña, sean dinámicos, lo cual no puedo ponerlo en el archivo de configuración de mi aplicación.
Pocas veces un reto me ha tenido pensando durante mucho tiempo para poder solucionarlo, tuve que conseguir información de muchas fuentes, blogs, artículos del MSDN y cosas así, y mezclando un poco mi experiencia, les comparto la solución.
Por tonto que pueda parecer, invocar a un servicio Web SOAP a través de WCF puede ser sencillo de buenas a primeras: Agregar Referencia de Servicio, pegar la URL del WSDL y presionar OK, listo! Ya tienes tu proxy para invocar el servicio.
El Problema
Pero luego te das cuenta de que el servicio en cuestión, tiene un esquema de seguridad que no está soportado por WCF, el WS-Security. Según Microsoft, este esquema de seguridad es obsoleto, y ya no se debería usar, y por lo tanto WCF no lo soporta, pero, ¿Qué pasa si el servicio que pretendemos consumir no lo hicimos nosotros?
Aquí el servicio que yo pretendía consumir:
https://www.sunat.gob.pe/ol-ti-itemision-otroscpe-gem-beta/billService?wsdl
Y lo que se debe enviar es lo siguiente:
El problema radica en que debo enviar el UsernameToken de manera dinámica y además la contraseña debe ser enviada como hashed passsword digest (disculpen que no lo traduzca) en combinación con nonce (un algoritmo pensando para que sólo una vez se pueda usar una petición, de manera que no puedes hacer dos o más peticiones con el mismo encriptado).
He estado indagando y buscando información y veo que este tipo de servicios son construídos con Java. Lo cual a ojos de un experto en programación, no tiene nada de malo, pues para eso se inventaron los Servicios Web, que sean independientes de la plataforma, Sistema Operativo y lenguaje de programación, es un estándar!
La Solución
Antes de escribir la solución, debo mencionar que no hay que reinventar la rueda, consejo de mi colega Ben Powell, ya que si vamos a escribir una claseUsernameToken que soporte este modelo de seguridad, hay que recordar que Microsoft ya lo hizo antes con las librerias WSE 2.0 y WSE 3.0, y cito literalmente lo que dice Powell: “No hay razón para no usarlo en conjunción con WCF”.
Bien, lo primero que hay que hacer es referenciar al ensamblado Microsoft Web Service Enhancements 3.0 a través de Nuget en el proyecto en cuestión, en mi caso yo utilizo Visual Studio 2015:
Una vez descargado, procedemos a crear nuestra clase que implemente la interfazIClientMessageInspector, la idea es que alteremos el XML de envío antes de ser enviado como petición al servidor:
Luego, creamos una clase que implemente la interfaz IEndpointBehavior, ya que también necesitamos alterar el comportamiento de nuestro proxy para que use la clase que hemos creado implementando la interfaz IClientMessageInspector:
Fíjense en el método ApplyClientBehavior, en la que le indicamos que use la clase que previamente hemos creado.
Luego veamos como tenemos que dejar nuestro archivo de configuración de nuestro proyecto:
Debido a que Visual Studio detectó que el servicio utiliza el protocolo https, el Binding debe ser configurado como Transport, ahora sólo nos queda modificar la invocación al proxy creado:
Ahora con este código, explicamente le estoy indicando a WCF que use elEndpointBehavior creado.
Finalmente, si queremos inspeccionar cual es el XML resultante de envío hay que usar un depurador de tráfico, en mi caso usaré Fiddler:
Aquí vemos la solicitud al Servicio Seguro:
Y acá vemos lo que estamos realmente enviando:
Si lo “parseamos” a XML queda de la siguiente manera:
Espero que esta solución os ayude, difundan la información.
Saludos.