/**
* LICENCIA LGPL:
*
* Esta librería es Software Libre; Usted puede redistribuirlo y/o modificarlo
* bajo los términos de la GNU Lesser General Public License (LGPL)
* tal y como ha sido publicada por la Free Software Foundation; o
* bien la versión 2.1 de la Licencia, o (a su elección) cualquier versión posterior.
*
* Esta librería se distribuye con la esperanza de que sea útil, pero SIN NINGUNA
* GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o ADECUACIÓN A UN
* PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public License (LGPL) para más
* detalles
*
* Usted debe recibir una copia de la GNU Lesser General Public License (LGPL)
* junto con esta librería; si no es así, escriba a la Free Software Foundation Inc.
* 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte
* <http://www.gnu.org/licenses/>.
*
* Copyright 2008 Ministerio de Industria, Turismo y Comercio
*
*/
package es.mityc.firmaJava.libreria.utilidades;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Conjunto de utilidades para el tratamiento genérico de certificados.
*
* @author Ministerio de Industria, Turismo y Comercio
* @version 0.9 beta
*/
public class UtilidadCertificados {
private static final Log logger = LogFactory.getLog(UtilidadCertificados.class);
public enum Filter { SIGN_SIGNER, CRL_SIGNER, OCSP_SIGNER, TS_SIGNER};
private final static String OID_OCSP_SIGNING = "1.3.6.1.5.5.7.3.9";
private final static String OID_TS_SIGNING = "1.3.6.1.5.5.7.3.8";
/**
* Recupera los CertPath's de certificados que pueda encontrar en el listado de certificados provistos.
*
* @param certificates Listado de certificados
* @return ArrayList con los CertPath's que se han podido construir
*/
public static ArrayList<CertPath> getCertPaths(Iterable<X509Certificate> certificates) {
ArrayList<ArrayList<X509Certificate>> list = getCertPathsArray(certificates);
ArrayList<CertPath> certPaths = new ArrayList<CertPath>();
Iterator<ArrayList<X509Certificate>> itArrays = list.iterator();
while (itArrays.hasNext()) {
CertPath cp = convertCertPath(itArrays.next());
if (cp != null)
certPaths.add(cp);
}
return certPaths;
}
/**
* Recupera los CertPath's de certificados que pueda encontrar en el listado de certificados provistos.
*
* @param certificates Listado de certificados
* @return ArrayList con los CertPath's que se han podido construir
*/
public static ArrayList<ArrayList<X509Certificate>> getCertPathsArray(Iterable<X509Certificate> certificates) {
ArrayList<ArrayList<X509Certificate>> certPaths = new ArrayList<ArrayList<X509Certificate>>();
if (certificates != null) {
// Pasa todos los certificados a una lista enlazada eliminando los certificados repetidos
ArrayList<NTo1Link<X509Certificate>> list = new ArrayList<NTo1Link<X509Certificate>>();
Iterator<X509Certificate> itCerts = certificates.iterator();
while (itCerts.hasNext()) {
NTo1Link<X509Certificate> nodo = new NTo1Link<X509Certificate>(itCerts.next());
if (!list.contains(nodo))
list.add(nodo);
}
// Busca para cada certificado su relación (hijo de, padre de)
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
linkCerts(list.get(i), list.get(j));
}
}
// Busca los nodos que no tengan previos. Esos son los comienzos de una cadena
Iterator<NTo1Link<X509Certificate>> itNodos = list.iterator();
while (itNodos.hasNext()) {
NTo1Link<X509Certificate> nodo = itNodos.next();
if (nodo.getNumPrevs() == 0) {
ArrayList<X509Certificate> cp = convertCertPathArray(nodo);
if (cp != null)
certPaths.add(cp);
}
}
}
return certPaths;
}
public static ArrayList<ArrayList<X509Certificate>> filterCertPathsArrays(ArrayList<ArrayList<X509Certificate>> list, Filter filter) {
ArrayList<ArrayList<X509Certificate>> result = new ArrayList<ArrayList<X509Certificate>>();
Iterator<ArrayList<X509Certificate>> it = list.iterator();
while (it.hasNext()) {
ArrayList<X509Certificate> certs = it.next();
if ((certs != null) && (certs.size() > 0)) {
if (Filter.OCSP_SIGNER.equals(filter)) {
if (isOCSPSigning(certs.get(0)))
result.add(certs);
}
else if (Filter.TS_SIGNER.equals(filter)) {
if (isTSSigning(certs.get(0)))
result.add(certs);
}
else if (Filter.CRL_SIGNER.equals(filter)) {
if (isCRLSigning(certs.get(0)))
result.add(certs);
}
else if (Filter.SIGN_SIGNER.equals(filter)) {
if ((!isOCSPSigning(certs.get(0))) && (!isTSSigning(certs.get(0))))
result.add(certs);
}
}
}
return result;
}
private static boolean isOCSPSigning(X509Certificate cert) {
try {
List<String> list = cert.getExtendedKeyUsage();
if (list != null) {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (OID_OCSP_SIGNING.equals(it.next()))
return true;
}
}
} catch (CertificateParsingException ex) {
}
return false;
}
private static boolean isTSSigning(X509Certificate cert) {
try {
List<String> list = cert.getExtendedKeyUsage();
if (list != null) {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (OID_TS_SIGNING.equals(it.next()))
return true;
}
}
} catch (CertificateParsingException ex) {
}
return false;
}
private static boolean isCRLSigning(X509Certificate cert) {
boolean[] usage = cert.getKeyUsage();
if ((cert != null) && (usage[6]))
return true;
return false;
}
/**
* Relaciona los certificados indicados entre si (si existe alguna relación)
* @param nodo1
* @param nodo2
*
* TODOLARGO: permitir establecer políticas de severidad a la hora de buscar las relaciones entre los certificados. Estas
* políticas pueden ser por ejemplo que se compruebe que un certificado ha firmado al otro, que campos opcionales sean
* exigidos como presentes, que alguno de los certificados de las cadenas resultantes sean certificados de confianza, etc.
*/
private static void linkCerts(NTo1Link<X509Certificate> nodo1, NTo1Link<X509Certificate> nodo2) {
if (nodo1.getData().getIssuerX500Principal().equals(nodo2.getData().getSubjectX500Principal())) {
// Comprueba que el certificado padre generó al certificado hijo
try {
nodo1.getData().verify(nodo2.getData().getPublicKey());
} catch (InvalidKeyException ex) {
return;
} catch (CertificateException ex) {
return;
} catch (NoSuchAlgorithmException ex) {
return;
} catch (NoSuchProviderException ex) {
return;
} catch (SignatureException ex) {
return;
}
nodo1.setNext(nodo2);
nodo2.addPrev(nodo1);
} else if (nodo2.getData().getIssuerX500Principal().equals(nodo1.getData().getSubjectX500Principal())) {
// Comprueba que el certificado padre generó al certificado hijo
try {
nodo2.getData().verify(nodo1.getData().getPublicKey());
} catch (InvalidKeyException ex) {
return;
} catch (CertificateException ex) {
return;
} catch (NoSuchAlgorithmException ex) {
return;
} catch (NoSuchProviderException ex) {
return;
} catch (SignatureException ex) {
return;
}
nodo2.setNext(nodo1);
nodo1.addPrev(nodo2);
}
}
/**
* Convierte una sucesion de nodos enlazados en un CertPath
* @param nodo
* @return
*/
public static CertPath convertCertPath(ArrayList<X509Certificate> certs) {
CertPath cp = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
cp = cf.generateCertPath(certs);
} catch (CertificateException ex) {
logger.error("Error al intentar generar CertPaths", ex);
}
return cp;
}
/**
* Convierte una sucesion de nodos enlazados en un CertPath
* @param nodo
* @return
*/
private static ArrayList<X509Certificate> convertCertPathArray(NTo1Link<X509Certificate> nodo) {
ArrayList<X509Certificate> certs = new ArrayList<X509Certificate>();
Iterator<NTo1Link<X509Certificate>> itNodo = nodo.iterator();
while (itNodo.hasNext()) {
certs.add(itNodo.next().getData());
}
return certs;
}
}