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)
- libnspr4.so
- libplc4.so
- libplds4.so
- libsoftokn3.so (módulo PKCS11)
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.