/** * LICENCIA LGPL: * * Esta librería es Software Libre; Usted puede redistribuirla y/o modificarla * bajo los términos de la GNU Lesser General Public License (LGPL) tal y como * ha sido publicada por la Free Software Foundation; o bien la versión 2.1 de * la Licencia, o (a su elección) cualquier versión posterior. * * Esta librería se distribuye con la esperanza de que sea útil, pero SIN * NINGUNA GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o * ADECUACIÓN A UN PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public * License (LGPL) para más detalles * * Usted debe recibir una copia de la GNU Lesser General Public License (LGPL) * junto con esta librería; si no es así, escriba a la Free Software Foundation * Inc. 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte * . * * Copyright 2011 Agencia de Tecnología y Certificación Electrónica */ package es.accv.arangi.base.device; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import org.apache.log4j.Logger; import sun.security.pkcs11.SunPKCS11; import com.sun.security.auth.callback.DialogCallbackHandler; import es.accv.arangi.base.device.util.CustomDialogCallbackHandler; import es.accv.arangi.base.device.util.MozillaPkcs11Manager; import es.accv.arangi.base.exception.device.InitializeProviderException; import es.accv.arangi.base.exception.device.LoadingObjectException; import es.accv.arangi.base.exception.device.MozillaKeyStoreException; import es.accv.arangi.base.exception.device.MozillaNotFoundException; import es.accv.arangi.base.exception.device.OpeningDeviceException; import es.accv.arangi.base.util.Util; /** * Clase para tratar almacenes de claves de Mozilla Firefox.

* * Este almacén es el propio del navegador Firefox. Si se desea, la clase puede tratar * los certificados que se encuentran en dispositivos PKCS#11 y que han sido configurados * como dispositivos de seguridad en Firefox. Esta solución es una buena idea en sistemas * Linux donde no se dispone del almacén de Windows para gestionar los certificados en * tarjeta.

* * NOTA: Esta clase no es capaz de trabajar con Firefox en Mac OS X por problemas con las * dependencias del módulo PKCS#11.

* * Ejemplo de uso:

* * * IDocument document = new FileDocument(new File ("/documento.txt"));
* MozillaKeyStoreManager manager = new MozillaKeyStoreManager ("Escriba contraseña maestra Firefox : ", "Contraseña maestra Firefox", false);
* String aliases = manager.getAliasNamesList();
* for (int i=0;i *   System.out.println ("Certificate: " + manager.getCertificate(aliases[i]));
*   System.out.println ("Firma: " + manager.signDocument(document, aliases[i])); // Firma con la clave privada del alias
* }
*

* * @author José M Gutiérrez */ public class MozillaKeyStoreManager extends AbstractBrowserKeyStoreManager { /* * Logger de la clase */ Logger logger = Logger.getLogger(MozillaKeyStoreManager.class); /* * Nombre del provider */ String PROVIDER_NAME = "NSS"; /* * Array de almacenes PKCS#11 que utiliza Mozilla */ List pkcs11Managers; /* * Texto del dialogo en el que se pedirá el pin al usuario */ String textPinDialog = null; /* * Texto del título del dialogo en el que se pedirá el pin al usuario */ String titleDialog = null; /** * Constructor: inicializa el proveedor de SUN si es necesario para acceder al * repositorio de Mozilla.

* * Se cargarán los módulos PKCS#11 asociados al almacén de claves de Mozilla. * * @throws MozillaNotFoundException No se ha podido encontrar la carpeta de * instalación de Mozilla en los archivos de programa o no se ha encontrado * la carpeta del perfil de Mozilla del usuario. * @throws MozillaKeyStoreException No se puede abrir el almacén de claves del * usuario de Mozilla. */ public MozillaKeyStoreManager () throws MozillaNotFoundException, MozillaKeyStoreException { initKeystores(new DialogCallbackHandler(), false); } /** * Constructor: inicializa el proveedor de SUN si es necesario para acceder al * repositorio de Mozilla.

* * Se cargarán los módulos PKCS#11 asociados al almacén de claves de Mozilla. * * @param textPinDialog texto del dialogo en el que se pedirá el pin al usuario. * @param titleDialog texto del título del dialogo en el que se pedirá el pin al usuario. * * @throws MozillaNotFoundException No se ha podido encontrar la carpeta de * instalación de Mozilla en los archivos de programa o no se ha encontrado * la carpeta del perfil de Mozilla del usuario. * @throws MozillaKeyStoreException No se puede abrir el almacén de claves del * usuario de Mozilla. */ public MozillaKeyStoreManager (String textPinDialog, String titleDialog) throws MozillaNotFoundException, MozillaKeyStoreException { this.textPinDialog = textPinDialog; this.titleDialog = titleDialog; initKeystores(new CustomDialogCallbackHandler(textPinDialog, titleDialog), false); } /** * Constructor: inicializa el proveedor de SUN si es necesario para * acceder al repositorio de Mozilla. * * @param withoutPkcs11Modules Si es cierto no se cargarán los módulos PKCS#11 * asociados al almacén de claves de Mozilla. * @throws MozillaNotFoundException No se ha podido encontrar la carpeta de * instalación de Mozilla en los archivos de programa o no se ha encontrado * la carpeta del perfil de Mozilla del usuario. * @throws MozillaKeyStoreException No se puede abrir el almacén de claves del * usuario de Mozilla. */ public MozillaKeyStoreManager (boolean withoutPkcs11Modules) throws MozillaNotFoundException, MozillaKeyStoreException { initKeystores(new DialogCallbackHandler(), withoutPkcs11Modules); } /** * Constructor: inicializa el proveedor de SUN si es necesario para * acceder al repositorio de Mozilla. * * @param textPinDialog texto del dialogo en el que se pedirá el pin al usuario. * @param titleDialog texto del título del dialogo en el que se pedirá el pin al usuario. * @param withoutPkcs11Modules Si es cierto no se cargarán los módulos PKCS#11 * asociados al almacén de claves de Mozilla. * * @throws MozillaNotFoundException No se ha podido encontrar la carpeta de * instalación de Mozilla en los archivos de programa o no se ha encontrado * la carpeta del perfil de Mozilla del usuario. * @throws MozillaKeyStoreException No se puede abrir el almacén de claves del * usuario de Mozilla. */ public MozillaKeyStoreManager (String textPinDialog, String titleDialog, boolean withoutPkcs11Modules) throws MozillaNotFoundException, MozillaKeyStoreException { this.textPinDialog = textPinDialog; this.titleDialog = titleDialog; initKeystores(new CustomDialogCallbackHandler(textPinDialog, titleDialog), withoutPkcs11Modules); } //-- Métodos públicos /** * Cerrar el keystore y eliminar el provider de SUN de la lista de proveedores */ public void close() { logger.debug ("Cerrando Keystore..."); ks = null; //-- Eliminar provider de SUN logger.debug("[CLOSE]::Eliminando el provider de SUN"); Security.removeProvider("SunPKCS11-NSS"); logger.debug("[CLOSE]::Eliminado el provider de SUN"); logger.debug("Cerrado Keystore...OK"); //-- Cerrar PKCS11s for (Iterator iterator = pkcs11Managers.iterator(); iterator.hasNext();) { MozillaPkcs11Manager pkcs11Manager = iterator.next(); logger.debug("[MozillaKeyStoreManager.getAliasNamesList]::Cerrando '" + pkcs11Manager.getProviderName() + "'"); pkcs11Manager.close(); } } @Override public void initialize() { if (textPinDialog != null || titleDialog != null) { try { initKeystores(new DialogCallbackHandler(), false); } catch (MozillaKeyStoreException e) { logger.info("[MozillaKeyStoreManager.initialize]:: Error al reinicializar el KeyStore", e); } catch (MozillaNotFoundException e) { logger.info("[MozillaKeyStoreManager.initialize]:: Error al reinicializar el KeyStore", e); } } else { try { initKeystores(new CustomDialogCallbackHandler(textPinDialog, titleDialog), false); } catch (MozillaKeyStoreException e) { logger.info("[MozillaKeyStoreManager.initialize]:: Error al reinicializar el KeyStore", e); } catch (MozillaNotFoundException e) { logger.info("[MozillaKeyStoreManager.initialize]:: Error al reinicializar el KeyStore", e); } } } /* * (non-Javadoc) * @see es.accv.arangi.base.device.DeviceManager#getAliasNamesList() */ public String[] getAliasNamesList() { logger.debug("Obteniendo todos los alias..."); //-- Obtener los alias del keystore Enumeration enumAlias = null; try { enumAlias = ks.aliases(); } catch (KeyStoreException e) { //-- Este error sólo se produce si no se ha inicializado la clase, lo que //-- ya se comprueba y no puede ocurrir logger.debug("[MozillaKeyStoreManager.getAliasNamesList]::Keystore no inicializado", e); } List lAlias = new ArrayList (); while (enumAlias != null && enumAlias.hasMoreElements()) { String alias = (String) enumAlias.nextElement(); lAlias.add(alias); } //-- Obtener los alias de los PKCS#11 for (Iterator iterator = pkcs11Managers.iterator(); iterator.hasNext();) { MozillaPkcs11Manager pkcs11Manager = iterator.next(); //-- Obtener los alias logger.debug("[MozillaKeyStoreManager.getAliasNamesList]::Obteniendo alias para '" + pkcs11Manager.getProviderName() + "'"); lAlias.addAll(pkcs11Manager.getAliasNamesList()); } return (String[]) lAlias.toArray(new String[0]); } /* * (non-Javadoc) * @see es.accv.arangi.base.device.DeviceManager#getCertificate(java.lang.String) */ public X509Certificate getCertificate(String alias) { logger.debug("getCertificate..."); logger.debug("alias: " + alias); //-- Buscar en el keystore try { X509Certificate certificate = (X509Certificate) ks.getCertificate(alias); logger.debug ("[MozillaKeyStoreManager.getCertificate]::Obtenido certificado::" + certificate); if (certificate != null) { return certificate; } } catch (KeyStoreException e) { //-- Este error sólo se produce si no se ha inicializado la clase, lo que //-- ya se comprueba y no puede ocurrir logger.debug("[MozillaKeyStoreManager.getCertificate]::Keystore no inicializado", e); } //-- Buscar en los alias de los PKCS#11 X509Certificate certificate; for (Iterator iterator = pkcs11Managers.iterator(); iterator.hasNext();) { MozillaPkcs11Manager pkcs11Manager = iterator.next(); //-- Buscar logger.debug("[MozillaKeyStoreManager.getCertificate]::Buscando en los alias para '" + pkcs11Manager.getProviderName() + "'"); if ((certificate = pkcs11Manager.getCertificate(alias)) != null) { logger.debug("[MozillaKeyStoreManager.getCertificate]::Encontrado alias '" + alias + "' en '" + pkcs11Manager.getProviderName() + "'"); return certificate; } } logger.info("[MozillaKeyStoreManager.getCertificate]::No se ha encontrado el alias '" + alias + "'"); return null; } /* * (non-Javadoc) * @see es.accv.arangi.base.device.DeviceManager#getPrivateKey(java.lang.String, java.lang.String) */ public PrivateKey getPrivateKey(String alias, String keyPassword) throws LoadingObjectException{ logger.debug("[MozillaKeyStoreManager.getPrivateKey]::Entrada::" + Arrays.asList(new Object [] { alias })); //-- Buscar en el keystore try { //-- Buscar por alias y viendo que sea una clave if (!isAliasFree(alias) && ks.isKeyEntry(alias)) { logger.debug("[MozillaKeyStoreManager.getPrivateKey]::Encontrada clave privada con alias '" + alias + "'"); return (PrivateKey) ks.getKey(alias, keyPassword.toCharArray()); } } catch (KeyStoreException e) { //-- Este error sólo se produce si no se ha inicializado la clase, lo que //-- ya se comprueba y no puede ocurrir logger.debug("[MozillaKeyStoreManager.getPrivateKey]::Keystore no inicializado", e); } catch (NoSuchAlgorithmException e) { //-- Este error sólo se produce si no se ha cargado Bouncy, con lo que //-- el error se habría producido mucho antes logger.debug("[MozillaKeyStoreManager.getPrivateKey]::No se encuentra el algoritmo para alguno de los certificados de la cadena", e); return null; } catch (UnrecoverableKeyException e) { logger.info("[MozillaKeyStoreManager.getPrivateKey]::La clave privada del alias '" + alias + "' no ha podido ser recuperada. " + "Compruebe que su password coincide con el PIN del dispositivo.", e); throw new LoadingObjectException("La clave privada del alias '" + alias + "' no ha podido ser recuperada. Compruebe " + "que su password coincide con el PIN del dispositivo.", e); } //-- Buscar en los alias de los PKCS#11 PrivateKey pk; for (Iterator iterator = pkcs11Managers.iterator(); iterator.hasNext();) { MozillaPkcs11Manager pkcs11Manager = iterator.next(); //-- Buscar logger.debug("[MozillaKeyStoreManager.getPrivateKey]::Buscando en los alias para '" + pkcs11Manager.getProviderName() + "'"); if ((pk = pkcs11Manager.getPrivateKey(alias)) != null) { logger.debug("[MozillaKeyStoreManager.getPrivateKey]::Encontrado alias '" + alias + "' en '" + pkcs11Manager.getProviderName() + "'"); return pk; } } logger.info("[MozillaKeyStoreManager.getPrivateKey]::No se ha encontrado una pk en el alias '" + alias + "'"); return null; } /* (non-Javadoc) * @see es.accv.arangi.base.device.DeviceManager#isAliasFree(java.lang.String) */ public boolean isAliasFree(String alias){ logger.debug ("[MozillaKeyStoreManager.isAliasFree]::Entrada::" + alias); try { if (ks.containsAlias(alias)) { return false; } } catch (KeyStoreException e) { logger.info("[MozillaKeyStoreManager.isAliasFree]::Error comprobando si existe el alias '" + alias + "'", e); } //-- Buscar en los alias de los PKCS#11 for (Iterator iterator = pkcs11Managers.iterator(); iterator.hasNext();) { MozillaPkcs11Manager pkcs11Manager = iterator.next(); //-- Buscar logger.debug("[MozillaKeyStoreManager.isAliasFree]::Buscando en los alias para '" + pkcs11Manager.getProviderName() + "'"); if (pkcs11Manager.getAliasNamesList().contains(alias)) { logger.debug("[MozillaKeyStoreManager.isAliasFree]::Encontrado alias '" + alias + "' en '" + pkcs11Manager.getProviderName() + "'"); return false; } } return true; } //-- Métodos privados /* * Inicializa el keystore de Mozilla y los keystores pkcs11 asociados */ private void initKeystores(CallbackHandler handler, boolean withoutPkcs11Modules) throws MozillaKeyStoreException, MozillaNotFoundException{ List lKeystores = new ArrayList(); this.pin = ""; this.ksType = STORE_TYPE_MOZILLA; SunPKCS11 pk11provider; if (!Util.existsCryptographicProvider(SUN_PROVIDER_PREFIX + PROVIDER_NAME)) { //-- Buscar la carpeta de instalación de Mozilla File mozillaFolder = getMozillaFolder (); //-- Buscar la carpeta para el secmod File secModFolder = getSecModFolder (); //-- Cargar las librerías de Mozilla try { if (System.getProperty("os.name").startsWith("Mac OS X")) { configureMacNSS(mozillaFolder); } else { loadMozillaLibraries (mozillaFolder); } } catch (Throwable e) { logger.info("[MozillaKeyStoreManager.initKeystores]::No se han podido cargar las librerías para Mozilla Firefox", e); throw new MozillaKeyStoreException("No se han podido cargar las librerías para Mozilla Firefox", e); } //-- Obtener el path al fichero de la librería int javaVersion = 6; if (System.getProperty("java.version").indexOf(".") > -1) { try { javaVersion = Integer.parseInt(System.getProperty("java.version").split("\\.")[1]); } catch (Exception e) { logger.info("No se puede determinar la versión de Java: " + System.getProperty("java.version")); } } String pathLibrary; try { pathLibrary = new File (mozillaFolder, getMozillaNSSLibraryName()).getCanonicalPath(); } catch (IOException e1) { pathLibrary = new File (mozillaFolder, getMozillaNSSLibraryName()).getAbsolutePath(); } if (javaVersion > 7) { pathLibrary = "\"" + pathLibrary.replace('\\', '/') + "\""; } try { //-- Configurar el provider String providerConfig = "name = " + PROVIDER_NAME + "\r" + "library = " + pathLibrary + "\r" + "attributes= compatibility" + "\r" + "slot=2\r" + "nssArgs=\"" + "configdir='" + secModFolder.getCanonicalPath() + "' " + "certPrefix='' " + "keyPrefix='' " + "secmod=' secmod.db' " + "flags=readWrite\"\r"; providerConfig = providerConfig.replaceAll("\\\\", "/"); //-- Inicializar el provider ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(providerConfig.getBytes()); pk11provider = new SunPKCS11(localByteArrayInputStream); Security.addProvider(pk11provider); } catch (Throwable e) { logger.info("[MozillaKeyStoreManager.initKeystores]::No se ha podido inicializar el proveedor criptográfico PKCS#11 para Mozilla Firefox", e); throw new MozillaKeyStoreException("No se ha podido inicializar el proveedor criptográfico PKCS#11 para Mozilla Firefox", e); } } else { pk11provider = (SunPKCS11) Security.getProvider(SUN_PROVIDER_PREFIX + PROVIDER_NAME); } try { pk11provider.login(new Subject(), handler); ks = KeyStore.getInstance("PKCS11",pk11provider); ks.load(null, null); lKeystores.add(ks); } catch (Exception e) { logger.info("[MozillaKeyStoreManager.initKeystores]::No se ha podido abrir el almacén de claves del usuario de Mozilla Firefox", e); throw new MozillaKeyStoreException("No se ha podido abrir el almacén de claves del usuario de Mozilla Firefox", e); } //-- Tarjetas tratadas con Mozilla pkcs11Managers = new ArrayList(); if (withoutPkcs11Modules) { //-- No se usan los PKCS#11 asociados a Mozilla logger.debug("[MozillaKeyStoreManager.initKeystores]::No se usan los PKCS#11 asociados a Mozilla"); } else { logger.debug("[MozillaKeyStoreManager.initKeystores]::Se usan los PKCS#11 asociados a Mozilla"); Hashtable hmPkcs11Modules = getMozillaPKCS11Modules(); Enumeration enumeration = hmPkcs11Modules.keys(); while (enumeration.hasMoreElements()) { String name = enumeration.nextElement(); try { MozillaPkcs11Manager pkcs11Manager = new MozillaPkcs11Manager(name, hmPkcs11Modules.get(name)); pkcs11Managers.add(pkcs11Manager); } catch(InitializeProviderException e) { logger.info("[MozillaKeyStoreManager.initKeystores]::No se ha podido cargar el proveedor PKCS#11 para el módulo '" + name + "'", e); continue; } catch(OpeningDeviceException e) { logger.info("[MozillaKeyStoreManager.initKeystores]::No se ha podido abrir el almacén de claves del módulo '" + name + "'", e); continue; } catch (Throwable e) { logger.info("[MozillaKeyStoreManager.initKeystores]::Error desconocido al cargar el proveedor PKCS#11 para el módulo '" + name + "'", e); continue; } logger.debug("[MozillaKeyStoreManager.initKeystores]::Se ha inicializado el módulo '" + name + "'"); } } } //-- Métodos privados /** * Obtiene la ruta a la carpeta donde se encuentra instalado Mozilla * * @return Ruta a la carpeta donde se encuentra instalado Mozilla * @throws MozillaNotFoundException No se ha encontrado una instalación de Mozilla */ private File getMozillaFolder() throws MozillaNotFoundException { //-- Si el SO es Windows ... if (System.getProperty("os.name").contains("indows")) { logger.debug("[MozillaKeyStoreManager]::Buscando la carpeta de instalación de Mozilla en Windows"); File mozillaFolder = new File (new File (System.getenv("PROGRAMFILES")), "Mozilla Firefox"); if (mozillaFolder.exists()) { logger.debug("[MozillaKeyStoreManager]::Se ha encontrado la carpeta de instalación de Mozilla Firefox en Windows: " + mozillaFolder.getAbsolutePath()); //-- Si el path al directorio contiene caracteres extraños no funcionará la carga del //-- provider PKCS#11 de SUN if (mozillaFolder.getAbsolutePath().contains(")") || mozillaFolder.getAbsolutePath().contains("(") || mozillaFolder.getAbsolutePath().contains("\u007E")) { //-- Copiar las DLLs a una ubicación temporal que será la que utilizará a partir de ahora try { logger.debug("[MozillaKeyStoreManager]::La carpeta de instalación de Mozilla Firefox en Windows contiene caracteres incompatibles con provider PKCS11: " + mozillaFolder.getAbsolutePath()); File mozillaTempFolder = File.createTempFile("mozillaTemp", null); mozillaTempFolder.delete(); mozillaTempFolder.mkdirs(); try { Util.copyFile(new File (mozillaFolder, "softokn3.dll"), new File (mozillaTempFolder, "softokn3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "plc4.dll"), new File (mozillaTempFolder, "plc4.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "plds4.dll"), new File (mozillaTempFolder, "plds4.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "nspr4.dll"), new File (mozillaTempFolder, "nspr4.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "nss3.dll"), new File (mozillaTempFolder, "nss3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "mozutils.dll"), new File (mozillaTempFolder, "mozutils.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "mozglue.dll"), new File (mozillaTempFolder, "mozglue.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "mozsqlite3.dll"), new File (mozillaTempFolder, "mozsqlite3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "sqlite3.dll"), new File (mozillaTempFolder, "sqlite3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "mozcrt19.dll"), new File (mozillaTempFolder, "mozcrt19.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "nssutil3.dll"), new File (mozillaTempFolder, "nssutil3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "freebl3.dll"), new File (mozillaTempFolder, "freebl3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "nssdbm3.dll"), new File (mozillaTempFolder, "nssdbm3.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "msvcr120.dll"), new File (mozillaTempFolder, "msvcr120.dll")); } catch (FileNotFoundException e) {} try { Util.copyFile(new File (mozillaFolder, "msvcp120.dll"), new File (mozillaTempFolder, "msvcp120.dll")); } catch (FileNotFoundException e) {} return mozillaTempFolder; } catch (Exception e) { logger.info("[MozillaKeyStoreManager]::No se han podido cargar las DLLs de Mozilla Firefoxen una carpeta temporal", e); throw new MozillaNotFoundException("No se han podido cargar las DLLs de Mozilla Firefoxen una carpeta temporal", e); } } return mozillaFolder; } } //-- Si el SO es Linux / Unix ... if(System.getProperty("os.name").contains("inux") || System.getProperty("os.name").contains("SunOS") || System.getProperty("os.name").contains("olaris")){ logger.debug("[MozillaKeyStoreManager]::Buscando la carpeta con las librerías de Mozilla en Linux/Unix"); String[] posiblesCarpetas = new String[] { "/usr/lib/firefox-" + searchLastFirefoxVersion("/usr/lib/"), "/usr/lib/firefox", "/opt/firefox", "/opt/firefox-" + searchLastFirefoxVersion("/opt/"), "/lib", "/usr/lib", "/usr/lib/nss", "/opt/fedora-ds/clients/lib", "/usr/lib/i386-linux-gnu/nss" }; for(String carpeta : posiblesCarpetas) { if (new File(new File (carpeta), "libsoftokn3.so").exists()) { logger.debug("[MozillaKeyStoreManager]::Se ha encontrado la carpeta con las librerías de Mozilla Firefox en Linux/Unix: " + carpeta); return new File(carpeta); } } } //-- Si el SO es Mac OS X if(System.getProperty("os.name").startsWith("Mac OS X")) { logger.debug("[MozillaKeyStoreManager]::Buscando la carpeta con las librerías de Mozilla en Mac OS X"); String[] posiblesCarpetas = new String[] { "/Applications/Firefox.app/Contents/MacOS", "/lib", "/usr/lib", "/usr/lib/nss", "/Applications/Minefield.app/Contents/MacOS" }; for(String carpeta : posiblesCarpetas) { if (new File(new File (carpeta), "libsoftokn3.dylib").exists()) { logger.debug("[MozillaKeyStoreManager]::Se ha encontrado la carpeta con las librerías de Mozilla Firefox en Mac OS X: " + carpeta); return new File(carpeta); } } } logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta de instalación de Mozilla Firefox"); throw new MozillaNotFoundException("No se ha podido obtener la carpeta de instalación de Mozilla Firefox"); } /** * Obtiene la ruta donde se encuentran los ficheros con el almacén de claves de Mozilla * * @return Ruta donde se encuentran los ficheros con el almacén de claves de Mozilla * @throws MozillaNotFoundException No se ha encontrado una instalación de Mozilla */ private File getSecModFolder() throws MozillaNotFoundException { if (System.getProperty("os.name").contains("indows")) { logger.debug("[MozillaKeyStoreManager]::Buscando el almacén de claves de Mozilla en Windows"); //-- Buscar carpeta de mozilla en Datos de Programa del usuario File secModFolder = new File (new File (System.getenv("APPDATA")), "Mozilla/Firefox"); if (!secModFolder.exists() || secModFolder.list() == null || secModFolder.list().length == 0) { logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta con el perfil del usuario de Mozilla Firefox: " + secModFolder.getAbsolutePath()); throw new MozillaNotFoundException("No se ha podido obtener la carpeta con el perfil del usuario de Mozilla Firefox: " + secModFolder.getAbsolutePath()); } logger.debug("[MozillaKeyStoreManager]::La carpeta de usuario de Mozilla Firefox es: " + secModFolder.getAbsolutePath()); //-- Obtener el fichero de inicio que contiene la referencia de los perfiles File iniFile = new File (secModFolder, "profiles.ini"); if (!iniFile.exists() || !iniFile.isFile()) { logger.info("[MozillaKeyStoreManager]::No se ha podido obtener el fichero de inicio con los perfiles de Mozilla: " + iniFile.getAbsolutePath()); throw new MozillaNotFoundException("No se ha podido obtener el fichero de inicio con los perfiles de Mozilla: " + iniFile.getAbsolutePath()); } //-- Obtener la carpeta del perfil try { return getFirefoxProfileDirectory (iniFile); } catch (IOException e) { logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta con el perfil del usuario en Windows", e); throw new MozillaNotFoundException("No se ha podido obtener la carpeta con el perfil del usuario en Windows", e); } } //-- Si estamos en un Linux... logger.debug("[MozillaKeyStoreManager]::Buscando el almacén de claves de Mozilla en Linux"); File regFile = new File(new File (System.getProperty("user.home")), "/.mozilla/firefox/profiles.ini"); if (regFile.exists()) { try { return getFirefoxProfileDirectory(regFile); } catch (Exception e) { logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta con el perfil del usuario en Linux", e); throw new MozillaNotFoundException("No se ha podido obtener la carpeta con el perfil del usuario en Linux", e); } } //-- Si es un Mac OS X... regFile = new File(new File (System.getProperty("user.home")), "/Library/Application Support/Firefox/profiles.ini"); if (regFile.exists()) { try { return getFirefoxProfileDirectory(regFile); } catch (Exception e) { logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta con el perfil del usuario en Mac OS X", e); throw new MozillaNotFoundException("No se ha podido obtener la carpeta con el perfil del usuario en Mac OS X", e); } } //-- Intento con el registro clasico de Mozilla regFile = new File(new File (System.getProperty("user.home")), "/.mozilla/appreg"); if (regFile.exists()) { try { return getFirefoxProfileDirectory(regFile); } catch (Exception e) { logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta con el perfil del usuario con el registro clásico de Mozilla", e); throw new MozillaNotFoundException("No se ha podido obtener la carpeta con el perfil del usuario con el registro clásico de Mozilla", e); } } logger.info("[MozillaKeyStoreManager]::No se ha podido obtener la carpeta con el perfil del usuario de Mozilla Firefox"); throw new MozillaNotFoundException("No se ha podido obtener la carpeta con el perfil del usuario de Mozilla Firefox"); } /** * Devuelve el directorio del perfil activo de Firefox. Si no hubiese perfil activo, * devolvería el directorio del perfil por defecto y si tampoco lo hubiese * el del primer perfil encontrado. Si no hubiese perfiles configurados, * devolvería {@code null}. * * @param iniFile Fichero con la información de los perfiles de Firefox. * @return Directorio con la información del perfil. * @throws IOException Cuando ocurre un error abriendo o leyendo el fichero. */ private File getFirefoxProfileDirectory(File iniFile) throws IOException { String currentProfilePath = null; // Leemos el fichero con la informacion de los perfiles y buscamos el activo(el que esta bloqueado) FirefoxProfile[] profiles = readProfiles(iniFile); for (FirefoxProfile profile : profiles) { if (isProfileLocked(profile)) { currentProfilePath = profile.absolutePath; logger.debug("[MozillaKeyStoreManager]::Perfil actual activo: " + currentProfilePath); return new File (currentProfilePath); } } // Si no hay ninguno actualmente activo, tomamos el por defecto if (currentProfilePath == null) { for (FirefoxProfile profile : profiles) { if (profile.isDefault) { currentProfilePath = profile.absolutePath; logger.debug("[MozillaKeyStoreManager]::Perfil actual por defecto: " + currentProfilePath); return new File (currentProfilePath); } } } // Si no hay ninguno por defecto, se toma el primero if (profiles.length > 0) { currentProfilePath = profiles[0].absolutePath; logger.debug("[MozillaKeyStoreManager]::Primer perfil: " + currentProfilePath); return new File (currentProfilePath); } return null; } /** * Parsea la informacion de los perfiles declarada en el fichero "profiles.ini". * Para identificar correctamente los perfiles es necesario que haya al menos una * línea de separación entre los bloques de información de * cada perfil. * @param iniFile Fichero con lainformación de los perfiles. * @return Listado de perfiles completos encontrados. * @throws IOException Cuando se produce un error durante la lectura de la * configuración. */ private static FirefoxProfile[] readProfiles(File iniFile) throws IOException { final String NAME_ATR = "name="; final String IS_RELATIVE_ATR = "isrelative="; final String PATH_PROFILES_ATR = "path="; final String IS_DEFAULT_ATR = "default="; String line = null; Vector profiles = new Vector(); BufferedReader in = new BufferedReader(new FileReader(iniFile)); try { while ((line = in.readLine()) != null) { // Buscamos un nuevo bloque de perfil if (!line.trim().toLowerCase().startsWith("[profile")) continue; FirefoxProfile profile = new FirefoxProfile(); while((line = in.readLine()) != null && line.trim().length() > 0 && !line.trim().toLowerCase().startsWith("[profile")) { if(line.trim().toLowerCase().startsWith(NAME_ATR)) profile.name = line.trim().substring(NAME_ATR.length()); else if(line.trim().toLowerCase().startsWith(IS_RELATIVE_ATR)) profile.isRelative = line.trim().substring(IS_RELATIVE_ATR.length()).equals("1"); else if(line.trim().toLowerCase().startsWith(PATH_PROFILES_ATR)) profile.path = line.trim().substring(PATH_PROFILES_ATR.length()); else if(line.trim().toLowerCase().startsWith(IS_DEFAULT_ATR)) profile.isDefault = line.trim().substring(IS_DEFAULT_ATR.length()).equals("1"); else break; } // Debemos encontrar al menos el nombre y la ruta del perfil if (profile.name != null || profile.path != null) { profile.absolutePath = profile.isRelative ? new File(iniFile.getParent(), profile.path).toString() : profile.path; profiles.add(profile); } } } catch (Exception e) { throw new IOException("Ocurrio un error al leer la configuracion de los perfiles de Firefox: " + e); } finally { try { in.close(); } catch (Exception e) {} } return profiles.toArray(new FirefoxProfile[profiles.size()]); } /** * Comprueba que un perfil de Firefox esté bloqueado. Un perfil esta * bloqueado cuando en su directorio se encuentra el fichero "parent.lock". * @param profile Información del perfil de Firefox. * @return Devuelve {@code true} si el perfil esta bloqueado, {@code false} * en caso contrario. */ private static boolean isProfileLocked(FirefoxProfile profile) { return new File(profile.absolutePath, "parent.lock").exists() || // En Windows new File(profile.absolutePath, "lock").exists(); // En Linux } /** * Obtiene el nombre de la librería del provider NSS dependiendo del SO * * @return Nombre de la librería del provider NSS */ private String getMozillaNSSLibraryName () { String nssLib = "libsoftokn3.so"; if(System.getProperty("os.name").contains("indows")) { nssLib = "softokn3.dll"; } else if (System.getProperty("os.name").startsWith("Mac OS X")) { nssLib = "libsoftokn3.dylib"; } return nssLib; } /** * Carga las librerías de mozilla * * @param mozillaFolder Carpeta donde se encuentran las librerías */ private void loadMozillaLibraries(File mozillaFolder) throws Throwable { logger.debug("[MozillaKeyStoreManager.loadMozillaLibraries]::Entrada::" + mozillaFolder); String[] librariesNames = null; if (!System.getProperty("os.name").contains("indows") && !System.getProperty("os.name").contains("inux") && !System.getProperty("os.name").contains("SunOS") && !System.getProperty("os.name").contains("olaris")) { throw new Exception ("Sistema Operativo desconocido"); } // Fedora: librerías entre /usr/lib y /lib if (System.getProperty("os.name").contains("inux") && new File("/usr/lib/libsoftokn3.so").exists() && new File("/lib/libnspr4.so").exists()) { librariesNames = new String[] { System.mapLibraryName("/usr/lib/libsoftokn3"), System.mapLibraryName("/lib/libplds4"), System.mapLibraryName("/usr/lib/libplds4"), System.mapLibraryName("/lib/libplc4"), System.mapLibraryName("/usr/lib/libplc4"), System.mapLibraryName("/lib/libnssutil3"), System.mapLibraryName("/usr/lib/libnssutil3"), System.mapLibraryName("/lib/libsqlite3"), System.mapLibraryName("/usr/lib/libsqlite3"), System.mapLibraryName("/lib/libmozsqlite3"), System.mapLibraryName("/usr/lib/libmozsqlite3") }; } //-- Windows if (System.getProperty("os.name").contains("indows")) { librariesNames = new String[] { System.mapLibraryName("msvcr120"), System.mapLibraryName("msvcp120"), System.mapLibraryName("mozcrt19"), System.mapLibraryName("mozutils"), System.mapLibraryName("mozglue"), System.mapLibraryName("sqlite3"), System.mapLibraryName("mozsqlite3"), System.mapLibraryName("nspr4"), System.mapLibraryName("plds4"), System.mapLibraryName("plc4"), System.mapLibraryName("nss3"), System.mapLibraryName("nssutil3"), System.mapLibraryName("nssdbm3"), System.mapLibraryName("freebl3"), System.mapLibraryName("nss3") }; } //-- Linux/Unix if (System.getProperty("os.name").contains("inux") || System.getProperty("os.name").contains("SunOS") || System.getProperty("os.name").contains("olaris")) { librariesNames = new String[] { "libnspr4.so", "libplds4.so", "libplc4.so", "libnssutil3.so", "libsqlite3.so", "libmozsqlite3.so" }; } for(String libraryName : librariesNames) { try { System.load(new File (mozillaFolder, libraryName).getAbsolutePath()); } catch (UnsatisfiedLinkError e) { logger.debug("No se ha podido cargar la librería: " + new File (mozillaFolder, libraryName).getAbsolutePath(), e); } } } /** * Obtiene las rutas completas hacia las bibliotecas (.dll o .so) de los módulos de seguridad externos (PKCS#11) * instalados en Mozilla / Firefox, indexados por su descripción dentro de una * Hashtable. * @return Nombres de las bibliotecas de los módulos de seguridad de Mozilla / Firefox */ private Hashtable getMozillaPKCS11Modules() { try { final Hashtable modsByDesc = new Hashtable(); for(MozillaPkcs11Module module : getCardModules()) { modsByDesc.put(module.getName(), module.getLibrary()); } return modsByDesc; } catch(Throwable t) { return new Hashtable(0); } } /** * Obtiene los módulos de seguridad PKCS#11 instalados en la base de datos secmod.db. * @return Vector con los módulos encontrados, el vector estará vacío * si se encuentra algún problema durante la b´squeda * @throws Exception * @throws AOException Cuando ocurre cualquier problema durante el proceso */ public ArrayList getCardModules() throws Exception { File secmod = new File(getSecModFolder(), "secmod.db"); if (!secmod.exists()) { throw new Exception("El directorio del perfil de Mozilla proporcionado no contiene una base de datos de modulos (secmod.db)"); } ArrayList lModules = new ArrayList(); HashSet moduleNames = new HashSet(); String contenidoSecmod = new String (Util.loadFile(secmod)); while(contenidoSecmod.indexOf(".dll") > -1) { int index = contenidoSecmod.indexOf(".dll") + 3; MozillaPkcs11Module module = getModule(contenidoSecmod, ".dll"); if (!moduleNames.contains(module.getName())) { lModules.add (module); moduleNames.add(module.getName()); } contenidoSecmod = contenidoSecmod.substring(0, index) + contenidoSecmod.substring (contenidoSecmod.indexOf(".dll") + 4); } while(contenidoSecmod.indexOf(".DLL") > -1) { int index = contenidoSecmod.indexOf(".DLL") + 3; MozillaPkcs11Module module = getModule(contenidoSecmod, ".DLL"); if (!moduleNames.contains(module.getName())) { lModules.add (module); moduleNames.add(module.getName()); } contenidoSecmod = contenidoSecmod.substring(0, index) + contenidoSecmod.substring (contenidoSecmod.indexOf(".DLL") + 4); } while(contenidoSecmod.indexOf(".so") > -1) { int index = contenidoSecmod.indexOf(".so") + 2; MozillaPkcs11Module module = getModule(contenidoSecmod, ".so"); if (!moduleNames.contains(module.getName())) { lModules.add (module); moduleNames.add(module.getName()); } contenidoSecmod = contenidoSecmod.substring(0, index) + contenidoSecmod.substring (contenidoSecmod.indexOf(".so") + 3); } while(contenidoSecmod.indexOf(".dylib") > -1) { int index = contenidoSecmod.indexOf(".dylib") + 5; MozillaPkcs11Module module = getModule(contenidoSecmod, ".dylib"); if (!moduleNames.contains(module.getName())) { lModules.add (module); moduleNames.add(module.getName()); } contenidoSecmod = contenidoSecmod.substring(0, index) + contenidoSecmod.substring (contenidoSecmod.indexOf(".dylib") + 5); } return lModules; } public MozillaPkcs11Module getModule(String contenidoSecmod, String extension) throws Exception { while(contenidoSecmod.indexOf(extension) > -1) { int index = contenidoSecmod.indexOf(extension) + (extension.length() - 1); String name = "", module = ""; while (contenidoSecmod.charAt(index) != 0) { module = contenidoSecmod.charAt(index) + module; index--; } if (module.length() > 0) { module = module.substring(1); } index--; while (contenidoSecmod.charAt(index) != 0) { name = contenidoSecmod.charAt(index) + name; index--; } if (name.length() > 0) { name = name.substring(1); } return new MozillaPkcs11Module(getValidName (name), new File (module)); } return null; } /* * Obtiene un nombre válido para un provider: sólo letras y números */ private String getValidName(String name) { return name.replaceAll("[^a-zA-Z1-9]", ""); } /** * Copyright (C) 2011 [Gobierno de Espana] * Este método forma parte del "Cliente @Firma". * * Busca la última versión de firefox instalada en un sistema * Linux o Solaris * @return Última versión instalada en el sistema */ private static String searchLastFirefoxVersion(final String startDir) { final File directoryLib = new File(startDir); if (directoryLib.isDirectory()) { final String filenames[] = directoryLib.list(); final List firefoxDirectories = new ArrayList(); for (final String filename : filenames) { if (filename.startsWith("firefox-")) { //$NON-NLS-1$ firefoxDirectories.add(filename.replace("firefox-", "")); //$NON-NLS-1$ //$NON-NLS-2$ } } if (firefoxDirectories.isEmpty()) { return ""; //$NON-NLS-1$ } for (int i = 0; i < firefoxDirectories.size(); i++) { try { Integer.getInteger(firefoxDirectories.get(i)); } catch (final Exception e) { firefoxDirectories.remove(i); } } if (firefoxDirectories.size() == 1) { return firefoxDirectories.get(0); } Collections.sort(firefoxDirectories, new Comparator() { /** {@inheritDoc} */ public int compare(final String o1, final String o2) { return o1.compareTo(o2); } }); return firefoxDirectories.get(0); } return ""; //$NON-NLS-1$ } /** * Copyright (C) 2011 [Gobierno de Espana] * Este método forma parte del "Cliente @Firma". */ private void configureMacNSS(File mozillaFolder) throws Throwable { // Intentamos la carga, para ver si es necesaria la reconfiguracion try { System.load(new File (mozillaFolder, "libsoftokn3.dylib").getAbsolutePath()); //$NON-NLS-1$ return; // Si funciona salimos sin hacer nada } catch (final Exception e) { // Se ignora el error } catch(final UnsatisfiedLinkError e) { // Se ignora el error } final String[] libs = new String[] { "libmozutils.dylib", // Firefox 9 y superiores //$NON-NLS-1$ "libnspr4.dylib", //$NON-NLS-1$ "libplds4.dylib", //$NON-NLS-1$ "libplc4.dylib", //$NON-NLS-1$ "libmozsqlite3.dylib", //$NON-NLS-1$ "libnssutil3.dylib" //$NON-NLS-1$ }; // Creamos enlaces simbolicos via AppleScript final StringBuilder sb = new StringBuilder(); for (final String lib : libs) { File fileLib = new File(mozillaFolder, lib); if (fileLib.exists()) { sb.append("ln -s "); //$NON-NLS-1$ sb.append(fileLib.getAbsolutePath()); sb.append(" /usr/lib/"); //$NON-NLS-1$ sb.append(lib); sb.append("; "); //$NON-NLS-1$ } } try { final Class scriptEngineManagerClass = Class.forName("javax.script.ScriptEngineManager"); //$NON-NLS-1$ final Object scriptEngineManager = scriptEngineManagerClass.newInstance(); final Method getEngineByNameMethod = scriptEngineManagerClass.getMethod("getEngineByName", String.class); //$NON-NLS-1$ final Object scriptEngine = getEngineByNameMethod.invoke(scriptEngineManager, "AppleScript"); //$NON-NLS-1$ final Class scriptEngineClass = Class.forName("javax.script.ScriptEngine"); //$NON-NLS-1$ final Method evalMethod = scriptEngineClass.getMethod("eval", String.class); //$NON-NLS-1$ evalMethod.invoke(scriptEngine, "do shell script \"" + sb.toString() + "\" with administrator privileges"); //$NON-NLS-1$ //$NON-NLS-2$ //new ScriptEngineManager().getEngineByName("AppleScript").eval("do shell script \"" + sb.toString() + "\" with administrator privileges"); } catch(final Exception e) { logger.info("No se ha podido crear los enlaces simbolicos para NSS: " + e, e); //$NON-NLS-1$ } // Y reintentamos la carga, para ver si ha surtido efecto try { System.load(new File (mozillaFolder, "libsoftokn3.dylib").getAbsolutePath()); //$NON-NLS-1$ } catch (final Exception e) { //-- A veces se carga dos veces en la misma JVM y la segunda pasa por estas excepciones //-- porque ya ha cargado la librería, así que lo mejor es seguir // throw new Exception("La configuracion de NSS para Mac OS X ha fallado por motivos de seguridad: " + e); //$NON-NLS-1$ } catch(final UnsatisfiedLinkError e) { // throw new Exception("La configuracion de NSS para Mac OS X ha fallado: " + e); //$NON-NLS-1$ } } //-- Clases /** * Clase que almacena la configuración para la identificacion de un perfil * de Mozilla Firefox. */ private static class FirefoxProfile { String name = null; boolean isRelative = true; String path = null; String absolutePath = null; boolean isDefault = false; } /** * Clase que representa la información de un módulo pkcs#11 definido en Mozilla */ public class MozillaPkcs11Module { private String name; private File library; public MozillaPkcs11Module(String name, File library) { super(); this.name = name; this.library = library; } public String getName() { return name; } public void setName(String name) { this.name = name; } public File getLibrary() { return library; } public void setLibrary(File library) { this.library = library; } } }