View Javadoc

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   * 	&nbsp;&nbsp;CertificadoCiudadano cCiudadano = (CertificadoCiudadano) cert;<br>
57   * 	&nbsp;&nbsp;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 }