REST con ssl

Hola todos,
Estamos intentando configurar los datos de conexión de rest para que use certificados ssl.
Hemos seguido la documentación de la wiki (http://toba.siu.edu.ar/trac/toba/wiki/Referencia/Rest), pero no nos funciona.
Hemos habilitado el módulo ssl en apache, y agregado las siguientes líneas en el virtalhost


SSLVerifyClient require
SSLOptions +StdEnvVars +ExportCertData

Los certificados estamos casi seguros que funcionan puesto que hemos verificado que son válidos empleando este comando:

 
openssl verify -purpose sslserver -CAfile ca.crt  cliente.crt

Les paso cómo hemos configurado el cliente.ini:


[conexion]
to = "http://tarjetau.unpa.edu.ar/hub_servicios/1.0/rest/"
auth_tipo = "ssl"
cert_file = "/home/roberto/certificados/CA/certificados/cliente.crt"
key_file = "/home/roberto/certificados/CA/privado/cliente.key"
ca_cert= "/home/roberto/certificados/CA/certificados/ca.crt"

El archivo servidor.ini


autenticacion = ssl

El archivo servidor_usuarios.ini


[admin]
fingerprint = 5bdb46940f1a50e5db7b259ac536f4fb8864e803

La fingerprint la calculamos ejecutando este código:


$certUtils = new SSLCertUtils();
$certUtils->loadCertFromFile("/home/roberto/certificados/CA/certificados/cliente.crt");
echo $certUtils->getFingerprint();

En el archivo sistema.log del servidor de ws nos muestra el siguiente error:


[DEBUG][hub_servicios] SIUToba\rest\http\respuesta_rest::__set_state(array(
   'status' => 401,
   'headers' => 
  array (
    'Content-Type' => 'application/json',
    'API-Version' => '1.0.0',
  ),
   'data' => 
  array (
    'mensaje' => 'autenticación cancelada, falta información',
  ),
   'encoding' => 'latin1',
   'api_version' => '1.0.0',
))

En apache


PHP Fatal error:  Uncaught exception 'GuzzleHttp\Exception\ClientException' with message 'Client error response [url] http://tarjetau.unpa.edu.ar/hub_servicios/1.0/rest/mapuche/agentes/29058990 [status code] 401 [reason phrase] Unauthorized' in /home/roberto/toba_2.7.2/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:89
Stack trace:
#0 /home/roberto/toba_2.7.2/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php(33): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Message\Request), Object(GuzzleHttp\Message\Response))
#1 /home/roberto/toba_2.7.2/vendor/guzzlehttp/guzzle/src/Event/Emitter.php(109): GuzzleHttp\Subscriber\HttpError->onComplete(Object(GuzzleHttp\Event\CompleteEvent), 'complete')
#2 /home/roberto/toba_2.7.2/vendor/guzzlehttp/guzzle/src/RequestFsm.php(203): GuzzleHttp\Event\Emitter->emit('complete', Object(GuzzleHttp\Event\CompleteEvent))
#3 /home/roberto/toba_2.7.2/vendor/guzzlehttp/guzzle/src/RequestFsm.php(92): GuzzleHttp\RequestFsm->complete(Object(GuzzleHttp\Transaction))
#4 /home/ro in /home/roberto/toba_2.7.2/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 89

Les adjunto los certificados por si quieren probarlos.

Saludos


certificados.zip (5.2 KB)

Hola Roberto,

un detalle… la URL que especificaron para el WS… no utiliza SSL, sin eso apache jamas va a presentarle el certificado a la libreria ya que no esta involucrada una conexion segura.
Esto esta asi en la config o es un typo en el post?

Saludos

Hola Richard, gracias por tu pronta respuesta.
Solo habilamos el módulo ssl en apache. Lo que no tenemos configurado es el virtualhost con ssl para acceder por https. Seguramente ese es nuestro error.
Te hago dos consultas para estar seguro:

  1. ambos sitios deben tener el https habilitado? (cliente y servidor)
  2. nosotros generamos un certificado en la máquina cliente que es el que empleamos para configurar el archivo cliente.ini de rest. ¿Ese mismo certificado es el que se utiliza en la configuración del virtualhost de ambos servidores?

Saludos y gracias nuevamente

Hola Roberto,

Unicamente quien sirve debe tener el modulo SSL en Apache y la correspondiente configuracion en VirtualHost… el cliente con tener instalado CURL funciona.

2) nosotros generamos un certificado en la máquina cliente que es el que empleamos para configurar el archivo cliente.ini de rest. ¿Ese mismo certificado es el que se utiliza en la configuración del virtualhost de ambos servidores?

