Buscar este blog

sábado, 28 de julio de 2018

Autofirma - Gestión de actualizaciones

Un tema bastante molesto relacionado con Autofirma es el control de las actualizaciones. Al ser un componente instalado en el equipo del usuario es él quien, en última instancia, decide qué versión instalar y cuándo actualizarla.

Cuando se desarrolla una aplicación web generalmente se prueba contra una versión concreta de @Firma, Autofirma incluida, y se certifica para su uso con ella. La parte de servidor no presenta ningún problema, puesto que se tiene control total sobre ella, no así la parte cliente. En cualquier momento puede publicarse una nueva versión de Autofirma y, gracias a la función automática de comprobación de actualizaciones, el usuario puede actualizarla. En este caso podrían presentarse problemas si esta nueva versión no es totalmente compatible con la anterior.

En este post se explicará cómo hace Autofirma la gestión y control de las actualizaciones y de qué forma se podría intervenir para prevenir los avisos al usuario.


Cada vez que se inicia Autofirma, ésta revisa si está configurada para comprobar de forma automática si existe una nueva actualización. En caso afirmativo, consulta en una URL pública cuál es la última versión disponible y la compara con su versión interna. Si la primera es mayor a la segunda, entonces redirige al usuario a una segunda URL para que proceda a su descarga manual.


Concretamente, al URL de descarga a día de hoy es http://firmaelectronica.gob.es/Home/Descargas.html



Existe una opción de configuración del componente que permite deshabilitar la consulta automática de actualizaciones, pero debe establecerlo explícitamente así el usuario, puesto que por defecto ya viene marcada.




A nivel técnico, el proceso descrito se controla de dos modos:
  1. Gestión de preferencias de usuario
  2. Consulta de actualizaciones
La gestión de preferencias de usuario se hace desde la  clase es.gob.afirma.standalone.ui.preferences.PreferencesManager. Esta clase gestiona dos objetos de propiedades, las preferencias guardadas y las preferencias por defecto.
static {
 preferences = Preferences.userNodeForPackage(PreferencesManager.class);

 properties = new Properties();
 try {

  properties.load(PreferencesManager.class.getResourceAsStream("/properties/preferences.properties" //$NON-NLS-1$
  ));
 } catch (final Exception e) {
  LOGGER.severe(
    "No han podido cargarse los valores por defecto del fichero de configuracion de preferencias, se usaran los valores por defecto: " //$NON-NLS-1$
      + e);
 }

}

Las preferencias se guardan empleando la clase java.util.java.util.prefs.Preferences, que en el caso de windows, persiste bajo la clave de registro HKEY_CURRENT_USER\Software\JavaSoft\Prefs\es\gob\afirma\standalone\ui\preferences.
Las preferencias por defecto se guardan dentro del propio jar,en el fichero preferences.properties.
(...)
#Panel General de Preferencias
signatureAlgorithm = SHA256withRSA
omitAskOnClose = false
hideDnieStartScreen = false
checkForUpdates = true
useAnalytics = true
enabledJmulticard = true
defaultSignatureFormatPdf = PAdES
defaultSignatureFormatOoxml = OOXML (Office Open XML)
defaultSignatureFormatFacturae = FacturaE
defaultSignatureFormatOdf = ODF (Open Document Format)
defaultSignatureFormatXml = XAdES
defaultSignatureFormatBin = CAdES
(...)

La consulta de actualizaciones se hace desde la clase es.gob.afirma.standalone.updater.
private static void loadProperties() {
 if (updaterProperties != null) {
  return;
 }
 updaterProperties = new Properties();
 try {
  updaterProperties.load(Updater.class.getResourceAsStream("/properties/updater.properties")); //$NON-NLS-1$
 }
 catch (final Exception e) {
  LOGGER.severe(
   "No se ha podido cargar el archivo de recursos del actualizador: " + e //$NON-NLS-1$
  );
  updaterProperties = new Properties();
 }
}

Las propiedades de consulta de versión se mantienen en un fichero independiente llamado updater.properties:
# Numero de version visible
currentVersionText.WINDOWS=1.6.4
currentVersionText.LINUX=1.6.4
currentVersionText.MACOSX=1.6.4

# Numero de version interno (numero entero). Si es par se trata de una version preliminar.
# Este numero no avanzara dentro de las distintas versiones preliminares anteriores a una
# version final. Cuando se establezca un numero impar, se identifica una version final.
currentVersion.WINDOWS=10
currentVersion.LINUX=10
currentVersion.MACOSX=10

# AutoFirma en Windows buscara si hay nuevas versiones y lo notificara al
# usuario remitiendole a la pagina de descargas
url=http://estaticos.redsara.es/comunes/autofirma/autofirma.version
updateSite=http://firmaelectronica.gob.es/Home/Descargas.html

Tal y como se aprecia la URL de consulta de versión es http://estaticos.redsara.es/comunes/autofirma/autofirma.version, que actualmente devuelve el valor 8.


