1 /** 2 * LICENCIA LGPL: 3 * 4 * Esta librería es Software Libre; Usted puede redistribuirla y/o modificarla 5 * bajo los términos de la GNU Lesser General Public License (LGPL) tal y como 6 * ha sido publicada por la Free Software Foundation; o bien la versión 2.1 de 7 * la Licencia, o (a su elección) cualquier versión posterior. 8 * 9 * Esta librería se distribuye con la esperanza de que sea útil, pero SIN 10 * NINGUNA GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o 11 * ADECUACIÓN A UN PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public 12 * License (LGPL) para más detalles 13 * 14 * Usted debe recibir una copia de la GNU Lesser General Public License (LGPL) 15 * junto con esta librería; si no es así, escriba a la Free Software Foundation 16 * Inc. 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte 17 * <http://www.gnu.org/licenses/>. 18 * 19 * Copyright 2011 Agencia de Tecnología y Certificación Electrónica 20 */ 21 package es.accv.arangi.certificate; 22 23 import java.io.File; 24 import java.io.FileNotFoundException; 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.security.cert.X509Certificate; 28 import java.util.ArrayList; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.Properties; 32 import java.util.Map.Entry; 33 34 import org.apache.log4j.Logger; 35 36 import es.accv.arangi.base.ArangiObject; 37 import es.accv.arangi.base.certificate.Certificate; 38 import es.accv.arangi.base.certificate.validation.CAList; 39 import es.accv.arangi.base.certificate.validation.ValidateCertificate; 40 import es.accv.arangi.base.exception.certificate.CertificateCANotFoundException; 41 import es.accv.arangi.base.exception.certificate.NormalizeCertificateException; 42 import es.accv.arangi.base.util.Util; 43 44 /** 45 * Los diferentes certificados que implementan la clase es.accv.arangi.base.certificate 46 * registran sus políticas en esta clase, de forma que cuando se llama al método 47 * getInstance pasándole un certificado x.509v3 la clase devolverá un objeto que se adecúe 48 * a la política del certificado pasado.<br><br> 49 * 50 * Ejemplo de uso: <br><br> 51 * 52 * <code> 53 * X509Certificate x509Certificate = Util.getCertificate(new File ("/certificates/cert.cer"));<br> 54 * ValidateCertificate cert = CertificateFactory.getInstance(x509Certificate);<br> 55 * if (cert instanceof CertificadoCiudadano) {<br> 56 * CertificadoCiudadano cCiudadano = (CertificadoCiudadano) cert;<br> 57 * System.out.println ("Nombre ciudadano: " + cCiudadano.getName());<br> 58 * } 59 * </code><br><br> 60 * 61 * En ciertas situaciones se quiere trabajar con certificados de algún otro prestador y sería 62 * deseable que las llamadas al método getInstance() pudiesen devolver clases que tratasen la 63 * información contenida en estos certificados. Los pasos a seguir son los siguientes:<br><br> 64 * 65 * <ol> 66 * <li>Programar la clase que trate con la información del nuevo tipo de certificado. Dicha 67 * clase ha de ser hija de <code>ValidateCertificate</code> o de alguno de las clases para 68 * certificados de Arangí. Debe contener un constructor con un parámetro de tipo {@link CertificadoDesconocido CertificadoDesconocido}. 69 * Hay un ejemplo más abajo. 70 * </li> 71 * <li>Crear un paquete <code>es.accv.arangi.ext.certificate</code> y en él generar un fichero 72 * llamado <i>certificates.properties</i>. En dicho archivo de propiedades se pueden asociar 73 * políticas de certificados con las clases que se quiere utilizar para tratarlos.</li> 74 * <li>Llamar a los métodos <code>getInstance()</code> con un {@link CAList CAList} que contenga 75 * los certificados de la cadena de confianza del nuevo tipo de certificado.</li> 76 * </ol><br><br> 77 * 78 * Ejemplo de la clase:<br><br> 79 * 80 * <code> 81 * public class CertificadoPrueba extends CertificadoPersona {<br><br> 82 * 83 * public CertificadoPrueba (CertificadoDesconocido certificado) throws CertificateCANotFoundException, NormalizeCertificateException {<br> 84 * super(certificado.toX509Certificate(), certificado.getCertificationChainAsCAList());<br> 85 * }<br> 86 * ... 87 * </code><br><br> 88 * 89 * Ejemplo de propiedad:<br><br> 90 * 91 * 1.3.6.1.4.1.17345.10.1=es.accv.arangi.ext.certificate.CertificadoPrueba 92 * 93 * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a> 94 */ 95 public class CertificateFactory { 96 97 /** 98 * Constante con el path dentro del classpath al fichero de propiedades con la 99 * información de certificados que no existen en Arangí. 100 */ 101 public static final String EXTRA_CERTIFICATES_PROPERTIES_PATH = "es/accv/arangi/ext/certificate/certificates.properties"; 102 103 /* 104 * Logger de la clase 105 */ 106 static Logger logger = Logger.getLogger(CertificateFactory.class); 107 108 /* 109 * Lista que contiene todos los certificados registrados 110 */ 111 private static List lCertificateTypes = new ArrayList (); 112 113 // Carga la lista de certificados y tipos 114 static { 115 //-- Por si acaso se adelanta al objeto ArangiObject, insertar el proveedor 116 Util.setProvider (ArangiObject.CRYPTOGRAPHIC_PROVIDER_NAME, ArangiObject.CRYPTOGRAPHIC_PROVIDER); 117 118 //-- Añadir el certificado de ciudadano a la lista de tratables por la factory 119 CertificateFactory.addCertificateTypes(CertificadoCiudadano.getBasePolicies(), CertificadoCiudadano.class); 120 121 //-- Añadir el certificado de empleado público a la lista de tratables por la factory 122 CertificateFactory.addCertificateTypes(CertificadoEmpleadoPublico.getBasePolicies(), CertificadoEmpleadoPublico.class); 123 124 //-- Añadir el certificado de pertenencia a empresa a la lista de tratables por la factory 125 CertificateFactory.addCertificateTypes(CertificadoPertenenciaEmpresa.getBasePolicies(), CertificadoPertenenciaEmpresa.class); 126 127 //-- Añadir el certificado de seudónimo a la lista de tratables por la factory 128 CertificateFactory.addCertificateTypes(CertificadoSeudonimo.getBasePolicies(), CertificadoSeudonimo.class); 129 130 //-- Añadir el certificado de entidad a la lista de tratables por la factory 131 CertificateFactory.addCertificateTypes(CertificadoEntidad.getBasePolicies(), CertificadoEntidad.class); 132 133 //-- Añadir el certificado de representante a la lista de tratables por la factory 134 CertificateFactory.addCertificateTypes(CertificadoRepresentante.getBasePolicies(), CertificadoRepresentante.class); 135 136 //-- Añadir el certificado de aplicacion a la lista de tratables por la factory 137 CertificateFactory.addCertificateTypes(CertificadoAplicacion.getBasePolicies(), CertificadoAplicacion.class); 138 139 //-- Añadir el certificado de sede electrónica a la lista de tratables por la factory 140 CertificateFactory.addCertificateTypes(CertificadoSede.getBasePolicies(), CertificadoSede.class); 141 142 //-- Añadir el certificado de sello de órgano a la lista de tratables por la factory 143 CertificateFactory.addCertificateTypes(CertificadoSello.getBasePolicies(), CertificadoSello.class); 144 145 //-- Añadir el certificado del DNIe a la lista de tratables por la factory 146 CertificateFactory.addCertificateTypes(CertificadoDNIe.getBasePolicies(), CertificadoDNIe.class); 147 } 148 149 /** 150 * Método al que llamarán los distintos certificados para registrarse 151 * 152 * @param policies Políticas de los certificados 153 * @param certificateClass Clase del certificado (ha de ser una subclase de es.accv.arangi.base.certificate.Certificate) 154 */ 155 public static void addCertificateTypes (String [] policies, Class certificateClass) { 156 logger.debug ("[CertificateFactory.addCertificateType]::Registrando políticas " + policies + " para la clase " + certificateClass); 157 158 //-- La clase debe ser hija de ValidateCertificate 159 if (certificateClass.isAssignableFrom(ValidateCertificate.class)) { 160 logger.info ("[CertificateFactory.addCertificateType]::La clase " + certificateClass + " no es una subclase de es.accv.arangi.base.certificate.Certificate"); 161 return; 162 } 163 164 //-- Añadir elementos a la lista 165 for (int i = 0; i < policies.length; i++) { 166 lCertificateTypes.add(new CertificateFactory().new CertificateElement (policies[i], certificateClass)); 167 } 168 } 169 170 /** 171 * Obtiene una instancia certificado en base a la política del certificado que 172 * se le pasa como parámetro. Sólo busca las clases asociadas a los tipos de 173 * certificado definidos en la librería Arangí. 174 * 175 * @param certificateFile Fichero que contiene un certificado en formato X.509v3 176 * @return Instancia de certificado 177 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 178 * proveedor criptográfico de Arangi 179 * @throws FileNotFoundException El fichero no existe 180 */ 181 public static ValidateCertificate getInstance (File certificateFile) throws NormalizeCertificateException, FileNotFoundException { 182 //-- obtener un certificate 183 Certificate certificate = new Certificate (certificateFile); 184 185 //-- Llamar a getInstance 186 return getInstance(certificate); 187 } 188 189 /** 190 * Obtiene una instancia certificado en base a la política del certificado que 191 * se le pasa como parámetro. Sólo busca las clases asociadas a los tipos de 192 * certificado definidos en la librería Arangí. 193 * 194 * @param isCertificate Stream de lectua a un certificado en formato X.509v3 195 * @return Instancia de certificado 196 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 197 * proveedor criptográfico de Arangi 198 */ 199 public static ValidateCertificate getInstance (InputStream isCertificate) throws NormalizeCertificateException { 200 //-- obtener un certificate 201 Certificate certificate = new Certificate (isCertificate); 202 203 //-- Llamar a getInstance 204 return getInstance(certificate); 205 } 206 207 /** 208 * Obtiene una instancia certificado en base a la política del certificado que 209 * se le pasa como parámetro. Sólo busca las clases asociadas a los tipos de 210 * certificado definidos en la librería Arangí. 211 * 212 * @param bCertificate Array de bytes de un certificado en formato X.509v3 213 * @return Instancia de certificado 214 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 215 * proveedor criptográfico de Arangi 216 */ 217 public static ValidateCertificate getInstance (byte[] bCertificate) throws NormalizeCertificateException { 218 //-- obtener un certificate 219 Certificate certificate = new Certificate (bCertificate); 220 221 //-- Llamar a getInstance 222 return getInstance(certificate); 223 } 224 225 /** 226 * Obtiene una instancia certificado en base a la política del certificado que 227 * se le pasa como parámetro. Sólo busca las clases asociadas a los tipos de 228 * certificado definidos en la librería Arangí. 229 * 230 * @param x509Certificate Certificado en formato X.509 231 * @return Instancia de certificado 232 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 233 * proveedor criptográfico de Arangi 234 */ 235 public static ValidateCertificate getInstance (X509Certificate x509Certificate) throws NormalizeCertificateException { 236 //-- Obtener la política del certificado 237 Certificate certificate = new Certificate (x509Certificate); 238 239 //-- Llamar a getInstance 240 return getInstance(certificate); 241 } 242 243 /** 244 * Obtiene una instancia certificado en base a la política del certificado que 245 * se le pasa como parámetro. Sólo busca las clases asociadas a los tipos de 246 * certificado definidos en la librería Arangí. 247 * 248 * @param certificate Certificado en formato X.509 249 * @return Instancia de certificado 250 */ 251 public static ValidateCertificate getInstance (Certificate certificate) { 252 logger.debug ("[CertificateFactory.getInstance]::Obteniendo un certificado para \n" + (certificate==null?"null":certificate.getCommonName())); 253 254 //-- Obtener la política del certificado 255 List<String> policies = certificate.getPolicyOIDs(); 256 if (policies.isEmpty()) { 257 return null; 258 } 259 260 //-- Iterar sobre la lista 261 for (Iterator iterator = lCertificateTypes.iterator(); iterator.hasNext();) { 262 CertificateFactory.CertificateElement element = (CertificateFactory.CertificateElement) iterator.next(); 263 for (String policy : policies) { 264 if (policy.startsWith(element.getPolicy())) { 265 logger.debug ("[CertificateFactory.getInstance]::Obtenido un certificado para la política " + policy); 266 try { 267 return (ValidateCertificate) element.getCertificateClass().getConstructor(new Class[] { X509Certificate.class }).newInstance(new Object[] { certificate.toX509Certificate() }); 268 } catch (Exception e) { 269 logger.info("[CertificateFactory.getInstance]::Error obteniendo una instancia de la clase " + element.getCertificateClass(), e); 270 } 271 } 272 } 273 } 274 275 logger.debug("[CertificateFactory.getInstance]::No se ha podido instanciar ninguna clase para las políticas " + policies); 276 return null; 277 278 } 279 280 /** 281 * Obtiene una instancia certificado en base a la política del certificado que 282 * se le pasa como parámetro. Busca en primer lugar en las clases asociadas a 283 * los tipos de certificado definidos en la librería Arangí. Si no encuentra 284 * una clase que trate con el tipo de certificado buscará según lo indicado 285 * en el javadoc inicial de esta clase. 286 * 287 * @param certificateFile Fichero que contiene un certificado en formato X.509v3 288 * @param caList Lista de certificados de CA 289 * @return Instancia de certificado 290 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 291 * proveedor criptográfico de Arangi 292 * @throws FileNotFoundException El fichero no existe 293 */ 294 public static ValidateCertificate getInstance (File certificateFile, CAList caList) throws NormalizeCertificateException, FileNotFoundException { 295 //-- obtener un certificate 296 Certificate certificate = new Certificate (certificateFile); 297 298 //-- Llamar a getInstance 299 return getInstance(certificate, caList); 300 } 301 302 /** 303 * Obtiene una instancia certificado en base a la política del certificado que 304 * se le pasa como parámetro. Busca en primer lugar en las clases asociadas a 305 * los tipos de certificado definidos en la librería Arangí. Si no encuentra 306 * una clase que trate con el tipo de certificado buscará según lo indicado 307 * en el javadoc inicial de esta clase. 308 * 309 * @param isCertificate Stream de lectura a un certificado en formato X.509v3 310 * @param caList Lista de certificados de CA 311 * @return Instancia de certificado 312 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 313 * proveedor criptográfico de Arangi 314 */ 315 public static ValidateCertificate getInstance (InputStream isCertificate, CAList caList) throws NormalizeCertificateException { 316 //-- obtener un certificate 317 Certificate certificate = new Certificate (isCertificate); 318 319 //-- Llamar a getInstance 320 return getInstance(certificate, caList); 321 } 322 323 /** 324 * Obtiene una instancia certificado en base a la política del certificado que 325 * se le pasa como parámetro. Busca en primer lugar en las clases asociadas a 326 * los tipos de certificado definidos en la librería Arangí. Si no encuentra 327 * una clase que trate con el tipo de certificado buscará según lo indicado 328 * en el javadoc inicial de esta clase. 329 * 330 * @param bCertificate Array de bytes de un certificado en formato X.509v3 331 * @param caList Lista de certificados de CA 332 * @return Instancia de certificado 333 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 334 * proveedor criptográfico de Arangi 335 */ 336 public static ValidateCertificate getInstance (byte[] bCertificate, CAList caList) throws NormalizeCertificateException { 337 //-- obtener un certificate 338 Certificate certificate = new Certificate (bCertificate); 339 340 //-- Llamar a getInstance 341 return getInstance(certificate, caList); 342 } 343 344 /** 345 * Obtiene una instancia certificado en base a la política del certificado que 346 * se le pasa como parámetro. Busca en primer lugar en las clases asociadas a 347 * los tipos de certificado definidos en la librería Arangí. Si no encuentra 348 * una clase que trate con el tipo de certificado buscará según lo indicado 349 * en el javadoc inicial de esta clase. 350 * 351 * @param x509Certificate Certificado en formato X.509 352 * @param caList Lista de certificados de CA 353 * @return Instancia de certificado 354 * @throws NormalizeCertificateException El certificado no puede ser normalizado al formato del 355 * proveedor criptográfico de Arangi 356 */ 357 public static ValidateCertificate getInstance (X509Certificate x509Certificate, CAList caList) throws NormalizeCertificateException { 358 //-- Obtener la política del certificado 359 Certificate certificate = new Certificate (x509Certificate); 360 361 //-- Llamar a getInstance 362 return getInstance(certificate, caList); 363 } 364 365 /** 366 * Obtiene una instancia certificado en base a la política del certificado que 367 * se le pasa como parámetro. Busca en primer lugar en las clases asociadas a 368 * los tipos de certificado definidos en la librería Arangí. Si no encuentra 369 * una clase que trate con el tipo de certificado buscará según lo indicado 370 * en el javadoc inicial de esta clase. 371 * 372 * @param certificate Certificado en formato X.509 373 * @param caList Lista de certificados de CA 374 * @return Instancia de certificado 375 */ 376 public static ValidateCertificate getInstance (Certificate certificate, CAList caList) { 377 logger.debug ("[CertificateFactory.getInstance]::Obteniendo un certificado para \n" + (certificate==null?"null":certificate.getCommonName())); 378 379 //-- Obtener un certificado de los tratados por Arangí 380 ValidateCertificate validateCertificate = getInstance(certificate); 381 if (validateCertificate != null) { 382 logger.debug("[CertificateFactory.getInstance]::El certificado pasado es de los tratados por Arangí"); 383 return validateCertificate; 384 } 385 386 //-- El certificado no es de los tratados por la ACCV. Lo inicializamos como un 387 //-- certificado desconocido. 388 try { 389 validateCertificate = new CertificadoDesconocido(certificate.toX509Certificate(), caList); 390 } catch (CertificateCANotFoundException e) { 391 logger.debug("[CertificateFactory.getInstance]::El certificado pasado no pertenece a ninguna de las CAs de CAList"); 392 return null; 393 } catch (NormalizeCertificateException e) { 394 //-- No se dará porque ya venía de un Certificate de Arangí 395 return null; 396 } 397 398 //-- Mediante introspección intentar obtener un objeto certificado de un tipo 399 //-- concreto. Para ello se busca un fichero de propiedades que relaciona el 400 //-- OID de la política con la clase que tratará ese tipo de ficheros. 401 Properties properties = null; 402 try { 403 InputStream is = new CertificateFactory().getClass().getClassLoader().getResourceAsStream(EXTRA_CERTIFICATES_PROPERTIES_PATH); 404 if (is != null) { 405 properties = new Properties (); 406 properties.load(is); 407 } 408 } catch (IOException e) { 409 // No existe el fichero. 410 logger.debug("[CertificateFactory.getInstance]::No existe o no se puede leer el fichero de propiedades en " + EXTRA_CERTIFICATES_PROPERTIES_PATH); 411 } 412 413 if (properties != null) { 414 List<String> policies = certificate.getPolicyOIDs(); 415 logger.debug("[CertificateFactory.getInstance]::Los OIDs de la política del certificado es: " + policies); 416 if (!policies.isEmpty()) { 417 for (Iterator<Entry<Object, Object>> iterator = properties.entrySet().iterator(); iterator.hasNext();) { 418 Entry<Object, Object> entry = iterator.next(); 419 for(String policy : policies) { 420 if (policy.startsWith((String)entry.getKey())) { 421 String className = (String)entry.getValue(); 422 try { 423 validateCertificate = (ValidateCertificate) Class.forName(className).getConstructor(new Class[] { CertificadoDesconocido.class }).newInstance(new Object[] { validateCertificate }); 424 } catch (Exception e) { 425 logger.info("[CertificateFactory.getInstance]::No ha sido posible inicializar un objeto de la clase " + className, e); 426 } 427 logger.debug("[CertificateFactory.getInstance]::El certificado pasado tiene una clase externa que puede tratarlo pero no puede ser instanciada"); 428 } 429 } 430 } 431 } 432 } 433 434 logger.debug ("[CertificateFactory.getInstance]::El certificado pasado no pertenece a los tratados por Arangí pero si a una de las CAs de CAList"); 435 return validateCertificate; 436 437 } 438 439 /* 440 * Clase que representa cada elemento de la lista de certificados 441 */ 442 private class CertificateElement { 443 444 String policy; 445 Class certificateClass; 446 447 public CertificateElement(String policy, Class certificateClass) { 448 super(); 449 this.policy = policy; 450 this.certificateClass = certificateClass; 451 } 452 453 public String getPolicy() { 454 return policy; 455 } 456 457 public void setPolicy(String policy) { 458 this.policy = policy; 459 } 460 461 public Class getCertificateClass() { 462 return certificateClass; 463 } 464 465 public void setCertificateClass(Class certificateClass) { 466 this.certificateClass = certificateClass; 467 } 468 469 470 } 471 472 }