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.X509; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.security.auth.x500.X500Principal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.DERApplicationSpecific; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DERInteger; import org.bouncycastle.asn1.DERObject; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERPrintableString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.DERT61String; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.DERVisibleString; import org.bouncycastle.openssl.PEMReader; import org.bouncycastle.openssl.PEMWriter; import org.viafirma.excepciones.CodigoError; import org.viafirma.excepciones.ExcepcionErrorInterno; /** * Manejador de certificados X509. * * @author Felix Garcia Borrego (borrego at gmail.com) */ public class X509Handler { /** * Parsea un certificado X509 para extraer todos sus oids * * @param certificadoX509 * @return */ public Map<String, String> readPropertiesOid(X509Certificate certificadoX509) { Map<String, String> propiedadesOid = new HashMap<String, String>(); // obtengo los Oids Set<String> oids = certificadoX509.getNonCriticalExtensionOIDs(); if (oids != null) { // iteramos sobre los Oids // TODO ( este es el mecanismo para FNMT) for (String oid : oids) { try { ASN1InputStream aIn = new ASN1InputStream( new ByteArrayInputStream(certificadoX509.getExtensionValue(oid))); ASN1OctetString extValue = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(new ByteArrayInputStream(extValue.getOctets())); @SuppressWarnings("unused") DERObject extensionType = aIn.readObject(); // System.out.println("oid= "+ oid + // ", valor= "+ASN1Dump.dumpAsString(extValue) // +"\n-\ntipo "+ASN1Dump.dumpAsString(extensionType)); readPropiedadesOid(oid, extValue, propiedadesOid); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // retornamos el conjunto de oids recuperados. return propiedadesOid; } /** * Navega sobre los campos ASN.1 del certificado recuperando los pares valor * oid= valor * * @param extensionType * @param propiedadesOid */ @SuppressWarnings("unchecked") public void readPropiedadesOid(String oidActual, DERObject extension, Map<String, String> propiedadesOid) { if (extension instanceof DERSequence) { // tengo un objeto de tipo secuence. DERSequence secuence = (DERSequence) extension; Enumeration enumObjetos = secuence.getObjects(); String oidUtilizadoNodo = oidActual; while (enumObjetos.hasMoreElements()) { DERObject objeto = (DERObject) enumObjetos.nextElement(); // si este objeto fuese un identificador quiere decir que el // siguiente seria un objeto que queremos guardar if (objeto instanceof DERObjectIdentifier) { DERObjectIdentifier objetoID = (DERObjectIdentifier) objeto; // este es el oid utilizado para los nodos que estan por // debajo del actual oidUtilizadoNodo = objetoID.getId(); } else { readPropiedadesOid(oidUtilizadoNodo, objeto, propiedadesOid); } } } else if (extension instanceof DERObjectIdentifier) { // el objeto es un identificador. DERObjectIdentifier objetoID = (DERObjectIdentifier) extension; String oid = objetoID.getId(); System.out.println("Valor perdido " + oid); } else if (extension instanceof DERIA5String) { // hemos localizado un par id-valor String valor = ((DERIA5String) extension).getString(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERVisibleString) { // hemos localizado un par id-valor String valor = ((DERVisibleString) extension).getString(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERPrintableString) { // hemos localizado un par id-valor String valor = ((DERPrintableString) extension).getString(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERBitString) { String valor = "" + ((DERBitString) extension).getPadBits(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERSet) { Enumeration enumSet = ((DERSet) extension).getObjects(); while (enumSet.hasMoreElements()) { readPropiedadesOid(oidActual, (DERObject) enumSet.nextElement(), propiedadesOid); } } else if (extension instanceof DERTaggedObject) { DERTaggedObject derTag = (DERTaggedObject) extension; if ((derTag.isExplicit() && !derTag.isEmpty()) || derTag.getObject() instanceof DERSequence) { DERObject nestedObj = derTag.getObject(); readPropiedadesOid(oidActual, nestedObj, propiedadesOid); } else { DEROctetString derOct = (DEROctetString) derTag.getObject(); readPropiedadesOid(oidActual, derOct, propiedadesOid); } } /* * else if(extension instanceof DERTaggedObject){ DERTaggedObject * tagged=((DERTaggedObject)extension); int tagNo=tagged.getTagNo(); * readPropiedadesOid(oidActual,tagged.getObject(),propiedadesOid); * * * //propiedadesOid.put(oidActual,valor); } */else if (extension instanceof DEROctetString) { DEROctetString oct = (DEROctetString) extension; // ASN1InputStream aIn= new ASN1InputStream(oct.getOctets()); ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(oct.getOctets())); try { DERObject extensionObj = aIn.readObject(); readPropiedadesOid(oidActual, extensionObj, propiedadesOid); } catch (IOException e) { // si no es un nuevo objeto codificado quizas sea un string(ej : // las crls se recuperan asi) propiedadesOid.put(oidActual, new String(oct.getOctets())); } catch (IllegalStateException e) { // Problema extrao detectado con los certificados corruptos. // OID: 2.5.29.14 :java.lang.IllegalStateException: DER length // more than 4 bytes // DER length more than 4 bytes log.warn(e.getMessage()); } catch (Exception e) { // Problema extrao detectado con los certificados corruptos. // OID: 2.5.29.14 :java.lang.IllegalStateException: DER length // more than 4 bytes e.printStackTrace(); } } else if (extension instanceof DERInteger) { String valor = "" + ((DERInteger) extension).getValue().longValue(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERT61String) { String valor = ((DERT61String) extension).getString(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERUTF8String) { String valor = ((DERUTF8String) extension).getString(); propiedadesOid.put(oidActual, valor); } else if (extension instanceof DERApplicationSpecific) { DERApplicationSpecific temp = (DERApplicationSpecific) extension; String valor = new String(temp.getContents()); propiedadesOid.put(oidActual, valor); } else { log.warn("Tipo de estructura ANS1 no soportada: " + extension); } // log.debug("tipo de dato en ASN1 parsear estructura : "+extension); } /** * Serializa el certificado indicado. * * @param certificado * @param out * @throws IOException */ public void writerX509(X509Certificate certificado, java.io.OutputStream out) throws IOException { PEMWriter writer = new PEMWriter(new OutputStreamWriter(out)); try { writer.writeObject(certificado); writer.close(); } catch (IOException e) { throw e; } } /** * Recupera el certificado en formato PEM. * * @param certificado * @param out * @throws IOException */ public X509Certificate loadX509(InputStream datosFormatoPEM) throws IOException { X509Certificate certificado; PEMReader reader = new PEMReader(new InputStreamReader(datosFormatoPEM)); try { certificado = (X509Certificate) reader.readObject(); reader.close(); return certificado; } catch (IOException e) { throw e; } } private static X509Handler singleton; public static X509Handler getCurrentInstance() { if (singleton == null) { singleton = new X509Handler(); } return singleton; } private X509Handler() { super(); } /** * Retorna la cadena de confianza asociada al certificado indicado * utilizando como base el conjunto de certificados de confianza de la * plataforma. * * @param certificadosConfianza conjunto de certificados de confianza que se pueden utilizar. * @param certificadoInicial Certificado inicio del camino de validacin * @return * @throws ExcepcionErrorInterno Alguno de los certificados asociados no es de confianza. */ public X509Certificate[] obtenerPath(X509Certificate certificadoInicial, Set<TrustAnchor> certificadosConfianza) throws ExcepcionErrorInterno { Vector<X509Certificate> vector = new Vector<X509Certificate>(); // el primer elemento es el certificado inicial. vector.add(0, certificadoInicial); // Recupera el certificado emisor del certificado indicado X509Certificate certificadoEmisor = getEmisor(certificadoInicial, certificadosConfianza); for (int i = 1; certificadoEmisor != null; i++) { vector.add(i, certificadoEmisor); certificadoEmisor = getEmisor(certificadoEmisor, certificadosConfianza); } return vector.toArray(new X509Certificate[0]); } /** * Retorna el certificado emisor del certificado actual * * @param certificado * @param certificadosConfianza * @return * @throws ExcepcionErrorInterno No se ha encontrado un certificado asociado vlido. */ public X509Certificate getEmisor(X509Certificate certificado, Set<TrustAnchor> certificadosConfianza) throws ExcepcionErrorInterno { // Si no es un certificado autoemitido if (certificado.getIssuerX500Principal().getName() .equals(certificado.getSubjectX500Principal().getName())) { // El certificado esta autofirmado, no hay emisor. return null; } else { // Busco el emisor indicado. for (TrustAnchor trustAnchor : certificadosConfianza) { if (trustAnchor.getTrustedCert().getSubjectX500Principal().getName() .equals(certificado.getIssuerX500Principal().getName())) { // Encontrado el certificado de confianza. return trustAnchor.getTrustedCert(); } } } log.error("No hay ningun certificado asociado a :" + certificado.getIssuerX500Principal().getName() + " es necesaria su instalacin para poder gestionar este tpo de certificados."); throw new ExcepcionErrorInterno(CodigoError.ERROR_VALIDACION_AUTORIDAD_NO_RECONOCIDA); } private Log log = LogFactory.getLog(X509Handler.class); }