Páginas

miércoles, 30 de mayo de 2012

EJB 3.0 vs JSF2+Spring+Hibernate

Vamos a comentar brevemente lo que se define en la especificación EJB 3.0, para luego compararlo con las funcionlidades equivalentes ofrecidas por la terna JSF2+Spring+Hibernate que aplicamos en nuestro equipo de desarrollo:

EJB 3.0.

Es una de los apartados de la especificación J2EE. La especificación define los objetos de negocio (los EJB) y el comportamiento que deben tener los servidores de aplicaciones que quieran cumplir la especificación en su área de gestión de estos EJB, a este área de gestión se le llama comunmente Contenedor.

Entity Bean: Son los objetos del lado del servidor que almacena los datos. Hay dos tipos:
  • Container Managed Bean. El contenedor se encarga de almacenar y recuperar los datos del objeto de entidad mediante el mapeo de las columnas de una tabla con los atributos de un objeto.
  • Bean Managed Persistence. El propio objeto entidad se encarga, mediante una base de datos u otro mecanismo, de almacenar y recuperar los datos a los que se refiere, esto es los mecanismos de persistencia los ha generado el programador.
Session Bean: EJB de Sesión (Session EJBs): gestionan el flujo de la información entre el servidor y el usuario. Puede haber dos tipos:
  • Statefull. Cada bean de sesión con estado, almacena los datos que persisten de una pantalla a otra como un carrito de la compra. Ofrece la funcionalidad común a N pantallas. Cuando el cliente termina el bean se borra.
  • Stateless. Cada bean de sesión sin estado, almacena los datos asociados a una pantalla y la lógica de negocio asociada a una pantalla. La pantalla X visualizada por cada uno de los 1000 usuarios conectados simultáneamente a la página del detalle de un banco tienen su propio bean. Los contenidos de las variables no se conservan entre llamada y llamada.

Message-Drive Bean: Son beans para tareas asíncrono. Usan el API JMS (Java Messaging System). Los hay de varios tipos:

  • Queue. N-Productores -> 1-Consumidor. Hay N generadores de mensajes que se apuntan a una cola donde dejan sus peticiones y un solo consumidor las va atendiendo por orden de llegada.
  • Topic. N-Productores -> N-Consumidores. Hay N generadores de mensajes que se envían al topic, desde allí se envía una copia para cada uno de los consumidores, todos ellos reciben cada mensaje.
Equivalencias con nuestra infraestructura (JSF+Spring+Hibernate):

Container Managed Bean. Clases mapeadas con JPA + hibernate. Usamos hibernate como motor de HQL e implementador de JPA. Utilizamos este tipo de objetos en la capa de acceso a datos cuando la BBDD es relacional.
Bean Managed Persistence. Spring JDBC Template. Utilizamos este tipo de objetos en la capa de acceso a datos cuando la BBDD no es relacional.
Statefull. Los SessionBean de JSF (JSF ofrece además los ApplicationBean, que viven durante el tiempo que el portal está levantado)
Stateless. Los RequestBean de JSF (JSF ofrece también algo mas, los ViewBean que dan respaldo a N peticiones siempre que se navegue en la misma pantalla)
Queue. No usamos JMS en ningún proyecto, usamos el planificador Quartz que viene con Spring para los tratamientos asíncronos donde cierto "consumidor" debe tratar las peticiones que se han almacenado en una cola, en nuestro caso esta cola es la BBDD. Si quisiésemos trabajar con JMS usaríamos el JmsTemplate de Spring.
Topic. Esta funcionalidad no se nos ha planteado en ningún proyecto, pero si se plantease usaríamos el JmsTemplate de Spring.

domingo, 27 de mayo de 2012

Manejo del DNIe con javax.smartcardio en Ubuntu

Una pequeña aplicación de ejemplo de acceso al DNIe para firmar y verificar la firma electrónica con el API javax.smartcardio de Oracle.

Son dos clases:
  • AppTest. Clase con el main que desencadena el ejemplo
  • UtilsDnie. Clase con los métodos para detectar el lector, pedir el dni, firmar y verificar la firma
Antes de comenzar aclarar que en el código se incluyen a "fuego" las ubicaciones de un par de librería, por lo que conviene ajustar estos path antes de probar.
  • Librería /usr/lib/i386-linux-gnu/libpcsclite.so de manejo de lectores de tarjetas.
  • Librería /usr/lib/opensc-pkcs11.so de manejo de las funcionalidades criptográficas del DNIe.
Clase AppTest
 
package ord.edu.app;

public class AppTest {

    private static final String TEXTO_A_FIRMAR  = "texto de ejemplo a firmar";
    private static final String PIN_ACCESO_DNIE = "xxxxxxxxxx";

    public static void main(String[] args) throws Exception {

        final UtilsDNIe ud = new UtilsDNIe();

        //Solicitamos el DNIe
        if(ud.solicitaDNI()) {

            //Si se ha insertado el DNIe firmamos
            final byte[] firma = ud.firmaDatos(PIN_ACCESO_DNIE,
                    TEXTO_A_FIRMAR.getBytes());

            //Verificamos la firma
            final boolean resultadoFirma = ud.verificaFirmaDatos(PIN_ACCESO_DNIE,
                    TEXTO_A_FIRMAR.getBytes(),
                    firma);
            System.out.println(resultadoFirma);
        }
    }
}


Clase UtilsDnie
 
package ord.edu.app;

import java.io.ByteArrayInputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.List;

