martes, 26 de marzo de 2013

Consumir webservices bajo Https en Java

Usualmente cuando desarrollamos aplicaciones en Java que van a hacer uso de recursos seguros (como en este caso, consumir webservices a través de un canal cifrado), debemos asegurarnos de tener instalados los certificados digitales necesarios puesto que al ser Java un lenguaje multiplataforma, no utiliza los certificados alojados en el sistema operativo [1]. Es por esa razón que decidí escribir este artículo dedicado a este tema. Los pasos para realizar esto los voy a detallar a lo largo de este post, en donde así mismo mostraré un ejemplo práctico. Bueno... ¡manos a la obra!

Descargando el certificado digital:
El Keysotre o JKS (Java Key Store) es un repositorio de certificados de seguridad, certificados de autorización o certificados de clave pública, utilizados por ejemplo en el cifrado SSL. Usualmente el JDK contiene un almacén de claves de certificados de autorización (CA) en la ruta jre/lib/security/cacerts. Así mismo el JDK provee una herramienta llamada keytool para manipual el keystore.
Para importar un certificado a nuestro keystore, lo primero que debemos hacer es descargar  dicho certificado desde la dirección url del wsdl. En este caso les mostraré cómo se hace utilizando el navegador Google Chrome por ser uno de los más populares (Y espero que no me condenen por lo último dicho). Bueno, accedemos al wsdl mediante la url tal como se muestra en la siguiente imagen:

Ahora hacemos clic en el candado verde y se desplegará un panel con dos pestañas. Debemos posicionarnos en la pestaña Conexión como muestro a continuación:


Ahora hacemos clic en Datos del certificado y se abrirá la siguiente ventana:


En la ventana anterior debemos hacer clic en el botón Copiar en archivo... para importar el certificado. Esto nos abrirá un asistente (el clásico siguiente-siguiente) tal como se muestra en la imagen siguiente:


Hacemos clic en Siguiente> y vamos a la siguiente imagen:


Seleccionamos X.509 codificado base 64 (.CER) y hacemos clic en Siguiente>. Ahora toca seleccionar la ruta donde queremos guardar nuestro certificado, en el caso de la imagen he seleccionado el escritorio. Elegimos la carpeta que gustemos y hacemos clic en Siguiente>.


Ahora solo nos falta hacer clic en el botón finalizar de la siguiente ventana:


Con esto ya tenemos el certificado, ahora solo hace falta importarlo a nuestro keystore, para lo cual haremos uso del keytool. 

Importando el certificado digital a nuestro keystore:
Para importa el certificado digital descargado a nuestro keystore vamos a usar el keytool (del cual ya les comenté al principio de este post) que se encuentra ubicado en: ..\Java\jdk1.6.0_23\jre\bin tal como se muestra en la siguiente imagen.


Lo que debemos hacer es llamar a la herramienta keytool.exe desde una consola, con el siguiente comando: 

keytool -import -keystore "C:\Archivos de programa\Java\jre1.6.0_05\lib\security\cacerts" -file c:\NuevaEntidadCertificadora.cer -alias CA_SRI -storepass changeit

En donde las partes de color azul son valores relativos de: nuestra instalación de java, la ruta en donde descargamos nuestro certificado y el alias que le asignaremos y que debe ser único.
Lo primero que debemos recordar es que debemos ejecutar la consola de windows como administrador ya que vamos a sobreecribir un archivo en una carpeta con privilegios especiales. Para hacer esto debemos ejecutar la consola tal como se muestra en la siguiente imagen:


Luego ejecutamos el comando que se mencionó anteriormente:


E indicamos que confiamos en el certificado que vamos a agregar con un si.


Descargando el wisdel:
Ahora solo resta descargar el wsdl, para lo cual ejecutamos los tres sencillos pasos que detallo a continuación en imágenes:




Creando el proyecto en netbeans:
Creamos una sencilla aplicación de escritorio y luego agregamos un nuevo cliente para consumir el webservice.


Seleccionamos el wisdel que hemos descargado anteriormente.


Y con eso nos deberá generar una estructura similar a la mostrada en la siguiente imagen:


Luego de eso, debemos agregar la siguiente clase que contiene el método main que nos servirá para poder probar la aplicación.
package com.rolandopalermo.ejemplos.test;

import com.rolandopalermo.ejemplos.RespuestaSolicitud;

/**
 *
 * @author Rolando
 */
public class Main {

    private static RespuestaSolicitud validarComprobante(byte[] xml) {
        com.rolandopalermo.ejemplos.RecepcionComprobantesService service = new com.rolandopalermo.ejemplos.RecepcionComprobantesService();
        com.rolandopalermo.ejemplos.RecepcionComprobantes port = service.getRecepcionComprobantesPort();
        return port.validarComprobante(xml);
    }

