org.viafirma.nucleo.X509.X509Handler.java Source code

Java tutorial

Introduction

Here is the source code for org.viafirma.nucleo.X509.X509Handler.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.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);
}