Java tutorial
/* Copyright (C) 2007 Flix Garca Borrego (borrego at gmail.com) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package org.viafirma.nucleo.validacion; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.AccessController; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PrivilegedAction; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.viafirma.excepciones.ExcepcionErrorInterno; import org.viafirma.nucleo.Nucleo; /** * Se encarga de recuperar del keyStore por defecto del sistema los certificados * de confianza. Retorna el conjunto de certificados de confianza. * * @author Felix Garcia Borrego (borrego at gmail.com) */ public class KeyStoreLoader { /** * Logger. */ private static Log log = LogFactory.getLog(KeyStoreLoader.class); /** * Almacen de certificados utilizado por la plataforma. */ private static KeyStore ks; /** * Returns the collection of default JSSE trust anchors. Uses the following * truststore search order:<br> * * 2) <code>${java.home}/lib/security/jssecacerts</code>, <br> * 3) <code>${java.home}/lib/security/cacerts</code> <br> * * @return the collection of default JSSE trust anchors. */ public static Set<TrustAnchor> getJSSETrustAnchors() { return AccessController.doPrivileged(new PrivilegedAction<Set<TrustAnchor>>() { @SuppressWarnings("synthetic-access") public Set<TrustAnchor> run() { try { return getDefaultJavaTrustAnchorsPrivileged(); } catch (ExcepcionErrorInterno e) { // No se pueden recuperar los certificados de confianza throw new RuntimeException(e); } } }); } /** * Retorna la coleccin de certificados de confianza. * * @return * @throws ExcepcionErrorInterno */ private static Set<TrustAnchor> getDefaultJavaTrustAnchorsPrivileged() throws ExcepcionErrorInterno { try { // Recuperamos los certificados de confianza del almacen List<Certificate> certificadosDeconcianza = getKeystoreCerts(loadDefaultKeyStore()); return createTrustAnchors(certificadosDeconcianza); } catch (ExcepcionErrorInterno e) { throw e; } } /* * Retorna el Key sotore por defecto utilizado. Nota: Para localizar el key * store se busca en los ficheros: -JAVA_HOME/lib/security/jssecacerts * -JAVA_HOME/lib/security/cacerts */ private static KeyStore loadDefaultKeyStore() throws ExcepcionErrorInterno { if (ks == null) { String javaHome = System.getProperty("java.home"); File jssecacerts = new File(javaHome, "lib/security/jssecacerts".replace('/', File.separatorChar)); File cacerts = new File(javaHome, "lib/security/cacerts".replace('/', File.separatorChar)); try { ks = jssecacerts.exists() ? loadKeystore(jssecacerts, null) : loadKeystore(cacerts, null); return ks; } catch (IOException e) { throw new ExcepcionErrorInterno(e.getMessage()); } catch (NoSuchAlgorithmException e) { throw new ExcepcionErrorInterno(e.getMessage()); } catch (CertificateException e) { throw new ExcepcionErrorInterno(e.getMessage()); } } else { return ks; } } /** * Load keystore of the default type from the specified file, using the * specified password. * * @param file * file to read keystore from * @param passwd * keystore password * * @return keystore loaded from the file * @throws IOException * if there is an I/O or format problem with the keystore data * @throws CertificateException * if any of the certificates in the keystore could not be * loaded * @throws NoSuchAlgorithmException * if the algorithm used to check the integrity of the keystore * cannot be found */ private static KeyStore loadKeystore(File file, char[] passwd) throws IOException, CertificateException, NoSuchAlgorithmException { try { return loadKeystore(file, passwd, KeyStore.getDefaultType()); } catch (KeyStoreException e) { throw new RuntimeException("FATAL: keystore type \"" + KeyStore.getDefaultType() + "\" not supported"); } } /** * Load keystore of the specified type from the specified file, using the * specified password. * * @param file * file to read keystore from * @param type * keystore type * @param passwd * keystore password * * @return keystore loaded from the file * @throws IOException * if there is an I/O or format problem with the keystore data * @throws CertificateException * if any of the certificates in the keystore could not be * loaded * @throws NoSuchAlgorithmException * if the algorithm used to check the integrity of the keystore * cannot be found */ private static KeyStore loadKeystore(File file, char[] passwd, String type) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { log.info("Utilizando el KeyStore: " + file.getPath()); FileInputStream fis = new FileInputStream(file); KeyStore ks = KeyStore.getInstance(type); ks.load(fis, passwd); fis.close(); return ks; } /** * Retora el listado de certificados almacenados dentro del keystore * indicado. * * @param ks * the keystore * @return list of certificates kept in the keystore */ @SuppressWarnings("unchecked") private static List<Certificate> getKeystoreCerts(KeyStore ks) { List<Certificate> list = new ArrayList<Certificate>(); StringBuffer certificadosIgnorados = new StringBuffer(); try { Enumeration aliases = ks.aliases(); while (aliases.hasMoreElements()) { String alias = (String) aliases.nextElement(); // FILTRA LOS CERTIFICADOS QUE NO QUEREMOS O NO SON NECESARIOS. if (!alias.contains(Nucleo.IDENTIFICADOR_CERTIFICADO_VIAFIRMA_KEYSTORE)) { certificadosIgnorados.append(alias + ","); } else { if (!(ks.isCertificateEntry(alias))) continue; Certificate c = ks.getCertificate(alias); if (c instanceof X509Certificate) { log.info("Detectado certificado de confianza: Alias=" + alias + ", DN=" + ((X509Certificate) c).getSubjectDN()); } list.add(c); } } log.debug("Certificados ignorados :" + certificadosIgnorados); return list; } catch (KeyStoreException e) { throw new RuntimeException("Keystore not loaded", e); } } /** * Generate a collection of trust anchors representing specified * certificates. * * @param certs * certificates * @return trust anchors representing the certificates */ @SuppressWarnings("unchecked") private static Set<TrustAnchor> createTrustAnchors(Collection certs) { return createTrustAnchors(certs, null); } /** * Generate a collection of trust anchors representing specified * certificates, using specified nameConstraints. * * @param certs * certificates * @param nameConstraints * a byte array containing the ASN.1 DER encoding of a * NameConstraints extension to be used for checking name * constraints. * @return trust anchors representing the certificates */ @SuppressWarnings("unchecked") private static Set<TrustAnchor> createTrustAnchors(Collection certs, byte[] nameConstraints) { Set<TrustAnchor> anchors = new HashSet<TrustAnchor>(certs.size()); for (Iterator i = certs.iterator(); i.hasNext();) { Certificate cert = (Certificate) i.next(); if (cert instanceof X509Certificate) { anchors.add(new TrustAnchor((X509Certificate) cert, nameConstraints)); } } return anchors; } /** * Recupera el clave privada del certificado con el alias indicado. * * @param alias * @param password * @return * @throws ExcepcionErrorInterno */ public static PrivateKey getPrivateKey(String alias, String password) throws ExcepcionErrorInterno { try { return (PrivateKey) loadDefaultKeyStore().getKey(alias, password == null ? null : password.toCharArray()); } catch (UnrecoverableKeyException e) { throw new ExcepcionErrorInterno(e.getMessage(), e); } catch (KeyStoreException e) { throw new ExcepcionErrorInterno(e.getMessage(), e); } catch (NoSuchAlgorithmException e) { throw new ExcepcionErrorInterno(e.getMessage(), e); } catch (ExcepcionErrorInterno e) { throw e; } catch (ClassCastException e) { throw new ExcepcionErrorInterno( "El alias indicado no pertenece a un certificado que tenga una clave privada almacenada."); } } /** * Recupera el certificado con el alias indicado. * * @param alias * Alias del certificado deseado. * @return Certificado con el alias indicado * @throws ExcepcionErrorInterno */ public static X509Certificate getCertificate(String alias) throws ExcepcionErrorInterno { try { return (X509Certificate) loadDefaultKeyStore().getCertificate(alias); } catch (KeyStoreException e) { throw new ExcepcionErrorInterno(e.getMessage()); } catch (ExcepcionErrorInterno e) { throw e; } catch (ClassCastException e) { throw new ExcepcionErrorInterno("El alias indicado no pertenece a un certificado X509."); } } /** * Recupera el camino completo para el certificado con el alias indicado. * * @param alias * Alias del certificado del que deseamos recuperar el alias. * @return ChainCertificate del alias indicado * @throws ExcepcionErrorInterno */ public static List<Certificate> getCertificateChain(String alias) throws ExcepcionErrorInterno { try { return Arrays.asList(loadDefaultKeyStore().getCertificateChain(alias)); } catch (KeyStoreException e) { throw new ExcepcionErrorInterno(e.getMessage()); } catch (ExcepcionErrorInterno e) { throw e; } } }