import javax.smartcardio.Card;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.TerminalFactory;

import sun.security.pkcs11.SunPKCS11;

public class UtilsDNIe {

    private static final String CERT_AUTEN = "CertAutenticacion";
    private static final String CERT_FIRMA = "CertFirmaDigital";
    private static final long TIMEOUT_INSERCION_DNIE = 5000;

    /**
     * Solicita al usuario la inclusión del DNIe en cualquiera de los lectores
     * de tarjetas PC/SC detectados.
     *
     * PC/SC (Personal Computer/Smart Card) es un conjunto de especificaciones para la
     * integración de tarjetas inteligentes en ordenadores personales. En particular se
     * define un API de programación que permite a los desarrolladores trabajar de forma
     * uniforme con lectores de tarjetas de distintos fabricantes (que cumplan con la
     * especificación).
     *
     * Como tenemos instalada la libería libpcsclite.so, accedemos al DNIe mediante el
     * proveedor SunPCSC, ajustando la propiedad "sun.security.smartcardio.library" del
     * sistema tal y como se documenta en la URL:
     * http://docs.oracle.com/javase/6/docs/technotes/guides/security/
     * SunProviders.html#SunPCSCProvider
     *
     * @return Card conectado al DNIe
     * @throws CardException Se se ha detectado algú problema con la tarjeta
     */
    public boolean solicitaDNI() throws CardException  {

        System.setProperty("sun.security.smartcardio.library",
                "/usr/lib/i386-linux-gnu/libpcsclite.so");

        //Recorremos la lista de lectores detectado pidiendo el DNIe
        Card card = null;
        CardTerminal terminal;
        final TerminalFactory factory = TerminalFactory.getDefault();
        final List<CardTerminal> terminals = factory.terminals().list();

        for (int i = 0; i < terminals.size(); i++) {

            terminal = terminals.get(i);
            //Por darle un punto interactivo indicamos al usuario
            //que inserte el DNIe y esperamos un poco
            System.out.println("Inserte su DNIe en: "+terminal.getName());
            terminal.waitForCardPresent(TIMEOUT_INSERCION_DNIE);

            if(terminal.isCardPresent()) {
                System.out.println("Detectada tarjeta, conectando");
                //Conectamos usando cualquier protocolo "*"
                card = terminal.connect("*");
                break;
            } else {
                System.out.println("No se ha detectado el DNIe en: "+terminal.getName());
            }           
        }

        //Se ha detectado el DNIe, devolvemos true y desconectamos reseteando la conexión
        if (card!=null){
            card.disconnect(true);
            return true;
        }

        //No se ha detectado el DNIe, devolvemos false
        return false;
    }

    /**
     * Firmamos los datos recibidos en el DNIe
     *
     * @param pinAcceso Número PIN de acceso al DNIe
     * @param datos Datos a firmar
     * @return Datos firmados
     * @throws Exception Problemas durante el proceso de firmado
     */
    public byte[] firmaDatos(String pinAcceso, byte[] datos) throws Exception {

        //Ajustamos el proveedor PKCS11
        final SunPKCS11 sunpkcs11 = ajustaPKCS11ProviderUbuntu();

        //Accedemos al almacen de certificados del DNIe con el pin.
        final KeyStore keyStore = KeyStore.getInstance("PKCS11", sunpkcs11);
        char[] pin = pinAcceso.toCharArray();
        keyStore.load(null, pin);

        //Recogemos la lista de alias de certificados del dispositivo
        final Enumeration<String> enumeration = keyStore.aliases();

        //Buscamos el alias del certificado de Firma, en el DNIe hay dos, uno para
        //firmar (CertFirmaDigital) y otro para autenticarse (CertAutenticacion)
        String alias = null;
        while(enumeration.hasMoreElements()) {

            alias = enumeration.nextElement().toString();
            if(CERT_FIRMA.equals(alias)) {
                break;
            }
        }

        //Se se ha encontrado el certificado de firma seguimos
        if(CERT_FIRMA.equals(alias)) {

            //Recogemos la clave privada del certificado de firma
            final Key key = keyStore.getKey(CERT_FIRMA, pin);

            //Firmamos (en el dnie) los datos recibidos
            final Signature firmador = Signature.getInstance("SHA1withRSA");
            firmador.initSign((PrivateKey)key);
            firmador.update(datos);
            final byte[] firma = firmador.sign();

            //devolvemos la firma
            return firma;

        } else {
            throw new Exception("No se encontro el certificado CertFirmaDigital de firma");
        }
    }

    /**
     * Validamos la firma recibida en el DNIe
     *
     * @param pinAcceso Número PIN de acceso al DNIe
     * @param datos Datos que se han firmado
     * @param firma Firma a verificar
     * @return Resultado de la validación de la firma
     * @throws Exception Problemas durante el proceso de validación
     */
    public boolean verificaFirmaDatos(String pinAcceso, byte[] datos, byte[] firma)
        throws Exception {

        //Ajustamos el proveedor PKCS11
        final SunPKCS11 sunpkcs11 = ajustaPKCS11ProviderUbuntu();

        //Accedemos al almacen de certificados del DNIe con el pin.
        final KeyStore keyStore = KeyStore.getInstance("PKCS11", sunpkcs11);
        char[] pin = pinAcceso.toCharArray();
        keyStore.load(null, pin);

        //Recogemos la lista de alias de certificados del dispositivo
        final Enumeration<String> enumeration = keyStore.aliases();

        //Buscamos el alias del certificado de Firma, en el DNIe hay dos, uno para
        //firmar (CertFirmaDigital) y otro para autenticarse (CertAutenticacion)
        String alias = null;
        while(enumeration.hasMoreElements()) {

            alias = enumeration.nextElement().toString();
            if(CERT_FIRMA.equals(alias)) {
                break;
            }
        }

        //Se se ha encontrado el certificado de firma seguimos
        if(CERT_FIRMA.equals(alias)) {

            //Recogemos el certificado de firma
            final Certificate certificado = keyStore.getCertificate(alias);

            //Validamos la firma de los datos (en el dnie)
            final Signature verificadorFirma = Signature.getInstance("SHA1withRSA");
            verificadorFirma.initVerify(certificado.getPublicKey());
            verificadorFirma.update(datos);

            return verificadorFirma.verify(firma);

        } else {
            throw new Exception("No se encontro el certificado CertFirmaDigital de firma");
        }
    }