Por tanto, de cara a tener un mayor control sobre el proceso de actualización se podrían hacer las siguientes modificaciones sobre estos ficheros:
  1. En el fichero preferences.properties configurar que no se marque por defecto la opción de comprobación de actualizaciones. Bastaría con cambiar la propiedad checkForUpdates.
  2. En el fichero updater.properties configurar la URL de comprobación de versión a una dirección controlada, y que se devuelva la versión compatible con la aplicación en la que se use. Bastaría con cambiar la propiedad url.


En cualquier caso, una vez hecho el cambio habría que recompilar el código y generar de nuevo el instalador: Autofirma - Compilación manual para Windows

Autofirma - Compilación manual para Windows

En este post resumo los pasos básicos para compilar y construir el instalador de Autofirma a partir del código del Github del Ministerio. Autofirma forma parte de la suite del cliente de firma elctróncia de @firma, siendo el componente encargado de hacer las firmas desde el equipo de usuario.

En esta prueba simplemente generaré la versión de 64 bits del instalador para Windows.

1 - Configuración de Maven

La versión de maven que usaré es la 3.5.0 con java 1.8 de 64 bits:
>mvn --version
Apache Maven 3.5.0 (ff8f5e7444045639af65f6095c62210b5713f426; 2017-04-03T21:39:06+02:00)
Maven home: C:\Program Files (x86)\Apache Software Foundation\apache-maven-3.5.0\bin\..
Java version: 1.8.0_111, vendor: Oracle Corporation
Java home: c:\Program Files\Java\jdk1.8.0_111\jre
Default locale: es_ES, platform encoding: Cp1252
OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"

No creo que haya problemas por utilizar otra versión cercana.

2 - Descarga de Launch4J

Cuando se tenga el jar de Autofirma se deberá empaquetar (más bien envolver) en un archivo ejecutable de windows. Para ello se necesitará usar la herramienta Launch4J.
La versión con la que lo probé es la 3.12, en su modalidad portable: https://sourceforge.net/projects/launch4j/files/launch4j-3/3.12/.
Basta con descargarse el zip y descomprimirlo.

3 - Descarga y configuración de NSIS

Con el exe creado por Launch4J ya se podría ejecutar la aplicación (a falta de un JRE), pero al usuario se le entrega un instalador (wizard) que se encarga de copiar el ejecutable a donde corresponda, crear entradas de registro, registrar la aplicación, etc. Para ello se usa NSIS (Nullsoft Scriptable install System).

La versión con la que probé es la 2.5.1, también en modalidad portable: https://sourceforge.net/projects/nsis/files/NSIS%202/2.51/.
Basta con descargarse el zip y descomprimirlo.

Una vez extraído hay que instalar el plugin de NsProcess, versión 1.6.7: http://nsis.sourceforge.net/NsProcess_plugin. Es un zip que contiene varias carpetas:
  • Example
  • Include
  • Plugin
  • Source

Hay que copiar el contenido de Include y Plugin a los subdirectorios correspondientes dentro del directorio de instalación de NSIS.

4 - Compilación y empaquetado de jmulticard

Uno de los módulos de @Firma tiene una dependencia con un artefacto del proyecto jmulticard. En el caso concreto de la versión 1.6.2 de @Firma la dependencia es con la versión 1.5-SNAPSHOT de jmulticard (sí amigos, tenemos una release que dependen de un snapshot, un clavo más en el ataúd del clean code).

En este caso me descargué directamente y compilé directamente el master: https://github.com/ctt-gob-es/jmulticard.

5 - Compilación y empaquetado de @Firma

Por fin entramos en materia con @Firma. En esta prueba me descargué el tag 1.6.2 del repo principal: https://github.com/ctt-gob-es/clienteafirma.

Justo en la versión 1.6.2 hay un bug (o más bien una mala praxis en el desarrollo) y es necesario hacer una pequeña corrección en un fichero pom. Concretamente, en el fichero \afirma-standalone\afirma-ui-standalone\pom.xml se deberá modificar la propiedad afirma.core.version, que vale 1.7-SNAPSHOT a la 1.6 (se ve que el tema de la gestión de versiones todavía no lo tienen muy dominado).
<properties>
 <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
 <afirma.keytool.keystore>${basedir}/afirma.keystore</afirma.keytool.keystore>
 <afirma.keytool.alias>codesign</afirma.keytool.alias>
 <afirma.keytool.password>afirma</afirma.keytool.password>
 <afirma.core.version>1.7-SNAPSHOT</afirma.core.version>
</properties>

Una vez hecho esto, y según lo que pone en el propio README del proyecto, la compilación se hará en dos pasos:
- mvn clean install, para generar los módulso básicos y los servicios.
- mvn clean install -Denv=install, para generar los artefactos desplegables y aplicaciones

