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);

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:
Si alguien sabe el porque de esto agradeceria lo comentara.