    /**
     * Ajustamos el proveedor SunPKCS11 apuntando a las librerías nativas pkcs11
     * del sistema. El proveedor SunPKCS11 no realiza funciones criptográficas
     * por si mismo, sino que permite a las aplicaciones Java usar los APIS JCA/JCE
     * para acceder a estas funcionalidades en las librerías nativas pkcs11.   
     */
    private SunPKCS11 ajustaPKCS11ProviderUbuntu() {

        final String pkcs11config = "name = DNIe\nlibrary = /usr/lib/opensc-pkcs11.so\n";
        final SunPKCS11 sunpkcs11 = new SunPKCS11(
                new ByteArrayInputStream(pkcs11config.getBytes()));
        Security.addProvider(sunpkcs11);
        return sunpkcs11;
        //TODO: Amplicar a otros sistemas operativos
    }
}

viernes, 25 de mayo de 2012

Boolean.TRUE versus true

El otro día comentábamos en el trabajo si era mejor, en cuanto a rendimiento, que en un método booleano se devolviese true o se devolviese Boolean.TRUE. Como en estas cuestiones mas vale una prueba que 2 horas de elucubraciones, tiramos el siguiente código:

public class AppTest {

    private static final int NUM_VUELTAS = 1000000000;

    public static void main(String[] args) throws Exception {
      
        long inic = System.currentTimeMillis();
        for (int x=0;x<NUM_VUELTAS;x++){
            for (int y=0;y<NUM_VUELTAS;y++){
                returnTrue1();
            }
        }
        System.out.println("return (boolean)true:"+(System.currentTimeMillis()-inic)+"ms");
      
        inic = System.currentTimeMillis();
        for (int x=0;x<NUM_VUELTAS;x++){
            for (int y=0;y<NUM_VUELTAS;y++){
                returnTrue2();
            }
        }
        System.out.println("return (boolean)true:"+(System.currentTimeMillis()-inic)+"ms");
      
        inic = System.currentTimeMillis();
        for (int x=0;x<NUM_VUELTAS;x++){
            for (int y=0;y<NUM_VUELTAS;y++){
                returnTrue3();
            }
        }
        System.out.println("return (boolean)true:"+(System.currentTimeMillis()-inic)+"ms");
      
        inic = System.currentTimeMillis();
        for (int x=0;x<NUM_VUELTAS;x++){
            for (int y=0;y<NUM_VUELTAS;y++){
                returnTrue4();
            }
        }
        System.out.println("return (boolean)true:"+(System.currentTimeMillis()-inic)+"ms");
    }
 
    private static boolean returnTrue1(){
        return true;
    }
 
    private static boolean returnTrue2(){
        return Boolean.TRUE;
    }
 
    private static Boolean returnTrue3(){
        return true;
    }
 
    private static Boolean returnTrue4(){
        return Boolean.TRUE;
    }
}
Los resultados, tras la primera ejecución los resultados parecían confusos:

return (boolean)true:6ms
return (boolean)Boolean.TRUE:4ms
return (Boolean)true:5ms
return (Boolean)Boolean.TRUE:4ms
Pero alterando el orden de las llamadas la confusión se disipó:

return (boolean)Boolean.TRUE:7ms
return (Boolean)true:5ms
return (Boolean)Boolean.TRUE:4ms
return (boolean)true:4ms
return (Boolean)true:7ms
return (Boolean)Boolean.TRUE:4ms
return (boolean)true:4ms
return (boolean)Boolean.TRUE:4ms




return (Boolean)Boolean.TRUE:7ms
return (boolean)true:4ms
return (boolean)Boolean.TRUE:4ms
return (Boolean)true:5ms

Resumiendo los tiempos:

  • returnTrue1: 6+4+4+4 --> 18 
  • returnTrue2: 4+7+4+4 --> 19 
  • returnTrue3: 5+5+7+5 --> 22 
  • returnTrue4: 4+4+7+7 --> 22

Analizando qué hace cada método:
  • returnTrue1. Devuelve un true. 
  • returnTrue2. Lee a una dirección de memoria y devuelve un "autounboxing" de su contenido. 
  • returnTrue3. Hace un "autoboxing" sobre un true. 
  • returnTrue4. Devuelve una dirección  de memoria.
A mi entender, los resultados de más rápido a más lento deberían haber sido:
  • returnTrue1 y returnTrue4. Igual de rápidas. 
  • returnTrue3.
  • returnTrue2.
Conclusión. Discutir sobre qué es más óptimo en este tema es perder el tiempo, dado que parece depender en gran medida de las optimizaciones internas del compilador. Es mas interesante elegir la opción que de mas claridad al código. 