Completada la instalación, los artefactos más relevantes para generar posteriormente el instalador de Autofirma serán los siguientes:
  • \afirma-simple\target\AutoFirma.jar
  • \afirma-ui-simple-configurator\target\AutoFirmaConfigurador.jar

6 - Generación del instalador de Autfirma

Para montar el instalador se tienen que ejecutar los siguientes pasos:
  1. Generación del ejectuable de Autofirma
  2. Generación del ejecutable del Configurador
  3. Generación del ejecutable del Command Line
  4. Generación del instalador de Autofirma
En este caso sólo usaré los archivos de windows de 64 bits.

6.1 Generación del ejecutable de Autofirma

Se copiará el archivo Autofirma.jar, creado anteriormente, al directorio \afirma-simple-installer\jar\.

Luego se cargará el fichero \afirma-simple-installer\AutoFirma_launch4J_project_64.xml en Launch4J y se lanzará su ejecución.


El resultado será el fichero \afirma-simple-installer\AutoFirma64\AutoFirma.exe

6.2 Generación del ejecutable del Configurador

Se copiará el archivo AutoFirmaConfigurador.jar, creado anteriormente, al directorio \afirma-simple-installer\jar\.

Luego se cargará el fichero \afirma-simple-installer\AutoFirma_launch4J_Configurador_project_64.xml en Launch4J y se lanzará su ejecución.


El resultado será el fichero \afirma-simple-installer\AutoFirma64\AutoFirmaConfigurador.exe

6.3 Generación del ejecutable del Command Line

También a partir del AutoFirma.jar, se cargará el fichero \afirma-simple-installer\AutoFirmaCommandLine_launch4J_project_64.xml en Launch4J y se lanzará su ejecución.


El resultado será el fichero \afirma-simple-installer\AutoFirma64\AutoFirmaCommandLine.exe

6.4 Generación del instalador de Autofirma

Lo primero será copiar un JRE 1.8 en el directorio \afirma-simple-installer\java64. El instalador incorporará el JRE internamente, de modo que dentro del directorio final de instalación del usuario también se incluirá éste, que será el que utilice en última instancia Autofirma durante su ejecución.

Se lanzará la herramienta makensisw.exe que se incluye dentro del directorio de instalación de NSIS. Sobre ella se cargará el fichero \afirma-simple-installer\AutoFirma_NSIS_project_EXE_64.nsi y se lanzará la creación del instalador.



El resultado final será el fichero \afirma-simple-installer\AutoFirma64\AutoFirma_64_v1_6_2_installer.exe. 

Por fin hemos conseguido el instalador!!

domingo, 8 de julio de 2018

JAXB - XMLGregorianCalendar - Blank date value

When you work with web services or other schema-based objects, date fields are interpreted as XMLGregorianCalendar.

For example if you have this schema:
<xs:simpleType name="timeStamp">
 <xs:annotation>
  <xs:documentation>timeStamp</xs:documentation>
 </xs:annotation>
 <xs:restriction base="dateTime" />
</xs:simpleType>

You will end with this POJO:
   @XmlSchemaType(name = "dateTime")
   protected XMLGregorianCalendar timeStamp;

The problem here is that when you want to transform this POJO in XML (marshalling), the timestamp value will always be empty.

I found this problem working with Camel and CXF. Clients connect to the Camel route throught a SOAP web service by using only simple data types (a string wich contains an XML), while inside the route this XML is transformed into a java object and sent to a second web service. The response run throught the inverse process.

Something like this:
   final JaxbDataFormat jaxb = new JaxbDataFormat("xxx.xxxx.xxxx.xxx");
   jaxb.setEncoding("UTF-8");      

   from("cxf:bean:simpleServiceProvider") 
   .unmarshal(jaxb) 
   .to("cxf:bean:complexServiceClient")
   .setBody().simple("${body[0]}")
   .marshal(jaxb)
   .convertBodyTo(String.class, "UTF-8");

In this case the solution is to use java.util.Date instead XMLGregorianCalendar, so marshal step works fine.
As I posted before in Eclipse - Maven - Configure wsdl2java and wsdl2java - Use java.util.Date instead of XMLGregorianCalendar, you build your POJOs directly from the WSDL file. Besides, you can force wsdl2java to use Date instead XMLGregorianCalendar, so you should not have any problems.

Actually, in my new task, the WSDL file was a bit more complex, and schema files were external. In this case you have to configure the binding at schema-level instead WSDL-level:
<jaxb:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0" 
 schemaLocation="dct.xsd"
 xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
 jaxb:extensionBindingPrefixes="xjc">
 
 <jaxb:globalBindings>
  <jaxb:serializable uid="1" />
  
   <jaxb:javaType name="java.util.Date" xmlType="xsd:dateTime"
    parseMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDateTime"
    printMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.printDateTime" />
        
 </jaxb:globalBindings>
</jaxb:bindings>

The schema file it is called dtc.xsd, with is referenced from schemalocation attribute.