org.viafirma.nucleo.validacion.KeyStoreLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.viafirma.nucleo.validacion.KeyStoreLoader.java

Source

/* 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;
        }
    }

}