Nota. Sobre autoboxing y autounboxing.

autounboxing. Es la transformación de objeto a tipo básico, por dentro se realiza una llamada a Boolean.booleanValue(Boolean.TRUE):

   boolean boolVar = Boolean.TRUE;

autoboxing. Es la otra transformación de tipo básico a objeto, por dentro se realiza una llamada a Boolean.valueOf(true):

   Boolean boolVar = true;

jueves, 24 de mayo de 2012

Habilitar USB en Virtualbox sobre Ubuntu 12.04

En un Virtualbox instalado sobre Ubuntu 12.04 hemos tenido problemas para poder acceder a dispositivos USB, concretamente al lector de tarjetas C3PO LTC31

La solución pasa por:

1) Añadir nuestro usuario al grupo vboxusers mediante el siguiente comando, donde xxxxxxxx el el idusuario que hace login: 

sudo usermod -aG vboxusers xxxxxxxxxx

Tras este ajuste reiniciamos sesión (si no, no tiene efecto).

2) En la configuración de cada servidor virtual que deba acceder al dispositivo USB deberemos añadir un filtro con la opción "Añadir filtro desde dispositivo", eligiendo el dispositivo USB que necesitemos hacer visible.

Nota. Si queremos manejar dispositivos USB 2.0, deberemos añadir el "Oracle VM VirtualBox Extension Pack" en: Archivo/Preferencias/Extensiones

miércoles, 23 de mayo de 2012

Enlaces para verificación de tarjetas criptográficas

Para verificar que tenemos perfectamente instalos los módulos criptográficos de las tarjetas de la FNMT, DNIe y CACERT, tenemos estos enlaces:

DNIe: https://av-dnie.cert.fnmt.es/compruebacert/compruebacert
FNMT: https://www.cert.fnmt.es/index.php?cha=cit&sec=8&fpage=1&lang=es
CACERT: https://secure.cacert.org/index.php?id=4

Nota. Para acceder al enlace del DNIe deberemos tener un DNIe en un lector de tarjetas del ordenador. Igualmenet para acceder a la página de CACERT deberemos tener un certificado CACERT. En cualquier otro caso obtendremos un error "ssl_error_handshake_failure_alert" dado que no tenemos el certificado adecuado para negociar la conexión SSL.

martes, 22 de mayo de 2012

Instalar el DNI-e electrónico en Ubuntu 12.04

Para instalar el DNIe en Ubuntu 12.04 LTS con un lector C3PO LTC31 he seguido el tutorial siguiente:

http://www.ubuntu-guia.com/2010/08/dni-e-electronico-ubuntu-instalar.html?showComment=1337685250484#c3500494686808401341

Sólo he necesitado ajustar el subversion para que saliese por el proxy, para lo cual he tenido que ajustar el fichero: /etc/subversion/servers

Descomentando las siguientes líneas e introduciendo los datos valores adecuados:

[Global]
http-proxy-host = XXX.XXX.XXX.XXX
http-proxy-port = XXXX
http-proxy-username = XXXXXXXX
http-proxy-password = XXXXXXXX

COPIA (Por aquello de no perder un trabajo tan bueno, que nunca se sabe, replico íntegramente el contenido del anterior enlace.)

Para poder utilizar sin problemas el DNI-e (electrónico) y de esta forma poder acceder a páginas web oficiales y hacer todo tipo de trámites sin tener que ir de oficina en oficina, seguiremos los siguientes pasos:

 

 1. - Instalación del hardware (lector)

En mi caso he instalado el lector c3po LTC31.

La mayoría de los lectores de DNI-e funcionan en GNU/Linux con el driver CCID a través del servicio pcscd.

1.1 - Instalación del controlador

