Driver DNIe en Java

 

 

Aquí os dejo una librería en Java que permite acceder al DNIe electrónico. No necesita los drivers oficiales del DNIe, sino que se comunica directamente con la tarjeta.

En la página http://puttydnie.es/puttydnie/dnie-provider encontraréis un par de ejemplos de cómo utilizarlo.

Comentarios

Selecciona arriba tu forma preferida de visualizar los comentarios y pulsa el botón para guardar tu elección para próximas visitas (sólo si eres usuario registrado).
Fernando Acero's picture

Creo que la noticia más impactante de esto....


Mientras había "recompensa" por el driver:

http://www.inteco.es/convocatorias/contratacion/procedimientos_en_vigor/...

Ha ocurrido esto:

http://puttydnie.es/puttydnie/dnie-provider

Es decir, que alguien lo ha hecho gratis, lo malo que a pesar de haberlo hecho, nunca habría podido optar a cobrarlo por los requisitos del concurso.

Felicidades al autor.

"Copyleft Fernando Acero Martí­n. Verbatim copying, translation and distribution of this entire article is permitted in any digital medium, provided this notice is preserved. Quotation is allowed."

jonsito's picture

Al autor: Estas tardando


En ir al registro de la propiedad intelectual.

Hablo totalmente en serio.

Si alguien se lee el pliego de condiciones verá que es imposible cumplirlo... salvo que ya esté escrito/copiado... y ¿pre-asignado?

El pliego especifica un mínimo de cuatro personas ( se proponen: jefe de proyecto, director de calidad, programador, y documentalista ) en el equipo de desarrollo y una empresa con al menos 10 trabajadores. Si echamos cuentas son 60.000 euros cuatro personas, cuatro meses, mas comisiones, mas inversiones, dietas, viajes, gastos de administración... salvo que al jefe de proyecto -y por extensión a los demás, pero a la baja- les des sueldo mileurista, no salen las cuentas... y hacer el driver cumpliendo las especificaciones en cuatro meses partiendo de cero... suena a chiste: solo la preparación y elaboración de la documentación preliminar se come un mes entero

Voy a ver si saco tiempo para echarle un vistazo al driver... ya conocía algunas implementaciones java de applets de firma para el DNIe basados en el provider pkcs11, pero no había visto nunca un provider "nativo"*

A ver si entre todos le damos impulso al DNIe. Se me ocurren virguerías basadas en la combinación SoC (SystemOnChip) + lector + keypad con display para TPV's y demás familia... muchísimo más seguras que un PC

[*] "nativo" quiere decir que no depende de pkcs11, sino que se accede directamente mediante java.smartcard.io Por consiguiente todavía se necesita pcsc/ccid en la máquina cliente así como los drivers de bajo nivel del lector. En el pliego de requisitos dan a entender que no debe haber bibliotecas jni ni requerir drivers adicionales, lo cual es un imposible metafísico :-)

Juan Antonio

Gegen die Dummheit kämpfen selbst die Götter vergebens - Schiller

jonsito's picture

Revisitando el comentario...


Por lo que estoy viendo, Luis Fernando (el autor) se ha tenido que leer el pliego de condiciones... porque en la web pone algunas frases calcadas de éste :-)

Tendré que investigar algo más al respecto, pero algo suena raro: Estuve hace poco en contacto con una empresa que estaba por participar en el concurso, pero finalmente se echó atrás por -entre otras- las condiciones que indico en el post anterior

Raroraroraro... un concurso extraño, que prácticamente exige que el trabajo esté previamente hecho, y luego alguien que publica un trabajo que cumple con el pliego de condiciones de dicho concurso... una semana antes de que éste se adjudique...

Por cierto, salvo que la petición y gestión de pin se haga a nivel del crypto-api de java, creo que al código ejemplo de firma del pdf falta/falla algo...

Juan Antonio

Gegen die Dummheit kämpfen selbst die Götter vergebens - Schiller

lfern's picture

Ejemplo de firma - gestión del PIN


En realidad es la "clase" del driver la que se encarga de mostrar al usuario la ventana de solicitud del PIN, por eso en el ejemplo no ves que al abrir el keystore se pase el pin como parámetro. Podría hacerse así, pasándolo como parámetro, pero pense en la posibilidad "remota" de que pudiera haber más de un DNIe presente. Si así fuera creo que sería complicado seleccionar un DNIe u otro con la interfaz que proporciona KeyStore de Java (o al menos no se me ocurrió).

jonsito's picture

Me lo imaginaba...