    public static void main(String[] args) {
        System.setProperty("javax.net.ssl.keyStore", "C:\Program Files (x86)\Java\jdk1.6.0_23\jre\lib\security\cacerts");
        System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
        System.setProperty("javax.net.ssl.trustStore", "C:\Program Files (x86)\Java\jdk1.6.0_23\jre\lib\security\cacerts");
        System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
        RespuestaSolicitud respuesta = validarComprobante(null);
        System.out.println(respuesta.getEstado());
    }
}
Y con eso ejecutamos la aplicación recibiendo una respuesta lo que nos indica que nuestra aplicación ha funcionado correctamente.


Es muy imporatnte indicar que el método main deberá contener las siguientes líneas de código lo cual le indicará a la aplicación en dónde se encuentra nuestro repositorio de certificados.
System.setProperty("javax.net.ssl.keyStore", "C:\Program Files (x86)\Java\jdk1.6.0_23\jre\lib\security\cacerts");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
System.setProperty("javax.net.ssl.trustStore", "C:\Program Files (x86)\Java\jdk1.6.0_23\jre\lib\security\cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
Espero que este artículo les sea de utilidad y cualquier pregunta no duden en comentarla. Saludos y hasta una próxima oportunidad.

Y para los interesados, brindamos asesoría en el tema de facturación electrónica. El temario del taller lo pueden descargar aquí: Temario del Taller.

46 comentarios:

  1. Hola (teniendo pocos conocimientos) queria saber para que sirve lo expuesto no entiendo si es referido a seguridad, o a compatibilidad. Consumir webservices bajo Https y con respecto a esto, seria el uso de java en internet? saludos.

    ResponderEliminar
    Respuestas
    1. Hola Walt, gracias por comentarnos tus dudas, sin embargo te agradecería que seas más puntual para poder ser objetivo en la respuesta.
      Saludos

      Eliminar
  2. quisieras que me pongas en contexto, para que funciona lo que explicaste arriba por favor.

    ResponderEliminar
    Respuestas
    1. Hola Walt, lo que pasa es que a veces cuando queremos consumir webservices a través del protocolo https, se presentan problemas por no tener los certificados en nuestro almacén. En este artículo explico la manera como deberíamos descargar e instalar esos certificados para poder consumir los servicios que terceros publican en la Web (típico caso de entidades recaudadoras tal como la SUNAT en Perú o el SRI en Ecuador).
      Saludos

      Eliminar
  3. Hola Rolando, muchas gracias por tu documento me sirvió mucho, aprovecho la oportunidad para pedirte por favor si tuvieras algo similar a este articulo tal puntual y concreto sobre Web Services y Spring con Netbeans, buscando en la web encontré mucho material pero nada tan claro y entendible como el tuyo.

    Un abrazo

    Jacobo

    ResponderEliminar
    Respuestas
    1. Gracias por apreciar este artículo Don, muy pronto estaré posteando algo relacionado a Spring y JAX-WS. Sígue a este blog a través de su página en facebook para que te enteres tan pronto suba la publicación.
      Saludos.

      Eliminar
  4. Hola, estoy intentando conectarme a un web servicies que tienes credenciales de autenticidad y no se como o donde ingresarle las credenciales para usar la web servicies. Estoy trabajando con Netbenas 7.3

    ResponderEliminar
  5. Amigo, es muy bueno tu manual, tengo un problema al llamar al certificado desde una página jsp, no me funciona, parece que el System.setproperty no se ejecuta desde la página jsp, hay que cambiar alguna parte de ese código, tal ves el path?, me da este error-> ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target .. gracias.

    ResponderEliminar
  6. Que tal rolando he estado investigando y llamando a la respuesta encontramos un objeto de tipo comoprobantes, he tratado de utilizar marshaller y un marshaller, pero no puedo leer esos datos, te rogaria una guia por la cual pueda acceder a ese objeto muchas gracias de antemano

    ResponderEliminar
  7. Muchas gracias de antemano por la explicacion , resulta que estoy trantando de consumir un webservice en java (con netbeans) y al crear cliente y especificarle la url del archivo wsdl este me lanza el siguiente error "Selected wsdl is rpc encoded. You must select JAX-RPC Client" sabes a que se refiere he tratado de buscar informacion y no encuentro nada, se agradece su respuesta. saludos cordiales.

    ResponderEliminar
  8. Hola Rolando, Tu manual es genial me sirvió muchísimo.
    Te comento que utilice tu manual y me funciono muy bien en un servidor Glassfish V2.1 sobre un Linux Red Hat. importe el certificado utilizando el en el keystore de Glassfish (keystore.jks) y referencie dicho keystore en mi aplicativo desarrollado NetBeans.
    Lo mismo hice pero en un servidor Glassfish V3.1.2.2 y me muestra el siguiente Errror: "Error de transporte HTTP: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present".

    Espero me puedas apoyar. Gracias de antemano por todo.

    ResponderEliminar
  9. Hola Rolando gracias por tu explicación funcionó a la perfección, quisiera me des una mano, estoy casi terminando el desarrollo de un sistema de factura electrónica en Ecuador. Lo que explicaste fucionó perfecto, mas cuando quiero rescatar los datos de los mensajes de respuesta que me envían como respuesta (clave de acceso, info adicional, ect) me sale el arreglo en cero y como si no tuviese nada. La factura se envía perfecta y consulto en la pagina del SRI y me da autorización y todo pero desde el programa no puedo rescatar ni los mensajes ni el documento ya autorizado. Desde ya te agradezco

    ResponderEliminar
    Respuestas
    1. Que tal Guillermo, tengo exactamente el mismo problema, si ya encontraste la solucion a este problema te agradecería muchisimo si pudieras compartirla, ya que estoy con este asunto un largo tiempo ya. Te agradezco de antemano si puedes ayudar con este tema.

      Eliminar
    2. Saludos y gracias por seguir mis artículos. Les invito a participar en el taller que estoy dictando en donde se solucionan este y muchos otros problemas que se presentan al consumir los servicios web del SRI,

      Eliminar
  10. Muy buen post, sólo una consulta:

    Después de generar el cliente tipo Axis tengo la excepción:

    {http://xml.apache.org/axis/}stackTrace:org.xml.sax.SAXException: Invalid element in

    Alguna idea

    Muchas Gracias

    ResponderEliminar
  11. hola tengo una situación con el consumo del servicio del SRI para los comprobantes digitales y es que no comprendo que espera el servicio en este método validarComprobante( espera que le envie el XML transformado a bit y la respuesta la veria en la propiedad respuestaSolicitu

    ResponderEliminar
  12. Que tal Rolando, muchas gracias por el manual, estaba buscando info y el tuorial esta excelente

    ResponderEliminar
  13. Que tal Rolando, muy buen aporte. La inquietud que tengo es que ya tengo el servicio web funcionando correctamente pero al momento de traer los resultados solo me resuelve el estado (RECIBIDA, DEVUELTA), de ahí los tipos List dentro del ws como lo son Comprobante o Mensajes no se resuelven y me quedan como un array vacío, pese a que el xml salga como devuelto y a que debería haber mas información para ver los errores. Anticipadamente gracias por tus respuestas.

    ResponderEliminar
    Respuestas
    1. Saludos Gerardo, este problema es muy común. Te invito a participar en el taller que estoy dictando en donde se solucionan este problema y en donde te comparto mi experiencia de implementación de proyectos de Facturación Electrónica en Ecuador. Al final de post está el temario del taller.

      Eliminar
  14. Buenas tardes:
    A la fecha estoy aprendiendo sobre Web Services y tengo una pregunta cuando el web service requiere datos de autenticacion donde se incluye, es decir si requiero un usuario y login, para poder entrar.
    Gracias

    ResponderEliminar
  15. Rolando muy bueno este tutorial.. le he estado realizando, pero al momento de enviar un arreglo de byte al webservice del sri, como respuesta me devuelve y con array vacio.. Que puedo hacer

    ResponderEliminar
    Respuestas
    1. Saludos y gracias por seguir mis artículos. Bernardo, te invito a participar en el taller que estoy dictando en donde se solucionan este y muchos otros problemas que se presentan al consumir los servicios web del SRI.

      Eliminar
  16. Gracias por su respuesta Rolando, y si me gustaria participar en taller.. Que se tiene que hacer para participar..

    ResponderEliminar
    Respuestas
    1. Saludos Bernardo
      Te dejo el enlace de los temas en donde se incluyen los datos de contacto: http://goo.gl/SY9AAa

      Eliminar
  17. Hola Rolando
    Mi cliente me han pasado como certificado un archivo .jks, como se instalaría este tipo de certificado ...

    ResponderEliminar
  18. Hola podrias hacer un ejemplo con visual studio 2010 por favor

    ResponderEliminar
  19. Hola puedes realizar un ejemplo en visual studio 2010 por favor

    ResponderEliminar
  20. Hola Rolando, por favor, podrías describir un breve costo del taller relacionado con facturación electrónica, estoy muy interesado, gracias.

    ResponderEliminar
  21. Hola Rolando, tienes alguna version del taller en linea o algun material que no sea presencial. Especialmente dirigido al consumo de los web services del SRI y la autorizacion de documentos. .net seria mucho mejor. gracias.

    ResponderEliminar
  22. Fernando un saludo,
    Una consulta estoy realizando el proceso que indicas, pero siempre tengo el mismo problema, cuando me retorna en estado DEVUELTA se esperaría los motivos pero no me llega ningún mensaje de error que podrá ser?

    Att. J. Desa.

    ResponderEliminar
  23. Fernando saludos,

    Interesante tu tema... tu crees q el mismo procedimiento se aplique para .net? tengo el problema de q el mensaje me trae "DEVUELTA". Por otro lado donde podriamos adquirir el material de apoyo que has publicado sobre el tema? gracias de antemano.

    ResponderEliminar
  24. Podrían decirme si aún están dictando este taller, información al correo andysil2007@gmail.com

    ResponderEliminar
  25. Muchas gracias por su aporte, por favor tengo este mensaje de error, al probar su código : No se puede crear una instancia del tipo RecepcionComprobantesService.

    Por favor que causa este problema. Gracias

    ResponderEliminar
  26. Saludos ami tambien me sale erroe ne esta linea: recepcion.ws.sri.gob.ec.RecepcionComprobantesService service = new recepcion.ws.sri.gob.ec.RecepcionComprobantesService();

    Creo que es porque se esta tratando de crear una instancia de una interface pero no se como solucionarlo alguien podria ayudar, muchas gracias de antemano

    ResponderEliminar
  27. Hola Rolando, primero por felicitarte por el documento publicado me sirvio muchismo, tambien por pedirte ayuda en la recepción de respuesta de la autorizacion me devuelve como Nulo, en realidad no se a que se deba este problema utilizo NetBeans.

    De antemano gracias por tu ayuda.

    ResponderEliminar
  28. Rolando te quedo agradecido por el taller on line realizado, con la librería del taller ya pudimos obtener las listas de recepcion y de autorizacion.

    ate,
    Juan A. Pulley


    ResponderEliminar
  29. Cuanto es el valor del curso? info a bscfernando@hotmail.com

    ResponderEliminar
  30. Ronaldo, lo que publicaste sirve para Recepcion de comprobantes y me a servido muchisimo GRACIAS, pero quisiera saber la clase main para saber como utilizar la Autorzacion de comprobantes
    Gracias

    ResponderEliminar
  31. Estimado Rolando, que pasa si cambian el certificado en el WS habria que bajarlo de nuevo y adjuntarlo al proyecto nuevamente ? o con una vez es suficiente?

    saludos.

    ResponderEliminar
  32. Hola,

    Las imágenes del punto "Descargando el wisdel:" ya no se pueden visualizar. Por favor, actualizarlas. De ante mano gracias por el aporte

    Saludos

    ResponderEliminar
  33. Hola amigos, les cuento que yo desarrolle un sistema de facturación electrónica que se comunica con el web service de SUNAT( BillService www.sunat.gob.pe/ol-ti-itcpgem-beta https://www.sunat.gob.pe/ol-ti-itcpgem-beta/billService?wsdl ) en el lenguaje JAVA. asi que si tienes duda o consulta sobre este tema escribeme al ingpipa@hotmail.com espero ayudarles.

    ResponderEliminar
  34. Hola un favor enorme, obtengo este error, en cliente ya al consumirlo no comprendo cual es la razón, veo que se encuentran los paths correctamente configurados, las propiedades
    javax.net.ssl.trustStore, etc. Mi duda es en

    System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

    se debe mantener ese valor changeit

    gracias de antemano.

    abr 28, 2016 11:17:28 PM [com.sun.xml.internal.ws.policy.EffectiveAlternativeSelector] selectAlternatives
    ADVERTENCIA: WSP0075: Policy assertion "{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}SignedSupportingTokens" was evaluated as "UNKNOWN".

    Exception in thread "main" com.sun.xml.internal.ws.client.ClientTransportException: Error de transporte HTTP: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

    ResponderEliminar
  35. Hola sabes que encontre el inconveniente pero ahora si no comprendo donde esta el error obtengo esta excepcion.

    Ojala puedas ayudarme. Gracias por este tutorial.

    Exception in thread "main" com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: An error occurred when verifying security for the message. Please see the server log to find more detail regarding exact cause of the failure.

    ResponderEliminar
  36. Enhorabuena por el tutorial. Es el más completo y sencillo que he visto.

    ResponderEliminar