martes, marzo 27, 2007

Cómo acceder al keystore de Firefox con Java

Pues resulta que mi última labor ha consistido en acceder al keystore de Firefox para recuperar certificados y firmar con ellos.

La labor ha sido ardua, ya que he encontrado mil referencias en páginas, foros y blogs, pero como casi siempre, he tenido que ir tomando ideas de aquí y de allí e ir construyendo la solución a partir de todas ellas.

El primer camino consistió en tratar de utilizar la librería JSS de Mozilla, pero no hubo manera de que funcionase. Para empezar era necesario colocar una librería en el directorio de instalación de Firefox. Después, necesitabamos firmar (CMS signature) y para ello usabamos las clases de Bouncy Castle, pero no conseguimos firmar con BC con el certificado que obteníamos mediante JSS... ¡¡uff!!.

Hay que decir que realmente sí conseguíamos firmar, pero sólo obteníamos el hash, cuando lo que buscábamos era el formato "detached" (el resultado de firmar es una cadena codificada en base 64 que incluye aparte del hash, información del propio certificado).

Inicialmente, sólo conseguimos firmar solicitando al cliente que indicase el archivo que contenía el certificado. No era lo que queríamos, pero no teníamos otra solución.

Comoquiera que esta solución no satisfacía a nadie, continué investigando, y descubrí la clase sun.security.pkcs11.SunPKCS11, la cual actúa como wrapper sobre las librerías nativas PKCS11 y permite su uso para acceder a los dispositivos que almacenan certificados.

Para usar la clase es necesario pasarle path completo a un archivo de configuración, cuya contenido consiste en una seríe de parámetros que dependen del módulo PKCS11 nativo que vayamos a cargar.

Por último, quedaba saber qué librerías eran necesarias (incluyendo el módulo PKCS11) para poder acceder al keystore de Firefox. Las librerías son también de Mozilla: NSS (Network Security Services) y NSPR.

Podemos obtener las librerías NSS de aquí:

Windows: http://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_11_4_RTM/msvc6.0/WINNT5.0_OPT.OBJ/nss-3.11.4.zip
Linux:
http://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_11_4_RTM/Linux2.6_x86_glibc_PTH_OPT.OBJ/nss-3.11.4.tar.gz

Cuando descomprimimos estos archivos, contienen un directorio lib en el cual aparecen una serie de librerías (.dll para Windows, .so para Linux) de las cuales necesitaremos exáctamente los siguiente archivos:

Windows:
  • freebl3.dll
  • libnspr4.dll
  • libplc4.dll
  • libplds4.dll
  • softokn3.dll (módulo PKCS11)
Linux:
  • libnspr4.so
  • libplc4.so
  • libplds4.so
  • libsoftokn3.so (módulo PKCS11)
Si colocamos estas librerías en el java.library.path, parte de lo que explicaré a continuación no será necesario, pero en mi caso no podía realizar una instalación previa en el cliente, por lo cual mi solución consiste en incluir estos archivos como recursos en el jar que genero y en tiempo de ejecución crear un directorio en "user.home" en el cual escribo los archivos correspondientes al sistema (por supuesto, chequeando previamente si existe el directorio y los archivos están en el mismo).

Antes de poder acceder al keystore, necesitamos algo más. El constructor de la clase SunPKCS11 recibe como parámetro el path a un archivo de configuración que contiene cierta información que depende en parte del la librería nativa PKCS11 que vayamos a usar. En nuestro caso el archivo (test.cfg) debe contener algo parecido a esto:

name = NSS
slot = 2
library = C:/test/softokn3.dll
nssArgs = "configdir='C:/Documents and Settings/usuario/Datos de programa/Mozilla/Firefox/Profiles/zehrgci3.default' certPrefix='' keyPrefix='' secmod='secmod.db' flags=readOnly"

Nota (1): Es muy importante que el caracter separador de archivos sea "/", de lo contrario, obtendremos una excepción en el constructor de la clase SunPKCS11.

