es.gob.afirma.standalone.configurator.ConfiguratorWindows Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of afirma-ui-simpleafirma-configurator Show documentation
Show all versions of afirma-ui-simpleafirma-configurator Show documentation
Aplicacion auxiliar de instalacion de AutoFirma
/* Copyright (C) 2011 [Gobierno de Espana]
* This file is part of "Cliente @Firma".
* "Cliente @Firma" is free software; you can redistribute it and/or modify it under the terms of:
* - the GNU General Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any later version.
* - or The European Software License; either version 1.1 or (at your option) any later version.
* You may contact the copyright holder at: [email protected]
*/
package es.gob.afirma.standalone.configurator;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import es.gob.afirma.core.misc.AOUtil;
import es.gob.afirma.standalone.configurator.CertUtil.CertPack;
/** Configura la instalación en Windows para la correcta ejecución de AutoFirma. */
final class ConfiguratorWindows implements Configurator {
static final Logger LOGGER = Logger.getLogger("es.gob.afirma"); //$NON-NLS-1$
private static final String KS_FILENAME = "autofirma.pfx"; //$NON-NLS-1$
private static final String KS_PASSWORD = "654321"; //$NON-NLS-1$
private static final String FILE_AUTOFIRMA_ROOT_CERTIFICATE = "AutoFirma_ROOT.cer"; //$NON-NLS-1$
/** Nombre del usuario por defecto en Windows. Este usuario es el que se usa como base para
* crear nuevos usuarios y no se debería tocar. */
private static String DEFAULT_WINDOWS_USER_NAME = "Default"; //$NON-NLS-1$
// A partir de la version 57 de Chrome cambia el fichero en el que se guardan los protocol handler
private static final String CHROME_V56_OR_LOWER_CONFIG_FILE = "AppData/Local/Google/Chrome/User Data/Local State"; //$NON-NLS-1$
private static final String CHROME_V57_OR_HIGHER_CONFIG_FILE = "AppData/Local/Google/Chrome/User Data/Default/Preferences"; //$NON-NLS-1$
private final boolean jnlpInstance;
public ConfiguratorWindows(final boolean jnlpInstance) {
this.jnlpInstance = jnlpInstance;
}
@Override
public void configure(final Console window) throws IOException, GeneralSecurityException {
window.print(Messages.getString("ConfiguratorWindows.2")); //$NON-NLS-1$
final File appDir = getApplicationDirectory(this.jnlpInstance);
window.print(Messages.getString("ConfiguratorWindows.3") + appDir.getAbsolutePath()); //$NON-NLS-1$
if (!checkSSLKeyStoreGenerated(appDir, this.jnlpInstance)) {
window.print(Messages.getString("ConfiguratorWindows.5")); //$NON-NLS-1$
final CertPack certPack = CertUtil.getCertPackForLocalhostSsl(
ConfiguratorUtil.CERT_ALIAS,
KS_PASSWORD
);
window.print(Messages.getString("ConfiguratorWindows.11")); //$NON-NLS-1$
//Generacion del certificado pfx
ConfiguratorUtil.installFile(
certPack.getPkcs12(),
new File(appDir, KS_FILENAME)
);
//Generacion del certificado raiz .cer
ConfiguratorUtil.installFile(
certPack.getCaCertificate().getEncoded(),
new File(appDir, FILE_AUTOFIRMA_ROOT_CERTIFICATE));
window.print(Messages.getString("ConfiguratorWindows.9")); //$NON-NLS-1$
try {
ConfiguratorFirefoxWindows.installCACertOnMozillaKeyStores(appDir, window);
}
catch(final MozillaProfileNotFoundException e) {
window.print(Messages.getString("ConfiguratorWindows.12") + ": " + e); //$NON-NLS-1$ //$NON-NLS-2$
}
if (this.jnlpInstance) {
JOptionPane.showMessageDialog(window.getParentComponent(), Messages.getString("ConfiguratorWindows.17")); //$NON-NLS-1$
window.print(Messages.getString("ConfiguratorWindows.6")); //$NON-NLS-1$
importCARootOnWindowsKeyStore(certPack.getCaCertificate(), CertUtil.ROOT_CERTIFICATE_PRINCIPAL);
}
}
else {
window.print(Messages.getString("ConfiguratorWindows.14")); //$NON-NLS-1$
}
// Si no se ha cargado mediante JNLP, registramos el protocolo para Google Chrome
if (!this.jnlpInstance) {
configureChrome(window, true);
}
window.print(Messages.getString("ConfiguratorWindows.8")); //$NON-NLS-1$
}
/**
* Recupera el directorio de la aplicación, que podrá variar
* según si está instalada o si se trata de un despliegue JNLP.
* @return Directorio en el que se almacenan los recursos de la aplicación.
*/
private static File getApplicationDirectory(final boolean jnlpDeployment) {
// Si el despliegue es JNLP seleccionamos un directorio de Windows en el que
// se puedan crear los ficheros sin permisos especiales
if (jnlpDeployment) {
final String commonDir = System.getenv("ALLUSERSPROFILE"); //$NON-NLS-1$
final File appDir = new File (commonDir, "AutoFirma"); //$NON-NLS-1$
if (appDir.isDirectory() || appDir.mkdirs()) {
return appDir;
}
return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
}
return ConfiguratorUtil.getApplicationDirectory();
}
/** Comprueba si ya existe un almacén de certificados generado.
* En caso del despliegue JNLP, primero consulta en el directorio por
* defecto de instalación de AutoFirma en el sistema, y después
* el directorio indicado.
* @param appDir Directorio de la aplicación.
* @return {@code true} si ya existe un almacen de certificados SSL, {@code false} en caso contrario. */
private static boolean checkSSLKeyStoreGenerated(final File appDir, final boolean jnlpDeployment) {
/*
// En caso de tratarse de un despliegue JNLP, probamos primeramente
// a buscar el almacen en el directorio de instalacion por defecto
// de AutoFirma para evitar tener que volver a generarlo
if (jnlpDeployment) {
final File[] defaultDirs = getDefaultInstallationDirs();
for (final File defaultDir : defaultDirs) {
if (new File(defaultDir, KS_FILENAME).exists()) {
return true;
}
}
}
*/
return new File(appDir, KS_FILENAME).exists();
}
/**
* Devuelve el listado de directorios en el que comúnmente se instala
* AutoFirma en este sistema operativo.
* @return Listado de directorios.
*/
private static File[] getDefaultInstallationDirs() {
final List dirs = new ArrayList<>();
final String subPath = "AutoFirma" + File.separator + "AutoFirma"; //$NON-NLS-1$ //$NON-NLS-2$
final String basePath = System.getenv("PROGRAMFILES"); //$NON-NLS-1$
if (basePath != null) {
dirs.add(new File(basePath, subPath));
if (basePath.endsWith(" (x86)")) { //$NON-NLS-1$
dirs.add(new File(basePath.substring(0, basePath.lastIndexOf(" (x86)")), subPath)); //$NON-NLS-1$
}
else {
dirs.add(new File(basePath + " (x86)", subPath)); //$NON-NLS-1$
}
}
return dirs.toArray(new File[dirs.size()]);
}
@Override
public void uninstall() {
LOGGER.info("Desinstalamos el certificado raiz del almacen de Windows"); //$NON-NLS-1$
uninstallRootCAWindowsKeyStore(CertUtil.ROOT_CERTIFICATE_PRINCIPAL);
LOGGER.info("Desinstalamos el certificado raiz del almacen de Firefox"); //$NON-NLS-1$
ConfiguratorFirefoxWindows.uninstallRootCAMozillaKeyStore(
getApplicationDirectory(this.jnlpInstance));
// Insertamos el protocolo afirma en el fichero de configuracion de Google Chrome
configureChrome(null, false);
// No es necesario eliminar nada mas porque el proceso de desinstalacion de Windows
// eliminara el directorio de aplicacion con todo su contenido
}
/**
* Configura el protocolo "afirma" en Chrome para todos los usuarios de Windows.
* @param window Consola de salida.
* @param installing Indica si se debe configurar ({@code true}) o desconfigurar
* ({@code false}) el protocolo "afirma" en Chrome.
*/
private static void configureChrome(final Console window, final boolean installing) {
if (window != null && installing) {
window.print(Messages.getString("ConfiguratorWindows.16")); //$NON-NLS-1$
}
final File usersDir = new File(System.getProperty("user.home")).getParentFile(); //$NON-NLS-1$
for (final File userDir : usersDir.listFiles()) {
if (userDir.isDirectory() && !DEFAULT_WINDOWS_USER_NAME.equalsIgnoreCase(userDir.getName())) {
try {
final File chromeConfigFileV56OrLower = new File(userDir, CHROME_V56_OR_LOWER_CONFIG_FILE);
if (chromeConfigFileV56OrLower.isFile() && chromeConfigFileV56OrLower.canWrite()) {
String config;
try (final FileInputStream fis = new FileInputStream(chromeConfigFileV56OrLower)) {
config = new String(AOUtil.getDataFromInputStream(fis), StandardCharsets.UTF_8);
}
config = config.replace("\"afirma\":false,", ""); //$NON-NLS-1$ //$NON-NLS-2$
config = config.replace("\"afirma\":false", ""); //$NON-NLS-1$ //$NON-NLS-2$
if (installing) {
config = config.replace("\"protocol_handler\":{\"excluded_schemes\":{", //$NON-NLS-1$
"\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false,"); //$NON-NLS-1$
config = config.replace("\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false,}", //$NON-NLS-1$
"\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false}"); //$NON-NLS-1$
// En caso de que Google Chrome este recien instalado no encontrara cadena a reemplazar,
// por lo que directamente insertaremos la cadena nosotros en el lugar correspondiente
if(!config.contains("excluded_schemes")) { //$NON-NLS-1$
config = config.replaceAll("last_active_profiles([^,]*),", //$NON-NLS-1$
"last_active_profiles$1,\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false}},"); //$NON-NLS-1$
}
}
else {
// Elimina la sintaxis que define los protocolos de confianza si no existe ninguno.
config = config.replace("\"protocol_handler\":{\"excluded_schemes\":{}},", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
try (final FileOutputStream fos = new FileOutputStream(chromeConfigFileV56OrLower)) {
fos.write(config.getBytes(StandardCharsets.UTF_8));
}
}
final File chromeConfigFileV57OrHigher = new File(userDir, CHROME_V57_OR_HIGHER_CONFIG_FILE);
if (chromeConfigFileV57OrHigher.isFile() && chromeConfigFileV57OrHigher.canWrite()) {
String config;
try (final FileInputStream fis = new FileInputStream(chromeConfigFileV57OrHigher)) {
config = new String(AOUtil.getDataFromInputStream(fis), StandardCharsets.UTF_8);
}
config = config.replace("\"afirma\":false,", ""); //$NON-NLS-1$ //$NON-NLS-2$
config = config.replace("\"afirma\":false", ""); //$NON-NLS-1$ //$NON-NLS-2$
if (installing) {
config = config.replace("\"protocol_handler\":{\"excluded_schemes\":{", //$NON-NLS-1$
"\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false,"); //$NON-NLS-1$
config = config.replace("\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false,}", //$NON-NLS-1$
"\"protocol_handler\":{\"excluded_schemes\":{\"afirma\":false}"); //$NON-NLS-1$
// En caso de que Google Chrome este recien instalado no encontrara cadena a reemplazar,
// pero si se ha insertado en el fichero Local State del paso anterior se configurara automaticamente
// en el Default/Preferences cuando se invoque el protocolo afirma
}
try (final FileOutputStream fos = new FileOutputStream(chromeConfigFileV57OrHigher)) {
fos.write(config.getBytes(StandardCharsets.UTF_8));
}
}
}
catch (final Exception e) {
if (window != null) {
window.print(String.format(Messages.getString("ConfiguratorWindows.15"), userDir.getName())); //$NON-NLS-1$
}
LOGGER.warning("No se pudo configurar Chrome para el usuario " + userDir + ": " + e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
}
/**
* Instala el certificado SSL en el almacén de autoridades de confianza Windows
* sin necesidad de tener permisos de administrador.
* @param cert Certificado a instalar.
* @param principal Principal del certificado.
* @throws GeneralSecurityException Cuando no se tiene acceso al almacén de
* autoridades de certificación.
* @throws IOException Cuando no se pudo cargar el almacén.
*/
private static void importCARootOnWindowsKeyStore(final Certificate cert, final String principal) throws GeneralSecurityException, IOException {
final KeyStore ks = KeyStore.getInstance("Windows-ROOT"); //$NON-NLS-1$
ks.load(null, null);
boolean installed = false;
boolean cancelled = false;
do {
try {
ks.setCertificateEntry(principal, cert);
installed = true;
}
catch (final KeyStoreException e) {
LOGGER.warning(
"No se pudo instalar la CA del certificado SSL para el socket en el almacen de Windows: " + e //$NON-NLS-1$
);
final int result = JOptionPane.showConfirmDialog(
null,
Messages.getString("ConfiguratorWindows.0"), //$NON-NLS-1$
Messages.getString("ConfiguratorWindows.1"), //$NON-NLS-1$
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE
);
if (result == JOptionPane.CANCEL_OPTION) {
cancelled = true;
LOGGER.severe("El usuario cancelo la instalacion del certificado SSL para el socket: " + e); //$NON-NLS-1$
}
}
}
while (!installed && !cancelled);
}
/**
* Desinstala un certificado del almacen de confianza de Windows sin
* necesidad de tener permisos de administrador.
* @param principal Principal del certificado a eliminar.
*/
private static void uninstallRootCAWindowsKeyStore(final String principal) {
try {
final KeyStore ks = KeyStore.getInstance("Windows-ROOT"); //$NON-NLS-1$
ks.load(null, null);
ks.deleteEntry(principal);
}
catch (final Exception e) {
LOGGER.warning("No se pudo desinstalar el certificado SSL raiz del almacen de Windows: " + e); //$NON-NLS-1$
}
}
}