Buscar este blog

sábado, 17 de febrero de 2018

JBoss - Check Vault, Users and JNDI entries

Almost all applications that we develop requires the use of encrypted strings, users with some specific role, or jndi entries. It is very common that, during the process of configuration, something goes wrong and you will have to lost much time trying to figure out what.

For example, your application publishes a web service, which is secured with security constraints, and only users with a proper role can consume it. The server is configured, the application is deployed, but when you try to invoke the service you get an 403 Unauthorized error. What it's wrong?, the user is not in the Application Realm of the server, the user credentials are not right, the vault password is wrong, the user has not the suitable rol?

I prepared a quite simple web app in order to test these problems easily: https://github.com/evazquezma/jboss/releases
Once deployed, from the index page, you can go to these pages:
  • http://localhost:8080/jboss-utils/vault
    Introduce a string in vault format and check the clear value.
  • http://localhost:8080/jboss-utils/users
    Introduce an user credentials and check if she exists, and which roles she has.
  • http://localhost:8080/jboss-utils/jndi
    Introduce a jndi key and check if it exists, its value and its class name.

viernes, 16 de febrero de 2018

Autofirma - Análisis de firma en Android

Siguiendo con el tema de Autofirma, en esta ocasión voy analizar los distintos mensajes intercambiados entre el navegador, Autofirma y los servidores de backend cuando se realiza una firma trifásica en Android.

Entorno

El entorno de la prueba es el siguiente:
  • Apache: 2.2
  • JBoss EAP 6.4
  • Android: 4.4.4
Además, los componentes involucrados son:
Componente Versión URL
Página de firma N/A http://192.168.43.239/sdxc/test/storageRetrieve
Autofirma Cliente movil firma 1.5 N/A
Server trifase 1.5 http://192.168.43.239/afirma-server-triphase-signer/SignatureService
Signature Storage 1.5 http://192.168.43.239/afirma-signature-storage/StorageService
Signature Retrieve 1.5 http://192.168.43.239/afirma-signature-retriever/RetrieveService
Directorio entrada server trifase N/A D:\tmp\afirma\entrada
Directorio salida server trifase N/A D:\tmp\afirma\salida
Directorio temporal servlets N/A D:\tmp\afirma\TriStorageRetrieve

Visión general

En esta prueba se lanzará una firma de tipo PAdES, sobre el fichero "prueba1.pdf", empleando el servidor trifase y los servlets de almacenamiento Sotrage y Retrieve.

El servidor trifase sirve para que la gestión del fichero que se va a firmar se haga en servidor y, por tanto, nunca se envíe completo al dispositivo del usuario. Este modo de funcionamiento se basa en tres etapas:
  1. Autofirma llama al servidor trifase para indicarle que prepare la firma. Es decir, que coja el fichero y prepare el hash que hay que firmar.
  2. Autofirma recibe el hash y lo firma con la clave privada del certificado de usuario.
  3. Autofirma llama al servidor trifase para indicarle que complete la firma. Es decir, que coja el hash firmado y lo "incruste" en el fichero a firmar.
Los servlets de almacenamiento sirven para permitir una comunicación bidireccional entre el navegador de usuario y Autofirma cuando ésta no puede hacerse de forma directa. Si el navegador o el dispositivo de usuario no permiten el uso de sockets (tanto por restricciones tecnológicas como de seguridad), se emplean estos servlets a modo de repositorio intermedio. Autofirma escribe datos y el navegador de usuario los lee. Según he visto, el contenido que se guarda en estos ficheros intermedios va cifrado con una clave generada por el navegador al comienzo del proceso.