NO, la idea es que los clientes tengan un certificado distinto al del servidor, de la misma forma que se hace en internet con cualquier sitio y que sirva para garantizarle a cada extremo que el otro es quien dice ser:

  • El certificado del servidor debe poder garantizar la identidad de ese sistema, onda… no te conectaste a un Pilaga distinto dentro de la red (uno de test)… sino que es el correcto, el de produccion.

  • El certificado del cliente, se usa para poder garantizar la identidad de ese cliente tambien.

Lo principal es que no se reusen certificados… salvo que el sistema opere como servidor y cliente al mismo tiempo… en dicho caso no tiene sentido usar uno distinto para cada cosa, aunque la configuracion lo permite.
Para ponerlo mas simple, sistemas distintos requiren certificados distintos. Si conecto Mapuche con Pilaga… requiero al menos uno para cada uno, independientemente de cual sea servidor y cual cliente.

No se si es puntualmente a lo que te referias.

Saludos

Hola cómo andan?
Estoy retomando nuevamente este asunto que me había quedado colgado el año pasado.
Ahora estamos por hacer una implementación con REST y es muy importante que podamos garantizar la seguridad durante el consumo de servicios web entre las aplicaciones.
Richard, seguí tus últimas indicaciones, y creo nos está faltando un detalle para que salga funcionando pero no podemos saber qué.
Paso a detallar la configuración de ambos extremos de la comunicación y nuestras dudas en cada caso. De momento estamos realizando prueba en local, de allí que coincida la ip para ambos servidores.

En la aplicación servidor de los web services tenemos lo siguiente:
servidor_usuarios.ini


[admin]
fingerprint = 078eb7355d61488272d27d3b2addbb0d896337c9 

El fingerprint lo calculamos usando la librería SIUToba\SSLCertUtils\SSLCertUtils;

Pregunta: El fingerprint que aquí debemos calcular, es a partir del certificado .crt del servidor o cliente? Nosotros probamos generando el fingerprint de ambos, pero no sabemos cuál es el que corresponde

servidor.ini

autenticacion = ssl

virtualhost sw.unpa.edu.ar.conf


<VirtualHost 192.168.10.241:443>

    ServerName sw.unpa.edu.ar
    Include /home/roberto/toba_2.7.2/vendor/siu-toba/framework/instalacion/toba.conf
 
    SSLEngine on


     SSLVerifyClient require
    SSLOptions +StdEnvVars +ExportCertData   

    
    SSLCertificateFile /home/roberto/certificados/CA/certificados/hubservicios.crt
    SSLCertificateKeyFile /home/roberto/certificados/CA/privado/hubservicios.key
    SSLCertificateChainFile /home/roberto/certificados/CA/certificados/ca.crt
    
</VirtualHost> 

En la aplicación cliente que consume los web service tenemos los siguiente

cliente.ini


[conexion]
;;Recuerde dejar una barra (/) al finalizar la URL
to = "https://sw.unpa.edu.ar/hub_servicios/1.0/rest/"
auth_tipo = "ssl"
cert_file = "/home/roberto/certificados/CA/certificados/hubservicios.crt"
key_file = "/home/roberto/certificados/CA/privado/hubservicios.key"
ca_cert= "/home/roberto/certificados/CA/certificados/ca.crt"