... Se me ocurrió despues de publicar el comentario, pero quería esperar a ver el driver por si acaso...

Todavía no he tenido tiempo de mirarlo a fondo, pero me han surgido un par de dudas:

- ¿Has previsto la posibilidad de que se pueda usar el driver en entornos headless (sin consola gráfica)? ¿Cómo haces en dicho caso para pedir el PIN?
(EDITO: acabo de ver que utilizas un Dialog java... :-()

- El pliego de condiciones habla de poder hacer cache del certificado (que no del PIN), pero hasta donde yo sé una de las "desgraciadas" features del DNIe es que necesitas el PIN para leer (no para enumerar) los certificados

- Así mismo el pliego indica (y tambien tu lista de TODO's) que hay que poder reconstruír la parte pública del certificado sin PIN... cosa que se me antoja harto difícil: puedes enumerarlos sin problemas, incluso obtener parte del contenido, pero no veo cómo obtener -por ejemplo- la clave pública sin PIN

¿Cómo tienes previsto pues "reconstruír" -que no enumerar- los certificados sin pedirle PIN al usuario?

- Del mismo modo, la documentación para desarrolladores que yo tengo indica que el DNI soporta firmas con SHA1withRSA, SHA-256withRSA, pero no habla nada de SHA-384withRSA y SHA-512withRSA. O bien la documentación oficial es incompleta, o bien (como creo que indicas) utilizas BouncyCastle para implementar los algoritmos que faltan... ¿me lo puedes confirmar?

Mas cosas:

* La primera y fundamental: te confirmo que funciona en Linux (con pcsc-lite+libccid) en un Fedora 16 i686

* Habría que mirar un poco el tema velocidad: no encuentro diferencias significativas con OpenDNIe en la consulta de certificados (desde modo terminal)

* La segunda: en el ejemplo primero de la página web, te pide el pin innecesariamente: El DNIe no necesita PIN para enumerar certificados. No sé si el código del provider lo requiere, pero es algo que habría que tener en cuenta

* Continuando con el ejemplo: la aplicación me enumera los dos certificados, y las dos claves... pero les asigna unos "Aliase" un tanto esotéricos... Te sugeriría que usaras los mismos alias que el driver pkcs11 del driver de la policía o del propio OpenDNIe. Así las aplicaciones podran acceder de forma transparente tanto al "provider pkcs11" como al "provider dnie"

No he tenido tiempo de mirar el tema de la firma y del funcionamiento en Mac. Dame un par de horas... :-)

Por si sirve de ayuda, adjunto el programa de prueba que he usado en Linux:

jantonio@router javadnie]$ cat TestJavaDNIe.java 
import java.security.Security;
import java.security.Provider;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;
import org.dnieprov.jce.provider.DnieProvider;
public class TestJavaDNIe {
    public static void main(String [] args) {
	try {
            Provider p = new DnieProvider();
            Security.addProvider(p);
            KeyStore ks = KeyStore.getInstance("DNIe");
            ks.load(null,null);
            Enumeration aliases = ks.aliases();
            while (aliases.hasMoreElements()) {
                String alias = aliases.nextElement();
                System.err.println("Alias: "+ alias);
                if (ks.isCertificateEntry(alias)){
                    System.err.println(">>> is cert");
                    Certificate cert = ks.getCertificate(alias);
                    System.out.println(cert);
                } else {
                    System.err.println(">>> is key");
                }
            }
        } catch (Exception e) {
            System.err.println(e.toString());
            e.printStackTrace();
        }
    }
}

Enhorabuena por el trabajo. Un saludo

Juan Antonio

Gegen die Dummheit kämpfen selbst die Götter vergebens - Schiller

lfern's picture

un par de dudas??


Antes de nada, gracias por tus comentarios y por confirmar que funciona en Linux. Voy a hacer una prueba tambien con VirtualBOX porque ahora no tengo posibilidad de montarme una máquina de pruebas en linux.

- Entornos headless. Sí lo he tenido en cuenta, pero creo que no funciona como debe. Cuando detecta que está en entorno headless utilizo System.console(), pero me devuele null. Creo que supuse algo que no es cierto y debo utilizar otro método para que el usuario escriba el PIN.

- "reconstrucción" (creo que en la página también lo pongo entre comillas). Este tema lo consulté a INTECO, porque sonaba un poco raro. La respuesta fue que el comportamiento debía ser similar al nuevo driver de Windows 7. Echa un vistazo a la página http://puttydnie.es/puttydnie/notas-dnie-windows7 . Aunque no esté muy bien escrito son unas notas acerca del tema. MI intención es mejorar el driver para que haga algo muy parecido a eso.