El uso de server trifase y de los servlets de almacenamiento es independiente entre sí. Es decir, se puede usar server trifase sin servlets, y se pueden usar servlets sin server trifase. También se puede realizar la firma sin ninguno de los dos.
  1. Si sólo se emplea server trifase, el navegador obligatoriamente debe soportar sockets. La comunicación entre navegador y autofirma se hace de forma directa. La ventaja es que el fichero a firmar no sale del backend.
  2. Si sólo se emplean los servlets, el navegador envía a Autofirma el fichero a firmar (se envía como cadena Base 64 durante la invocación). Al terminar la firma, Autofirma deja el fichero firmado (completo) en el repositorio intermedio para que lo lea el navegador. La ventaja es que el navegador no necesita soportar sockets.
  3. Si no se emplea ninguno de los dos mecanismos anteriores, el navegador debe soportar sockets. El fichero a firmar se envía durante la invocación y, una vez firmado, se recupera a través de un socket. La ventaja es que es el método más simple.
Combinando server trifase y servlets de almacenamiento se consigue que el navegador no tenga que soportar sockets, que el fichero no salga del backend, y que los mensajes intercambiados con los servlets de almacenamiento no contengan la firma en sí, sino información acerca de su finalización.

Mensajería

A continuación un ejemplo de firma correcta. Las trazas que incorporo provienen de dos fuentes:

1 - Navegador - Autofirma

Dese el navegador se hace una invocación por protocolo a autofirma pasándole los siguientes parámetros: 
intent://sign?ver=1&op=sign&id=8wp29aKd7L4486I60UrR&key=11095593&stservlet=http://192.168.43.239/afirma-signature-storage/StorageService&format=PAdEStri&algorithm=SHA512withRSA&properties=c2VydmVyVXJsPWh0dHA6Ly8xOTIuMTY4LjQzLjIzOS9hZmlybWEtc2VydmVyLXRyaXBoYXNlLXNpZ25lci9TaWduYXR1cmVTZXJ2aWNl&dat=cHJ1ZWJhMS5wZGY=#Intent;scheme=afirma;package=es.gob.afirma;end

Aquí, los parámetros relevantes son:
  • op. Operación de firma
  • id. Es un identificador que se empleará para nombrar los ficheros de "intercambio" que gestionará el Signature Storage y Retrieve.
  • key. Algún tipo de clave generada para cifrar ciertos mensajes.
  • Dirección de estos servlets
  • formato y algortimo de firma
  • properties. Contiene los parámetros adicionales, en este caso la dirección del server trifase.
  • dat. Identificador del fichero que se va a firmar (cHJ1ZWJhMS5wZGY=) 

2 - Autofirma - Server trifase (Prefirma)

Después de que el usuario selecciona el certificado de firma, Autofirma lanza un petición contra server trifase indicándole los datos del fichero que se quiere firmar y el certificado del firmate (sólo clave pública e identificación):
POST /afirma-server-triphase-signer/SignatureService

op=pre&cop=sign&format=pades&algo=SHA512withRSA&cert=MIII(...)A4=&doc=cHJ1ZWJhMS5wZGY=&params=IwojRnJpIEZlYiAxNiAxMjowMTo0OCBDRVQgMjAxOApzZXJ2ZXJVcmw9aHR0cFw6Ly8xOTIuMTY4LjQzLjIzOS9hZmlybWEtc2VydmVyLXRyaXBoYXNlLXNpZ25lci9TaWduYXR1cmVTZXJ2aWNlCg==

Parseando los parámetros tenemos:
  • op=pre. Operación de prefirma
  • cop=sign. Código de operación de firma
  • format=pades. Firma PAdES
  • algo=SHA512withRSA. Algoritmo de firma
  • cert=MIII(...). Certificado del firmante (parte pública)
  • doc=cHJ1ZWJhMS5wZGY=. Nombre del documento que se va a firmar. En este caso "prueba1.pdf"
  • params=Iwoj(...). Parámetros adicionales. En este caso contiene la dirección completa del servidor trifase, es decir, se referencia a sí mismo