Para Ubuntu y en general todas las derivadas de Debian, instalar los siguientes paquetes:
  • libccid (biblioteca que proporciona una implementación PC/SC del controlador de IFD para lectores USB de tarjetas inteligentes, compatibles con el protocolo CCID. Este paquete es necesario para comunicarse con los lectores de tarjetas inteligentes a través de CCID
  • pcscd (El demonio PC/SC se utiliza para asignar / cancelar dinámicamente los controladores del lector y administrar conexiones a los lectores.)

Desde la terminal sería:

sudo apt-get install libccid pcscd

Si tu lector está basado en ACR38, instalar el controlador:

sudo apt-get install libacr38u

1.2 - Instalación de librerías o bibliotecas

Instalar las siguientes librerías o bibliotecas y paquetes complementarios:

  • pinentry-gtk2 (Programa que permite garantizar la entrada de PIN o frases de contraseña. Esto significa que trata de cuidar que la información introducida no se intercambie en el disco o se almacene temporalmente en cualquier lugar. Esta funcionalidad es especialmente útil para introducir frases de contraseña cuando se utiliza software de cifrado GnuPG o como clientes de correo electrónico utilizando el mismo. Utiliza un protocolo abierto y por lo tanto, no vinculado a determinados programas)
  • pcsc-tools ("Gscriptor" contiene herramientas útiles para PC/SC, como pcsc_scan que analiza lectores de tarjetas inteligentes disponibles otras detectan los eventos como la inserción y extracción de la tarjeta con ATR ...)
  • libpcsclite1 y libpcsclite-dev (El propósito de PC/SC Lite es proporcionar una interfaz de tipo SCard de Windows(R) para comunicarse con tarjetas inteligentes y lectores)
  • libreadline6 y libreadline-dev (una biblioteca readline de GNU que ayuda en la consistencia de interfaces de usuario a través de programas discretos que necesitan proporcionar una interfaz en línea de órdenes)
  • coolkey (Soporte de controladores de Linux para las tarjetas inteligentes CoolKey and Common Access Card (CAC) con claves de seguridad utilizadas en una Infraestructura de Clave Pública (PKI). El módulo de libpkcs11 permite el uso de tarjetas inteligentes en aplicaciones como Mozilla Network Security Services (NSS))

Instalación de todos estos paquetes desde la terminal:

sudo apt-get install pinentry-gtk2 pcsc-tools libpcsclite1 libpcsclite-dev libreadline6 libreadline-dev coolkey

Llegados a este punto ya podemos probar si Ubuntu detecta el lector y puede leer el DNI-e, una vez conectado el lector usb e introducido el DNI-e.

Para ver si Ubuntu detecta el lector ejecuta en una terminal el siguiente comando:

lsusb

La terminal nos responderá algo parecido a:

    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
    Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
    Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
    Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
    Bus 002 Device 002: ID 0783:0006 C3PO LTC31v2
    Bus 005 Device 002: ID 046d:c05a Logitech, Inc. Optical Mouse M90


En la línea Bus 002 Device 002: ID 0783:0006 C3PO LTC31v2, vemos que ha detectado mi lector "c3po LTC31" (el nombre variará según sea vuestro lector)

Ahora vamos a ver si lee el DNI-e con el comando:

pcsc_scan

La terminal nos devolverá algo parecido a:

    PC/SC device scanner
    V 1.4.18 (c) 2001-2011, Ludovic Rousseau
    Compiled with PC/SC lite version: 1.7.4
    Using reader plug'n play mechanism
    Scanning present readers...
    0: C3PO LTC31 (00452764) 00 00

    Tue May 15 19:36:54 2012
    Reader 0: C3PO LTC31 (00452764) 00 00
    Card state: Card inserted,
    ATR: 1A 2B 36 00 00 00 8k 44 5L 49 1A 2B 36 00 00 00 8K 44 5L 49

    ATR: 3B 7F 38 00 00 00 6A 44 4E 49 65 2B 36 00 00 00 8K 44 5L 49
    + TS = 3B --> Direct Convention
    + T0 = 7F, Y(1): 0111, K: 15 (historical bytes)
    TA(1) = 38 --> Fi=744, Di=12, 62 cycles/ETU
    64516 bits/s at 4 MHz, fMax for Fi = 8 MHz => 129032 bits/s
    TB(1) = 00 --> VPP is not electrically connected
    TC(1) = 00 --> Extra guard time: 0
    + Historical bytes: 00 8k 44 5L 49 1A 2B 36 00 00 00 8K 44 5L 49
    Category indicator byte: 00 (compact TLV data object)
    Tag: 6, len: A (pre-issuing data)
    Data: 1A 2B 36 00 00 00 8k 44 5L 49
    Mandatory status indicator (3 last bytes)
    LCS (life card cycle): 03 (Initialisation state)
    SW: 9000 (Normal processing.)

    Possibly identified card (using /usr/share/pcsc/smartcard_list.txt):
    1A 2B 36 00 00 00 8k 44 5L 49 1A 2B 36 00 00 00 8K 44 5L 49
    1A 2B 36 00 00 00 8k 44 5L 49 1A [1,2]0 36 00 00 00 8K 44 5L 49
    DNI electronico (Spanish electronic ID card)
    http://www.dnielectronico.es


Vemos que ha leido el DNI-e en las líneas:

    Reader 0: C3PO LTC31 (00452764) 00 00
    Card state: Card inserted,


Pulsamos la combinación de teclas CTRL+C para salir y cerramos la terminal

2. - Instalación del software del DNI-e

Hay 2 métodos:

2.1 - Asistente de ZonaTic (No funciona a 22/05/2012)

En la página de descargas del DNI-e hay un enlace al asistente de ZonaTic.

En estos momentos este asistente aún no soporta Ubuntu 12.04 y dará error al ejecutarlo, pero lo dejo aquí en previsión de que en un futuro funcione.

ZonaTIC ha creado un asistente para la instalación del DNI-e que nos va a facilitar su instalación:
https://zonatic.usatudni.es/gl/aplicaciones/asistente-dnie.html

En la misma página nos advierte al principio que es necesario tener instalado " Java Access Bridge". Instalación:

sudo apt-get install libaccess-bridge-java

Pulsamos en "Zona de descargas" y descargamos la versión para Linux (Ubuntu) en nuestra carpeta personal.

Damos permisos al archivo descargado, haciendo clic derecho sobre él y seleccionando "propiedades" y en la pestaña permisos activar la casilla "permitir ejecutar el archivo como un programa".

Lo ejecutamos desde una terminal con el comando (considerando que el nombre del archivo descargado sea 'instalador-dnie'):

sudo ./instalador-dnie

Comienza el asistente. Aquí tenéis una detallada explicación por si os surge alguna duda:
https://zonatic.usatudni.es/aplicaciones/asistente-dnie/ayuda

2.2 - Compilación del código fuente (Sí me funcionó)


Lo primero que vamos a hacer es instalar los certificados del DNI-e para instalarlos en el navegador, uno para la autoridad de certificación y otro para la Autoridad de validación del DNIe. Para ello vamos a la siguienta página:
http://www.dnielectronico.es/seccion_integradores/certs.html
  • Para la Autoridad de certificación (La Dirección General de la Policía (Ministerio del Interior) actúa como Autoridad de Certificación (AC)):
            Haz clic en el enlace: "AC Raíz"
            y descarga el archivo: "pkcs1-sha256WithRSAEncryption".
  • Para la Autoridad de validación del DNIe (certificado que suministra información sobre la vigencia de los certificados electrónicos, que será el de la FMNT (Fábrica Nacional de Moneda y Timbre)):
            Haz clic en el enlace: “AV DNIE FNMT"
            y descarga el archivo: "pkcs1-sha256WithRSAEncryption".

Una vez descargados, hacemos en cada uno respectivamente, clic derecho y seleccionamos "extraer aquí" y ya tendremos los certificados "ACRAIZ-SHA2.crt" y "AVDNIEFNMTSHA2.cer".

Abrimos Firefox e importamos los certificados:

Para la Autoridad de certificación vamos al menú: "Editar > Preferencias > Avanzado > Cifrado > Ver certificados" y en la pestaña "Autoridades", hacemos clic en "Importar" y seleccionamos el archivo descomprimido "ACRAIZ-SHA2.crt".

Se nos abrirá una ventana en la que debemos de marcar las 3 opciones de confianza (muy importante). Si lo hubieramos dejado en blanco se pueden volver a activar, pulsando en el certificado "AC RAIZ DNIE" y pulsando en (editar confianza ...):

Nos aseguramos de se ha importado buscando en la lista de certificados "DIRECCIÓN GENERAL DE LA POLICIA (AC RAIZ DNIE)":

Para la Autoridad de validación del DNIe en el mismo menú (Editar > Preferencias > Avanzado > Cifrado > Ver certificados), en la pestaña "Servidores", hacemos clic en "Importar" y seleccionamos el archivo descomprimido "AVDNIEFNMTSHA2.cer".

Nos aseguramos de se ha importado buscando en la lista de certificados "DIRECCIÓN GENERAL DE LA POLICIA (AC DNIE FNMT)":

Para compilar el código fuente necesitaremos instalar algunas herramientas:
  • build-essential (Siempre lo recomiendo en cosas que hacer después de instalar Ubuntu, pero no está mal asegurarse de su instalación. Este paquete contiene una lista informativa de los paquetes considerados esenciales para la creación de paquetes .deb)
  • autoconf (El estándar para los paquetes de código fuente de FSF (Fundación para el software libre))
  • subversion (también conocido como svn, es un sistema de control de versiones muy parecido a «Concurrent Versions System» (CVS))
  • openssl y libssl-dev (Este paquete contiene los binarios y las biblioteca relacionadas para openssl)
  • libtool y libltdl-dev (script de soporte para bibliotecas genéricas. Libtool esconde la complejidad de generar tipos especiales de bibliotecas (como las bibliotecas compartidas) tras una interfaz consistente. Para usar libtool, agregue los nuevos comandos genéricos de construcción de bibliotecas a su Makefile, Makefile.in, o Makefile.am)
sudo apt-get install build-essential autoconf subversion openssl libssl-dev libtool libltdl-dev

Lo que vamos a hacer es compilar el código fuente de una versión de "opensc" especialmente modificada para el DNI-e, por lo que si tenemos instalada la versión de los repositorios hay que desinstalarla antes con el comando:

sudo apt-get autoremove opensc

Creamos un nuevo directorio en nuestra carpeta personal donde descargar el código fuente y compilar con posterioridad. Lo llamaremos por ejemplo ".openscDNIe" (el punto delante es para que esté oculto y no nos estorbe en nuestra carpeta personal(CTRL+H para ver archivos ocultos)). Lo creamos con el comando:

mkdir .openscDNIe

Nos colocamos dentro del nuevo directorio con el comando:

cd .openscDNIe

En el servidor de Cenatic están los archivos actualizados para la última versión y los descargamos directamente con el siguiente comando:

svn checkout https://svn.forge.morfeo-project.org/opendnie/opensc-opendnie/trunk

Al introducir el comando, la terminal te pedirá que aceptes un certificado para la descarga:

    Error validando el certificado del servidor de «https://svn.forge.morfeo-project.org:443»:
    - El nombre de máquina del certificado no coincide.
    - El certificado ha expirado.
    Información del certificado:
    - Nombre de máquina: morfeo-project.org
    - Válido desde Thu, 18 Mar 2010 09:57:43 GMT hasta Sat, 17 Mar 2012 09:57:43 GMT
    - Emisor: http://www.cacert.org, Root CA
    - "Huella": f0:73:dc:0a:01:16:fb:85:df:f4:17:9b:50:e6:55:37:2b:38:65:39
    ¿(R)echazar, aceptar (t)emporariamente o aceptar (p)ermanentemente?


Lo aceptamos temporalmente, escribiendo "t" ( te, sin ") y pulsando Enter.

Al finalizar la descarga se habrá creado una nuevo directorio llamada "trunk" dentro del directorio donde estamos colocados en la terminal: ".openscDNIe".

Dentro de Trunk está todo el código fuente, por lo que debemos de colocarnos dentro de él, para comenzar la compilación, con el comando:

cd trunk

Atención, durante la instalación debemos de ejecutar todos los comandos desde esta misma terminal y siempre dentro del directorio ".opendnie/trunk", manteniendo el prompt (línea inicial del terminal) en: "usuario@usuario-equipo:~/.opendnie/trunk$"

Antes de la compilación hay que añadir la ruta de la librería "libltdl.la" en el archivo "src/tools/Makefile.am" del código fuente (dentro de "trunk"). La ruta variará según sea la arquitectura (32 o 64 bits) de nuestro Ubuntu. Para ver la ruta ejecutamos en la terminal:

sudo find / -name libltdl.la

La respuesta puede tardar y nos devolverá la ruta exacta (en este ejemplo es para 64 bits):

    /usr/lib/x86_64-linux-gnu/libltdl.la

Editamos el archivo del Makefille.am con el comando:

sudo gedit src/tools/Makefile.am

Buscamos la siguiente línea:

    LIBS = $(top_builddir)/src/common/libcompat.la \
                  $(top_builddir)/src/libopensc/libopensc.la


Quedaría así para el ejemplo:

    LIBS = $(top_builddir)/src/common/libcompat.la \
                  $(top_builddir)/src/libopensc/libopensc.la \
                  /usr/lib/x86_64-linux-gnu/libltdl.la


Guarda y cierra el editor de textos.

Preparamos el entorno de compilación con el comando:

./bootstrap

Al terminar y ver de nuevo el prompt ("usuario@usuario-equipo:~/.opendnie/trunk$") ejecutamos:

./configure --prefix=/usr

Al terminar y ver de nuevo el prompt ("usuario@usuario-equipo:~/.opendnie/trunk$"), compilamos e instalamos con:

sudo make install

Cuando finalice la compilación e instalación (puede tardar), el dispositivo o módulo de seguridad "PKCS11" habrá quedado instalado en la ruta: "/usr/lib/opensc-pkcs11.so"

Para finalizar hay que añadir el módulo y su ruta al navegador. Para ello abrimos firefox y vamos al menú "Editar > Preferencias > Avanzado > Cifrado > Dispositivos de seguridad", pulsamos en "Cargar" y en la nueva ventana:

    En "Nombre del módulo", escribimos: PKCS11
    En "Archivo del módulo", escribimos la ruta: /usr/lib/opensc-pkcs11.so

Reiniciamos el navegador y ya esteremos en disposición de utilizar el DNI-e.

A la hora de escribir el pin del DNI-e, aparece un aviso de: "Identificación protegida por Token":

Para solventarlo editamos el archivo "opensc.conf" con el comando:

sudo gedit /usr/etc/opensc.conf

Buscamos las líneas (son 2, una al principio y otra al final del archivo) que pongan:

    # enable_pinpad = false;

y las descomentamos (quitar la almohadilla que hay delante de la línea). Deben de quedar así:

    enable_pinpad = false;

Reiniciamos el sistema.

Una vez reiniciado ya podemos probar el DNI-e en:
http://www.dnielectronico.es/como_utilizar_el_dnie/verificar.html

Nos da unas explicaciones y al final está el enlace de comprobación.

El lector parpadeará y nos pedirá el pin del DNI-e. Lo escribimos y aceptamos

Nota: La primera vez Firefox, nos avisará de que la conexión no estará verificada. Pulsamos en "Entiendo los riesgos" y luego en "Añadir una excepción":

3. - Pin y certificado personal del DNI-e

El pin te lo dieron al recibir el DNI-e.

En las comisarías de Policía hay puntos de Actualización del DNI-e, donde introduciendo tu DNI-e y tu huella dactilar, puedes cambiar el pin (si lo has perdido) y activar el certificado (si está caducado).

Los certificados caducan a los 30 meses (creo recordar) y se puede volver a activar en la misma comisaría, pulsando en "acceder a tus datos personales > renovar certificado" (Te volverá a pedir que pongas la huella dactilar en el lector de huellas).

lunes, 21 de mayo de 2012

Bug en hql de Hibernate 3.2.5 usando sub-select

Usando la librería hibernate3-3.2.5.ga.jar nos ha saltado el siguiente error:

java.lang.NullPointerException at org.hibernate.hql.ast.tree.FromClause.findIntendedAliasedFromElementBasedOnCrazyJPARequirements(FromClause.java:120)
at org.hibernate.hql.ast.tree.FromClause.getFromElement(FromClause.java:107)
at org.hibernate.hql.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:81)

Con la siguiente query HQL:

hql = "select count(e.notif) from Estado e ";
hql += "where e.notif.idApp = :idaplic ";
hql += "and e.estado='BAJA' ";
hql += "and e.fechaHoraEstado=(select max(e1.fechaHoraEstado) from Estado e1 where e1.notif.id = e.notif.id) ";
hql += "and (select count(e2) from Estado e2 where e.notif.id = e2.notif.id and e2.estado=:estadoPreborrado)>0";
query = getJpaTemplate().getEntityManager().createQuery(hql);
query.setParameter("estadoPreborrado", estado.getMensaje());
query.setParameter("idaplic", idApp);
numNotifEstadoPreborrado = ((Long) query.getSingleResult()).intValue();

"Googleando" un poco, parece que es un bug reconocido que estará resuelto en versiones posteriores, pero si no se quiere cambiar de versión, la forma de lanzar la query sin problemas es cambiar el orden de las condiciones a:

hql = "select count(e.notif) from Estado e ";
hql += "where e.fechaHoraEstado=(select max(e1.fechaHoraEstado) from Estado e1 where e1.notif.id = e.notif.id) ";
hql += "and (select count(e2) from Estado e2 where e.notif.id = e2.notif.id and e2.estado=:estadoPreborrado)>0";
hql += "and e.notif.idApp = :idaplic ";
hql += "and e.estado='BAJA'";
query = getJpaTemplate().getEntityManager().createQuery(hql);
query.setParameter("estadoPreborrado", estado.getMensaje());
query.setParameter("idaplic", idApp);
numNotifEstadoPreborrado = ((Long) query.getSingleResult()).intValue();
Parece que las condiciones que impliquen una sub-select deben ser las primeras.

miércoles, 16 de mayo de 2012

Configuración manual del plugin java para Firefox en Ubuntu 12.04

Cuando se instala a mano un JRE (por ejemplo uno de Oracle), hay que indicar a Firefox cual es el plugin para tratar con Applets. Para configurar esto deberemos:
  1. Localizar el fichero libnpjp2.so, que habitualmente tendremos en donde hayamos instalado el JRE:  /xxxxxxx/jre1.6.0_31/lib/amd64/libnpjp2.so
  2. Crear un enlace al anterior fichero en la carpeta /usr/lib/mozilla/plugins, para ello nos movemos a estar carpeta y tecleamos: 
sudo ln -s /xxxxxxx/jre1.6.0_31/lib/amd64/libnpjp2.so

Nota. Si por ejemplo tenemos instalados dos JRE: jre1.6.0_31 y jre1.7.0_10, podríamos necesitar cambiar de uno a otro para probar applets con diferentes versiones de Java. Para tener esta flexibilidad podemos tener GAlternatives instalado, hay alguna otra entrada donde se describe esta utilidad.

Arrancamos GAlternatives y creamos o editamos la entrada libnpjp2.so, la configuraremos en modo manual y añadiremos las opciones:

    /xxxxxxx/jre1.6.0_31/lib/amd64/libnpjp2.so
    /xxxxxxx/jre1.7.0_10/lib/amd64/libnpjp2.so

Marcaremos como "por defecto" la que deseemos y listo.


jueves, 10 de mayo de 2012

Gestión rápida de JDK/JRE en Ubuntu 12.04 LTS

Si en un equipo necesitamos tener instalados diferentes versiones del JDK, diferentes versiones del JRE, diferentes versiones del openJDK ó diferentes versiones del openJRE, y necesitamos poder ajustar que el compilador (javac) sea un jdk1.6.0_31, que el runtime (java) sea un jre1.6.0_29 o que el software de Oracle que permite descargar y ejecutar aplicaciones Java desde la Web (javaws) sea  java-6-openjdk, con cierta agilidad agilidad, lo mejor es:
  • Tener descargadas e instaladas las distintas versiones del JDK y JRE que vayamos a necesitar.
  • Descargar e instalar galternatives con: sudo apt-get install galternatives
  • Iniciar la aplicación y buscar los items "java/javac/javaws"
  • Definir en cada item las alternativas que nos interesen.
Para gestionar desde línea de comando qué alternativas para "java" han quedado registradas deberemos teclear: sudo update-alternatives --config java

Obtendremos como respuesta:

Existen 7 opciones para la alternativa java (que provee /usr/bin/java).

  Selección   Ruta                                            Prioridad  Estado
------------------------------------------------------------
  0    /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java   1061      modo
manual
  1    /home/usr1/java/jres/jre1.6.0_24/bin/java   20        modo manual
  2    /home/
usr1/java/jres/jre1.6.0_25/bin/java   20        modo manual
  3    /home/
usr1/java/jres/jre1.6.0_27/bin/java   20        modo manual
  4    /home/
usr1/java/jres/jre1.6.0_29/bin/java   20        modo manual
  5    /home/
usr1/java/jres/jre1.6.0_30/bin/java   20        modo manual
* 6   /home/
usr1/java/jres/jre1.6.0_31/bin/java   20        modo manual
 

Pulse <Intro> para mantener el valor por omisión [*] o pulse un número de selección:

Nota. Si en un momento dado necesitamos añadir una nueva categoría a galternatives, podemos usar:

sudo update-alternatives --install nombre_genérico enlace programa prioridad

Ajuste del proxy del sistema en Ubuntu 12.04 LTS

Tras instalar Ubuntu 12.04 LTS desde CD en un equipo limpio nos hemos encontrado con que no éramos capaces de ajustar la configuración del proxy ni mediante el gestor de actualizaciones (tocando el fichero /etc/apt/apt.conf) ni ajustando el proxy a nivel general del sistema (jugando con el ajuste de Red del área de Configuración del Sistema).

Tras muchas pruebas, la única forma de ajustar el proxy para que el gestor de paquetes APT (Advanced Packaging Tool) pudiese realizar la primera actualización completa ha sido la siguiente:

- Crear el fichero /etc/apt/apt.conf.d/02proxy
- Meterle la siguiente línea:
    Acquire::http::Proxy "http://idusuario:password@xxx.xxx.xxx.xxx:puerto";

Tras ésto, se reinicia el equipo y se pulsa la opción actualizar software del menú que se despliega en el icono superior derecho.

Nota. Tras lanzar el gestor de actualizaciones y reiniciar el equipo, ya es efectivo el ajuste del proxy a nivel de sistema el apartado "Red" de la herramienta "Configuración del sistema". Para ajustar el proxy del sistema, deberemos:

- Seleccionar el método manual (el automático con la URL de configuración no funciona al menos con nuestro proxy)
- Definir el host como usuario:password@ipproxy
- Meter el puerto en el cuadro de diálogo apropiado