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 }