Cuando el server trifase recibe esta petición, imprime el siguiente log:
12:01:48,611 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == INICIO FIRMA TRIFASICA ==
12:01:48,614 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recuperamos el documento mediante el DocumentManager
12:01:48,614 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recuperamos el documento con identificador: cHJ1ZWJhMS5wZGY=
12:01:48,615 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Buscamos el fichero: D:\tmp\afirma\entrada\prueba1.pdf
12:01:48,617 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recuperado documento de 175247 octetos
12:01:48,617 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Formato de firma seleccionado: pades
12:01:48,617 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1)  == PREFIRMA en servidor
12:01:48,618 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Prefirma PAdES - Firma - INICIO
12:01:48,618 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se invocan las funciones internas de prefirma PAdES
12:01:48,641 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se ha seleccionado la generacion de CAdES para inclusion en PAdES
12:01:48,644 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se prepara la respuesta de la prefirma PAdES
12:01:48,644 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Prefirma PAdES - Firma - FIN
12:01:48,644 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se ha calculado el resultado de la prefirma y se devuelve
12:01:48,645 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == FIN PREFIRMA

Básicamente lee el fichero que se solicita y prepara el hash que tendrá que deberá firmar Autofirma. Con esto se evita devolver todo el fichero.

El resultado es un XML, en base 64, con la siguiente estructura:

<xml>
 <firmas>
  <firma Id="73d6e601-7442-4737-aee2-6aa699cdc393">
   <param n="NEED_PRE">true</param>
   <param n="TIME">1518778908618</param>
   <param n="PRE">(....)</param>
   <param n="PID">WzxkYzQ5NmI1Y2ZjZDAxZDJmNTYwNGQzZWQwNTlhM2RkNz48NGM0YTA2ODc0OTM2MDg1OTNlOTU2MjYxODViYTIyNTI+XQ==</param>
  </firma>
 </firmas>
</xml>

De aquí, lo más salientable es que en el tag "PRE" da la impresión de que se devuelve la el hash a firmar, junto con información del propio certificado.

2 - Autofirma - Server trifase (Postfirma)

Autofirma realiza la firma en local empleando la clave privada del certificado de usuario, que nunca sale del dispositivo. 
Al terminar vuelve llamar al server trifase para que "incruste" esta firma en el PDF.
Los parámetros de la petición son:
POST /afirma-server-triphase-signer/SignatureService

op=post&cop=sign&format=pades&algo=SHA512withRSA&cert=MIII(...)&doc=cHJ1ZWJhMS5wZGY=&session=PHhtb(...)&params=IwojRnJpIEZlYiAxNiAxMjowMTo0OCBDRVQgMjAxOApzZXJ2ZXJVcmw9aHR0cFw6Ly8xOTIuMTY4LjQzLjIzOS9hZmlybWEtc2VydmVyLXRyaXBoYXNlLXNpZ25lci9TaWduYXR1cmVTZXJ2aWNlCg==

Los parámetros son casi los mismos que en el mensaje de prefirma, cambiando la operación. Pero, además, en esta ocasión llega un parámetro session que contiene un XML con el resultado de la firma.
El contenido de session se pinta en los logs del server trifase;
12:01:48,757 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == INICIO FIRMA TRIFASICA ==
12:01:48,764 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recibidos los siguientes datos de sesion para 'post':
<xml>
 <firmas>
  <firma Id="73d6e601-7442-4737-aee2-6aa699cdc393">
   <param n="PRE">MYI(...)</param>
   <param n="PK1">s/5oZ(...)</param>
   <param n="PID">WzxkYzQ5NmI1Y2ZjZDAxZDJmNTYwNGQzZWQwNTlhM2RkNz48NGM0YTA2ODc0OTM2MDg1OTNlOTU2MjYxODViYTIyNTI+XQ==</param>
   <param n="TIME">1518778908618</param>
   <param n="NEED_PRE">true</param>
  </firma>
 </firmas>
