Java 6 y tu dni electrónico

Posted by david

Ahora que han pasado unos meses desde el lanzamiento de Java 6 y que en casi en cualquier provincia puedes expedir un dni electrónico ya podemos empezar a pensar como hacer uso de ambos.

Dentro de nuestro dnie se encuentran varios certificados digitales para firma electrónica o autenticación, para acceder a ellos necesitamos es un lector de tarjetas que acepte nuestro documento.

Una de las nuevas incorporaciones en java 6, y de las menos publicitadas, es el soporte nativo para acceder al almacen de claves de windows. Hasta ahora teníamos que usar complejas DLLs de windows para acceder a este almacen, pero con esta nueva release es tan fácil como esto:

KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);

Como no todos en este mundo usamos ese sistema operativo, dentro de nuestro dnie también podremos encontrar los certificados dentro de una librería PKCS#11 llamada opensc-pkcs11.so que podremos usar en cualquier otro sistema. El proceso de acceder a una librería PKCS#11 es un poco más tedioso pero hay muy buenos artículos en la red que lo explican . Una aproximación rápida podría ser que tenemos que añadir esta librería como un proveedor de certificados a nuestro almacen de claves PKCS#11 y luego acceder a este para recuperar los certificados. Para añadir la librería a nuestro almacén necesitaríamos algo como esto:

String pkcs11config = "name = DNIE\nlibrary = opensc-pkcs11.so ";
InputStream confStream = new ByteArrayInputStream(pkcs11config.getBytes());

Class sunPkcs11Class = Class.forName("sun.security.pkcs11.SunPKCS11");
Constructor pkcs11Constr = sunPkcs11Class.getConstructor(InputStream.class);

Provider pkcs11Provider = (Provider) pkcs11Constr.newInstance( confStream );
Security.addProvider(pkcs11Provider);

para acceder a este almacén:

KeyStore keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, password);

Una vez cargado nuestro almacén de claves correspondiente podríamos acceder a todos los certificados que contiene a través de sus alias:

Enumeration enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {

String alias = enumeration.nextElement().toString();
Certificate[] certs = store.getCertificateChain( alias );

}

Y con esto ya tendríamos acceso a los certificados de nuestro dni electrónico y podríamos ir pensando en como darles uso.

Actualización: Si nuestros usuarios usan Mac OS X y queremos acceder al almacén de claves de este sistema tendríamos que usar el siguiente código:

KeyStore keyStore = KeyStore.getInstance("KeychainStore", "Apple");
keyStore.load(null, null);
  1. CarlosJuly 12, 2007 @ 09:53 AM
    Hay alguna forma de acceder al almacen WIndows-MY, de forma remota?? Me explico, necesito acceder desde un servidor de aplicaciones (por medio de servlets), a un DNIe, para autenticar a la persona, y necesito leer el almacen de la maquina que se conecta con la aplicacion.
  2. AntonioJuly 12, 2007 @ 12:16 PM
    Hola, Yo quería preguntar si sabríais como acceder al DNIe a través de una aplicación web. Es decir, autenticarse en un servidor remoto...como tiene hecho Caja Madrid o la DGT...algo asi. Muchas gracias
  3. DavidAugust 07, 2007 @ 06:38 PM
    La única forma que conozco de hacer esto con java es a través de un applet que se comunique con el servidor de aplicaciones. Saludos
  4. AngelOctober 03, 2007 @ 03:14 PM
    Hola David, Estoy tratando de implantar esto mismo a través de un applet y siempre me choco con el problema de la seguridad. El applet lo firmo con un certificado creado a través del keytool. El error que se muestra en la consola de la VM es java.security.AccessControlException: access denied (java.security.SecurityPermission authProvider.SunMSCAPI) apuntando a la línea KeyStore keyStore = KeyStore.getInstance("Windows-MY"); También he probado a introducir esto mismo dentro de un bloque AccessController.doPrivileged con el mismo resultado. ¿Alguna idea? Gracias por adelantado
  5. ValeOctober 08, 2007 @ 08:17 AM
    Hola. Sobre la autenticación con servlets yo estoy intentando hacer lo mismo, y el acceso al certificado se hace a traves de la HttpServletRequest en el método doGet del servlet. Se debe poner la aplicación web en modo seguro con SSL y solicitar la autenticación del cliente. Hay varios artículos que lo explican por la web, por ejemplo este lo explica bastante bien para tomcat: http://coyotevil.blogspot.com/2006/11/configurar-el-tomcat-con-certificado.html En un comentario está el código para acceder a los certificados. Saludos.
  6. PabloJanuary 17, 2008 @ 02:37 PM
    Hola David, ¿el acceso al DNIe desde windows se puede hacer con PKCS#11? El problema que tengo es que quiero ser yo quien muestre la pantalla de PIN, no utilizar la que sale desde su CSP. Me pregunto si hay algún metodo para detectar cuándo se introduce la SmartCard y en ese momento pedir el PIN y almacenarlo en el keystore del PKCS11 para que ya no lo vuelva a pedir. También me gustaría saber si hay alguna diferencia de uso entre los certificados de autenticación y firma. Muchas gracias por adelantado. Saludos, Pablo
  7. GabiJanuary 24, 2008 @ 01:25 PM
    Hola, h ehcho la prueba y desde un applet también y parece funcionar, sólo me gustaría saber si alguien puede y cómo se hace saltarse o cambiar la ventana de petición de password. Aparte de eso lo pide dos veces, pensé que quizás pasándole el password en el momento de cargar el keystore o que era porque recorria los alias del keystore y sacaba los certificados, sin embargo la petición (2 veces) del PIN es en el mismo moneto de cargar el keystore es decir keyStore.load(null, null); Me faltaría probar acceder de la segunda forma... ¿Alguien a avanzado en esto? Por cierto muy buen blog y muy interesante este artículo, podríamos abrir un foro de desarrolladores e ir comentando y ayudándonos en tods estos temas, me a encantado vuestra idea de los viernes y las ponencias, lástima que no sea de Madrid, sino intentaría prepararme algún tema y acercarme por allí. Un saludo.
  8. EvicFebruary 27, 2008 @ 02:10 PM

    A ver si ahora se ve bien...

    La segunda opción con alguna modificación funciona (más o menos) pero al menos es estándar y no depende del navegador (y cambiando el nombre de la librería ni del SO), y funciona desde jre1.5, no hace falta 1.6. Además, tu controlas la ventana de solicitud de password, pudiendo controlar que solo salga una vez.

    El problema que tengo es que no se porque razón solo me funciona una de cada dos veces (es como si se quedara en un estado malo). El código es este (para windows, para linux hay que cambiar el nombre de la libreria):

    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.security.KeyStore;
    import java.security.Security;
    
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.PasswordCallback;
    import javax.security.auth.callback.UnsupportedCallbackException;
    
    import sun.security.pkcs11.SunPKCS11;
    
    public class TestDnie implements CallbackHandler {
    
    	public void Test() {
    
    		SunPKCS11 sunpkcs11 = null;
    		try {
    			String pkcs11config = "name = DNIe\nlibrary = c:/WINDOWS/system32/UsrPkcs11.dll\nslot=1\nshowInfo=true\n";
    			InputStream confStream = new ByteArrayInputStream(pkcs11config.getBytes());
    			sunpkcs11 = new SunPKCS11(confStream);
    			Security.addProvider(sunpkcs11);
    
    			KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", sunpkcs11, new KeyStore.CallbackHandlerProtection(this));
    			KeyStore keyStore = builder.getKeyStore();
    
    			// Aqui trabajamos con el keystore de forma normal
    
    			sunpkcs11.logout();
    			Security.removeProvider(sunpkcs11.getName());
    		} catch (Exception ex) {
    			ex.printStackTrace();
    		}
    	}
    
    	public static void main(String[] args) {
    		TestDnie t = new TestDnie();
    		t.Test();
    	}
    
    	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
    		for (Callback c : callbacks) {
    			System.out.println(c.toString());
    			if (c instanceof PasswordCallback) {
    				PasswordCallback pc = (PasswordCallback) c;
    				//Poner el password del dnie
    				pc.setPassword("***********".toCharArray());
    			}
    		}
    	}
    }

    Si lo ejecutáis con -Djava.security.debug=sunpkcs11 os sacará aún más trazas (y con -Djava.security.debug=all más aún).

    No se si el problema que tengo esta en los drivers de mi lector de tarjetas o en la libreria de pkcs11 de la fnmt, pero una de cada dos veces me va bien y la otra me da esta excepcion:

    sunpkcs11: Initializing PKCS#11 library c:/WINDOWS/system32/UsrPkcs11.dll
    java.security.ProviderException: Initialization failed
    	at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:186)
    	at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:80)
    	at es.bancamarch.test.dnie.TestFirmaDnie.Sign(TestFirmaDnie.java:42)
    	at es.bancamarch.test.dnie.TestFirmaDnie.main(TestFirmaDnie.java:80)
    Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_DEVICE_ERROR
    	at sun.security.pkcs11.wrapper.PKCS11.C_GetTokenInfo(Native Method)
    	at sun.security.pkcs11.Token.<init>(Token.java:105)
    	at sun.security.pkcs11.SunPKCS11.initToken(SunPKCS11.java:566)
    	at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:180)
    	... 3 more

    Si alguien sabe el porque de esto agradeceria lo comentara.

  9. Oscar BlancoMarch 25, 2008 @ 10:17 AM
    Hola. Te explongo mi duda y a ver si es posible hacerla. Yo tengo una aplicacion web que autentifica mediante dnie, una vez dentro cada usuario puede remitir un o varios documentos que necesito firmar con su dnie. Es esto posible? como obtengo la clave privada para firmalos? Gracias.
  10. oscarApril 19, 2008 @ 09:35 AM
    Hola, me gustaría saber si alguien ha conseguido extraer los datos públicos del dnie (nombre, apellidos, NIF) sin necesidad de introducir el PIN. Sé que algunos fabricantes de lectores de tarjetas como C3PO y cherry lo han conseguido, pero de momento están cobrando aparte dicho software. Gracias!