- Respecto a los algoritmos SHA, sí, vienen de BC, pero también te digo que no he probado a ver cómo funciona. (Este código estaba dentro de un desarrollo de un applet de firma de PDFs. Es con lo que lo he estado probado, firma con iText de PDFs, en el que utilizo SHA1).

- velocidad. probablemente se puedan ordenar algunos comandos APDU para ahorrar tiempo, pero el código es posible que se "ensucie" un poco más. Primero quiero centrarme en las mejoras que comentabas.

- PIN para certificados en el ejemplo. Es cierto que no necesitas el PIN para enumerarlos. A la hora de pedir los alias disponibles, no es necesario el PIN. Pero cuando se invocara a alguno de los métodos del KeyStyore en los que se pide el certificado, entonces necesitas el PIN (o el utilizar el sistema de "reconstrucción").

- ALIAS. Tienes razón. Pero creo que tampoco puede ser solo "CertAutenticacion" o "CertFirmaDigital", al menos bajo el enfoque que le he dado inicialmente: que puede haber dos DNIe conectados. Quizás "CertAutenticacion-" seguido del número del DNIe, o algo así quedaría un poco mejor.

jonsito's picture

Te confirmo...


... Que tambien funciona en Mac ( MacMini con SnowLeopard )

A ver si puedo mirar lo del System.console()...

Según lo que entiendo pues sobre la "reconstrucción" de los certificados, lo que pasa es que "al menos" siempre se pide el pin la primera vez, y que se debe guardar de algun modo (memoria/registro de windows) información suficiente para poder recuperar la parte pública del certificado, sin tener que volver a pedir el pin)

Si: CertAutenticacion-xxxx y CertFirmaDigital-xxxx podría ser una solucion para los nombres de los alias habría que cambiar "equals()" por "startsWtih()", pero creo que sería un mal menor. En cualquier caso habría que consultarlo con INTECO

No sé si es el foro adecuado, o si es la pregunta oportuna, o si prefieres contestar por privado, o decirme que no es asunto mío... pero ¿Qué xxxx ha pasado con el concurso de INTECO para que ahora se publique este driver?

Un saludo, y de nuevo gracias por este estupendo trabajo

Juan Antonio

Gegen die Dummheit kämpfen selbst die Götter vergebens - Schiller

jonsito's picture

Una última duda.


¿Has tenido en cuenta los posibles problemas de concurrencia en acceso al canal seguro de dos aplicaciones simultáneas?

Estoy pensando en un applet basado en tu código que se pelea con el navegador que está renegociando el SSL a través del módulo pkcs11

A mi me costó Dios y ayuda conseguir que los applets java funcionaran con OpenDNIe, y creo recordar que el pliego de Inteco dice expresamente que este problema tiene que estar resuelto... aunque no indica si lo que hay que resolver es un conflicto entre dos instancias del driver o bien una del driver y otra de -por ejemplo- un pkcs11

Creo que ya no me queda nada por preguntar :-)

Juan Antonio

Gegen die Dummheit kämpfen selbst die Götter vergebens - Schiller

lfern's picture

La concurrencia ...


yo creo que es imposible. Es decir, que si otro driver renegocia el canal seguro cuando ya se tenía establecido, hay que volver a renegociarlo, y eso implica volver a escribir PIN. Habría una forma, (pero creo que no funcionaría, al menos en windows) que sería bloquear de forma exclusiva el canal. Pero en algunas pruebas en windows y con el driver oficial antiguo no había forma de obtenerlo de forma exclusiva. Lo que si tengo previsto es intentar que la renegociación se haga internamente de forma transparente, lo que, unido al sistema de reconstrucción de certificados, permitirá no tener que introducir el PIN a no ser que sea una operación de firma.

jonsito's picture

El pin no es necesario...


... para crear/renegociar el canal seguro.... doy fe :-)

Yo lo que hice en OpenDNIe es justamente lo que describes: comprobar el retorno de las operaciones, y en caso de obtener error de cifrado (ahora no recuerdo los apdu response, sorry) , abortaba la transacción, renegociaba el canal y reintentaba.

Como para obtener los aliases no hace falta ni pin ni "reconstrucción", el procedimiento era transparente al usuario, aunque evidentemente más lento debido a la renegociación del canal. Solo en casos muy raros -en las páginas web que piden la renegociación del SSL en la (re)carga de la página- tenía que pedir pin de nuevo.

Juan Antonio

Gegen die Dummheit kämpfen selbst die Götter vergebens - Schiller