</xml>
12:01:48,765 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recuperamos el documento mediante el DocumentManager
12:01:48,766 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recuperamos el documento con identificador: cHJ1ZWJhMS5wZGY=
12:01:48,766 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Buscamos el fichero: D:\tmp\afirma\entrada\prueba1.pdf
12:01:48,767 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Recuperado documento de 175247 octetos
12:01:48,767 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Formato de firma seleccionado: pades
12:01:48,768 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1)  == POSTFIRMA en servidor
12:01:48,804 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Postfirma PAdES - Firma - INICIO
12:01:48,804 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se invocan las funciones internas de postfirma PAdES
12:01:48,820 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Postfirma PAdES - Firma - FIN
12:01:48,820 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1)  Se ha calculado el resultado de la postfirma y se devuelve. Numero de bytes: 227838
12:01:48,820 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Almacenamos la firma mediante el DocumentManager
12:01:48,826 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Escribiendo el fichero: D:\tmp\afirma\salida\prueba1.pdf
12:01:48,826 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Documento almacenado
12:01:48,827 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == FIN POSTFIRMA

Dentro del tag "PRE" se manda lo mismo que se recibió en la petición anterior. A mayores en el campo "PK1" irá la firma en sí.

Como resultado se devuelve un mensaje de OK referenciando el fichero que se acaba de firmar.
OK NEWID=cHJ1ZWJhMS5wZGY=

3 - Autofirma - Signature Storage

Cuando la firma ya se completó, Autofirma llama al Signatura Storage para decirle que cree el fichero de intercambio y que dentro guarde cierta información.
POST /afirma-signature-storage/StorageService

op=put&v=1_0&id=8wp29aKd7L4486I60UrR&dat=5.G0z2jkmD9erRoyvk0bCobg==

Se le indica que cree el fichero 8wp29aKd7L4486I60UrR y que escriba en él el texto indicado en la variable dat. Aparentemente es algún tipo de texto cifrado empleando un algoritmo de clave simétrica...

En el servidor, el Signature Storage genera los siguientes mensajes de log:
12:01:48,865 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1)  == INICIO GUARDADO == 
12:01:48,866 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se solicita guardar un fichero con el identificador: 8wp29aKd7L4486I60UrR
12:01:48,914 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se guardo correctamente el fichero: D:\tmp\afirma\TriStorageRetrieve\8wp29aKd7L4486I60UrR
12:01:48,915 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == FIN DEL GUARDADO ==

4 - Navegador - Signature Retrieve

La forma que tiene el navegador de saber si la firma se ha completado o, en su caso, si se ha producido algún error, es invocando al Signature Retrieve. Se le pide el fichero cuyo nombre se preacordó al comienzo, y que contiene un texto cifrado indicativo de que la firma ha ido bien.

En el servidor, el Signature Retrieve genera los siguientes mensajes de log:
12:01:49,019 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == INICIO DE LA RECUPERACION ==
12:01:49,020 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se solicita el fichero con el identificador: 8wp29aKd7L4486I60UrR
12:01:49,021 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Se recupera el fichero: 8wp29aKd7L4486I60UrR
12:01:49,022 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) == FIN DE LA RECUPERACION ==
12:01:49,022 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Limpiamos el directorio temporal
12:01:49,023 INFO  [es.gob.afirma] (ajp-/0.0.0.0:8009-1) Fin de la limpieza

Al recuperar el resultado de la firma (el navegador no obtiene la firma en sí, porque esta todavía está en el directorio de salida del server trifase) el navegador sabrá si el proceso ha ido bien y, según proceda, llamar a los callbacks de éxito o error.

sábado, 3 de febrero de 2018

JBoss EAP 6.4 and Hibernate 5 - JBoss Logging problems

Problem

When working with Hibernate 5 in JBoss EAP 6.4 you can get the following error: java.lang.NoSuchMethodError: org.jboss.logging.Logger.debugf(Ljava/lang/String;I)V
Caused by: java.lang.NoSuchMethodError: org.jboss.logging.Logger.debugf(Ljava/lang/String;I)V
 at org.hibernate.internal.NamedQueryRepository.checkNamedQueries(NamedQueryRepository.java:149)
 at org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:769)
 at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:484)
 at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
 at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:708)
 at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:724)
 at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:416)
 at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:401)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)