Pregunta: los certificados que hay que referenciar aquí son los del servidor de servicios web?

virtualhost tarjeta.unpa.edu.ar.conf


<VirtualHost 192.168.10.241:443>
    ServerName tarjeta.unpa.edu.ar
    Include /home/roberto/toba_2.7.2/vendor/siu-toba/framework/instalacion/toba.conf

    SSLEngine on
    SSLCertificateFile /home/roberto/certificados/CA/certificados/cliente.crt
    SSLCertificateKeyFile /home/roberto/certificados/CA/privado/cliente.key
    SSLCertificateChainFile /home/roberto/certificados/CA/certificados/ca.crt

</VirtualHost> 

Los certificados estamos seguros que funcionan, hemos hecho las siguientes pruebas para corroborar que permiten verificar la identidad de cada uno:
curl --cacert /home/roberto/certificados/CA/certificados/ca.crt -E /home/roberto/certificados/CA/cliente.pem https://tarjeta.unpa.edu.ar
curl -v --cacert /home/roberto/certificados/CA/certificados/ca.crt https://tarjeta.unpa.edu.ar

Estos comandos los hemos ejecutado para cada servidor. Con el sitio cliente tarjeta.unpa.edu.ar, los comando retornaron resultado satisfactorio. Sin embargo para el caso del servidor sw.unpa.edu.ar, con las variables: SSLVerifyClient y SSLOptions, seteadas, los comandos fallan. Cuando las quitamos, los comandos de verificación retornan resultado satisfactorio.

Algo similar ocurre al ejecutar la aplicación cliente. Cuando invocamos un web services desde el cliente, y el servidor posee las variables SSLVerifyClient y SSLOptions seteadas, retorna el siguiente error:


* Hostname was NOT found in DNS cache
*   Trying 192.168.10.241...
* Connected to sw.unpa.edu.ar (192.168.10.241) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /home/roberto/certificados/CA/certificados/ca.crt
  CApath: /etc/ssl/certs
* error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca
* Closing connection 0
PHP Fatal error:  Uncaught exception 'GuzzleHttp\Ring\Exception\ConnectException' with message 'cURL error 35: error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca' in /home/roberto/toba_2.7.2/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php:126

Cuando quitamos las variables de la configuración del virtualhost del servidor, la verificación de certificados entendemos que funciona, pero lanza una excepción de acceso no autorizado:


* Hostname was NOT found in DNS cache
*   Trying 192.168.10.241...
* Connected to sw.unpa.edu.ar (192.168.10.241) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /home/roberto/certificados/CA/certificados/ca.crt
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
*        subject: C=AR; ST=SANTA CRUZ; L=RIO GALLEGOS; O=UNPA; OU=PSTI; CN=sw.unpa.edu.ar; emailAddress=huelemu@unpa.edu.ar
*        start date: 2017-03-15 19:42:53 GMT
*        expire date: 2018-03-15 19:42:53 GMT
*        common name: sw.unpa.edu.ar (matched)
*        issuer: C=AR; ST=SANTA CRUZ; L=RIO GALLEGOS; O=UNPA; OU=PSTI; CN=www.unpa.edu.ar; emailAddress=huelemu@unpa.edu.ar
*        SSL certificate verify ok.
> GET /hub_servicios/1.0/rest/mapuche/agentes/29058990 HTTP/1.1
Host: sw.unpa.edu.ar
User-Agent: Guzzle/5.1.0 curl/7.38.0 PHP/5.6.24-0+deb8u1

< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Mar 2017 16:24:59 GMT
* Server Apache/2.4.10 (Debian) is not blacklisted
< Server: Apache/2.4.10 (Debian)
< API-Version: 1.0.0
< Content-Length: 85
< Content-Type: application/json
< 
* Connection #0 to host sw.unpa.edu.ar left intact
PHP Fatal error:  Uncaught exception 'GuzzleHttp\Exception\ClientException' with message 'Client error response [url] https://sw.unpa.edu.ar/hub_servicios/1.0/rest/mapuche/agentes/29058990 [status code] 401 [reason phrase] Unauthorized'

De acuerdo a la documentación de wiki, dichas variables son obligatorias para permitir el intercambio de certificados. Quizás nos estemos pasando por alto un paso para que funcione, pero no hemos podido encontrarle la vuelta

Espero nos puedan ayudar.
Saludos a todos

Hola Roberto,

Es a partir del certificado del cliente, ya que se usa para matchearlo.

servidor.ini
autenticacion = ssl

virtualhost sw.unpa.edu.ar.conf


<VirtualHost 192.168.10.241:443>

    ServerName sw.unpa.edu.ar
    Include /home/roberto/toba_2.7.2/vendor/siu-toba/framework/instalacion/toba.conf
 
    SSLEngine on


     SSLVerifyClient require
    SSLOptions +StdEnvVars +ExportCertData   

    
    SSLCertificateFile /home/roberto/certificados/CA/certificados/hubservicios.crt
    SSLCertificateKeyFile /home/roberto/certificados/CA/privado/hubservicios.key
    SSLCertificateChainFile /home/roberto/certificados/CA/certificados/ca.crt
    
</VirtualHost> 

En la aplicación cliente que consume los web service tenemos los siguiente

cliente.ini


[conexion]
;;Recuerde dejar una barra (/) al finalizar la URL
to = "https://sw.unpa.edu.ar/hub_servicios/1.0/rest/"
auth_tipo = "ssl"
cert_file = "/home/roberto/certificados/CA/certificados/hubservicios.crt"
key_file = "/home/roberto/certificados/CA/privado/hubservicios.key"
ca_cert= "/home/roberto/certificados/CA/certificados/ca.crt"

Pregunta: los certificados que hay que referenciar aquí son los del servidor de servicios web?
NO, el cliente.ini lo tenes en tu maquina cliente...si vas a llamar un WS de Banelco por ejemplo, tenes que mandarle tus certificados... no poner los de ellos, dicho sea de paso.. no creo que consigas la PK de Banelco ni a punta de pistola, ese es un buen parametro para saber que tenes que poner.
virtualhost tarjeta.unpa.edu.ar.conf

<VirtualHost 192.168.10.241:443>
    ServerName tarjeta.unpa.edu.ar
    Include /home/roberto/toba_2.7.2/vendor/siu-toba/framework/instalacion/toba.conf

    SSLEngine on
    SSLCertificateFile /home/roberto/certificados/CA/certificados/cliente.crt
    SSLCertificateKeyFile /home/roberto/certificados/CA/privado/cliente.key
    SSLCertificateChainFile /home/roberto/certificados/CA/certificados/ca.crt

</VirtualHost> 

Este servidor a que corresponderia?, hay cruzamiento de WS entre ellos?.
Los certificados estamos seguros que funcionan, hemos hecho las siguientes pruebas para corroborar que permiten verificar la identidad de cada uno: curl --cacert /home/roberto/certificados/CA/certificados/ca.crt -E /home/roberto/certificados/CA/cliente.pem https://tarjeta.unpa.edu.ar curl -v --cacert /home/roberto/certificados/CA/certificados/ca.crt https://tarjeta.unpa.edu.ar
De la configuracion anterior, pareciera que le estas enviando al servidor sus propios certificados.. esto es asi?.
Estos comandos los hemos ejecutado para cada servidor. Con el sitio cliente tarjeta.unpa.edu.ar, los comando retornaron resultado satisfactorio. Sin embargo para el caso del servidor sw.unpa.edu.ar, con las variables: SSLVerifyClient y SSLOptions, seteadas, los comandos fallan. Cuando las quitamos, los comandos de verificación retornan resultado satisfactorio.
Cuando las quitas, no se esta verficando nada.. porque Apache no esta esperando recibir ningun certificado desde el cliente.. por lo que da lo mismo.
Algo similar ocurre al ejecutar la aplicación cliente. Cuando invocamos un web services desde el cliente, y el servidor posee las variables SSLVerifyClient y SSLOptions seteadas, retorna el siguiente error:

* Hostname was NOT found in DNS cache
*   Trying 192.168.10.241...
* Connected to sw.unpa.edu.ar (192.168.10.241) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /home/roberto/certificados/CA/certificados/ca.crt
  CApath: /etc/ssl/certs
* error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca
* Closing connection 0
PHP Fatal error:  Uncaught exception 'GuzzleHttp\Ring\Exception\ConnectException' with message 'cURL error 35: error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca' in /home/roberto/toba_2.7.2/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php:126

Esta perfecto el error, salvo que tu CA este dentro de las que CURL conoce o confia.
Cuando quitamos las variables de la configuración del virtualhost del servidor, la verificación de certificados entendemos que funciona, pero lanza una excepción de acceso no autorizado:

* Hostname was NOT found in DNS cache
*   Trying 192.168.10.241...
* Connected to sw.unpa.edu.ar (192.168.10.241) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /home/roberto/certificados/CA/certificados/ca.crt
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
*        subject: C=AR; ST=SANTA CRUZ; L=RIO GALLEGOS; O=UNPA; OU=PSTI; CN=sw.unpa.edu.ar; emailAddress=huelemu@unpa.edu.ar
*        start date: 2017-03-15 19:42:53 GMT
*        expire date: 2018-03-15 19:42:53 GMT
*        common name: sw.unpa.edu.ar (matched)
*        issuer: C=AR; ST=SANTA CRUZ; L=RIO GALLEGOS; O=UNPA; OU=PSTI; CN=www.unpa.edu.ar; emailAddress=huelemu@unpa.edu.ar
*        SSL certificate verify ok.
> GET /hub_servicios/1.0/rest/mapuche/agentes/29058990 HTTP/1.1
Host: sw.unpa.edu.ar
User-Agent: Guzzle/5.1.0 curl/7.38.0 PHP/5.6.24-0+deb8u1

< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Mar 2017 16:24:59 GMT
* Server Apache/2.4.10 (Debian) is not blacklisted
< Server: Apache/2.4.10 (Debian)
< API-Version: 1.0.0
< Content-Length: 85
< Content-Type: application/json
< 
* Connection #0 to host sw.unpa.edu.ar left intact
PHP Fatal error:  Uncaught exception 'GuzzleHttp\Exception\ClientException' with message 'Client error response [url] https://sw.unpa.edu.ar/hub_servicios/1.0/rest/mapuche/agentes/29058990 [status code] 401 [reason phrase] Unauthorized'

De acuerdo a la documentación de wiki, dichas variables son obligatorias para permitir el intercambio de certificados. Quizás nos estemos pasando por alto un paso para que funcione, pero no hemos podido encontrarle la vuelta


Si sacas las variables esas, Apache no verifica nada, no le devuelve a la libreria REST la informacion del certificado y por tanto no puede saber a que cliente pertenece la conexion, ergo te devuelve un 401. No es bueno que pruebes este tipo de cosas en la misma maquina, salvo que tengas bien diferenciado cada lado, incluso asi… no alcanzas a notar cuestiones sobre la confiabilidad de la CA. Si podes usar Docker para correr esto de manera aislada mejor y que cada contenedor tenga su certificado y CA propia si podes.

Para mi lo que te falta es separar mejor cada rol, ahi vas a ver bien que configuracion de certificados va de que lado. Y por otra parte, como estas trabajando con CAs autofirmadas tenes que registrarlas para que CURL confie en ellas, en linux via el comando update-ca-certificates.

Saludos