Nota (2): Es labor de cada uno averiguar en tiempo de ejecución en qué directorio está almacenado el keystore de Firefox. Lo que es seguro es que está en el "user.dir" o en un directorio dentro del "user.dir". Lo que es seguro es que en el directorio que buscamos deben estar los archivos "cert8.db", "key3.db" y "secmod.db". El directorio exácto en el que se encuentra depende del sistema operativo que usemos.

Lo que debemos hacer es algo parecido a esto:

//Crear el directorio "C:/test/"
//Escribir las librerías correspondientes
//Escribir el archivo de configuración
...
...

// Carga de las librerías en Windows
System.load("C:/test/libnspr4.dll");
System.load("C:/test/libplc4.dll");
System.load("C:/test/libplds4.dll");
System.load("C:/test/softokn3.dll");

//Creamos el Provider con la clase SunPKCS11
Provider nss = new sun.security.pkcs11.SunPKCS11("C:/test/test.cfg");
Security.insertProviderAt(nss, 1);
KeyStore ks = KeyStore.getInstance("PKCS11", nss);
ks.load(null,"".toCharArray());
Enumeration aliases = ks.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = (String) aliases.nextElement();
System.out.println(alias);
}
Security.removeProvider(nss.getName());

En mi siguiente post, enviaré un ejemplo de un código completo y funcional y además explicaré cómo acceder con Java a los certificados almacenados en el DNI electrónico de España.

Si alguien necesita el código inmediatamente, que me envié una respuesta al post.

Saludos,

pedrop.

Acceso a datos con Java

Llevo unos 10 años programando principalmente en Java. A excepción de un año que tuve que hacer mis pinitos con Visual Basic 6. Siempre me ha gustado mucho más Java, me siento más cómodo, no hay ningún otro motivo.

Desde que conocí el lado oscuro, echo de menos la facilidad con la que puedes acceder a fuentes externas como son las bases de datos. En Java no tenemos nada parecido al ADO.NET, y no estoy hablando de calidad, sino de funcionalidad, homogeneidad y facilidad de uso.

Para la persistencia en Java podemos utilizar frameworks como iBatis o el megaarchiconocido Hibernate. Pero muchas veces es demasiado complicado configurar estas magníficas herramientas o el fin para el que lo queremos no merece tanto trabajo o recursos.

Yo propongo esta solución. Consta únicamente de una interfaz y una clase (factory), además de las clases propias que controlan el tipo de conexión que se requiere. No es mio, sino que lo encontré sin querer, pero que desde que lo vi lo llevo siempre en mi pendrive. Siento no recordar el sitio de donde lo saqué para poder hacer la correspondiente referencia.

Los fuentes los podeis descargar de aquí.

Veamos. Lo primero es la interfaz Dao.java. Sólamente tiene el método getConnection. Esta interface será la que tengan que implementar nuestras clases para poder recuperar la conexión.

Estas clases yo las llamo DataAccessXXX, donde XXX normalmente es DIRECT o POOL... y alguna vez LDAP. En ella está la lógica para la obtención de la conexión.

Y por último, pero no menos importante por ello, tenemos la clase FactoryDao. Es una singelton que nos proporciona una instancia de nuesta clase DataAccessXXX a través de reflexión. La clase que debe instanciar se indica a través de los parámetros de configuración del fichero config.properties. Tanto esta clase como la DataAccessXXX utilizan el fichero de propiedades de donde recuperan el valor de los parámetros que necesitan.

La clase Main.java que acompaña a las anteriores muestra cómo utilizar todo este tingladillo.

Estoy seguro de que existen formas más bonitas de hacerlo, pero a mi esta forma me resulta limpia y clara, además de muy fácil de usar y portar.

Espero que os guste.

P.D.
Gracias a mi compañero de curro Miguel Jimenez y su blog por el alojamiento de los fuentes.