Cause

The root cause is that Hibernate 5 requires jboss-logging-3.3.0.Final.jar library, while JBoss EAP packs jboss-logging-3.1.4.GA-redhat-2.jar. This error is produced when NamedQueryRepository class is trying to call the debugf(String format, Object param1) method of org.jboss.logging.Logger class.
This method exists in version 3.3.0 but not in version 3.1.4. As Jboss loads the former one, there is one incompatibiltiy.

This is the hibernate code which calls this method, org.hibernate.internal.NamedQueryRepository:


And this is the method implementation in org.jboss.logging.Logger: 


Jboss EAP 6.4 is integrated with hibernate-core-4.2.18.Final (https://access.redhat.com/articles/112673#EAP_6), which in turn required jboss-logging-3.1.0 (https://mvnrepository.com/artifact/org.hibernate/hibernate-core/4.2.18.Final)

Solution

In thease cases the solution is obvious. Just use jboss-deployment-structure and exclude Hibernate and Logging stuff in order to use all dependencies packet in the application´s lib directory. So, the first guess would be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>     
  <exclude-subsystems>
   <subsystem name="jpa" /> 
   <subsystem name="logging" />
  </exclude-subsystems>
  
  
  <exclusions>     
   <module name="org.hibernate"/>
   <module name="javax.persistence.api"/>
   <module name="org.hibernate.envers"/>
   <module name="org.hibernate.validator"/>
   <module name="org.hibernate.commons-annotations"/>  
   <module name="org.jboss.as.jpa.hibernate"/> 
   <module name="org.jboss.as.jpa"/> 
   <module name="org.jboss.as.jpa.util"/>  
   <module name="org.jboss.as.jpa.hibernate "/> 
   <module name="org.jboss.as.jpa.spi"/>  
   <module name="org.jboss.as.osgi.jpa"/>
   
   <module name="org.jboss.logging" /> 
   <module name="org.apache.commons.logging" />  
   <module name="org.apache.log4j" />  
   <module name="org.slf4j" />  
   <module name="org.jboss.logging.jul-to-slf4j-stub" />  
  </exclusions>
       
 </deployment>
</jboss-deployment-structure>

Unfortunately this doesn't work. There must be some kind of bug in Jboss EAP 6.4 and jboss-logging 3.1.0 is still be using. For the record, this do works with Jboss EAP 6.2.

After some digging, the solution appears to be to exclude org.jboss.resteasy.resteasy-jaxrs too. The final jboss-deployment-structure will be this:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>     
  <exclude-subsystems>
   <subsystem name="jpa" /> 
   <subsystem name="logging" />
  </exclude-subsystems>
  
  
  <exclusions>
   <module name="org.jboss.resteasy.resteasy-jaxrs"/>     
   <module name="org.hibernate"/>
   <module name="javax.persistence.api"/>
   <module name="org.hibernate.envers"/>
   <module name="org.hibernate.validator"/>
   <module name="org.hibernate.commons-annotations"/>  
   <module name="org.jboss.as.jpa.hibernate"/> 
   <module name="org.jboss.as.jpa"/> 
   <module name="org.jboss.as.jpa.util"/>  
   <module name="org.jboss.as.jpa.hibernate "/> 
   <module name="org.jboss.as.jpa.spi"/>  
   <module name="org.jboss.as.osgi.jpa"/>
   
   <module name="org.jboss.logging" /> 
   <module name="org.apache.commons.logging" />  
   <module name="org.apache.log4j" />  
   <module name="org.slf4j" />  
   <module name="org.jboss.logging.jul-to-slf4j-stub" />  
  </exclusions>
       
 </deployment>
</jboss-deployment-structure>