View Javadoc

1   package es.caib.signatura.impl;
2   
3   import java.io.ByteArrayOutputStream;
4   import java.io.File;
5   import java.io.FileFilter;
6   import java.io.FileInputStream;
7   import java.io.FileNotFoundException;
8   import java.io.FileWriter;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.io.OutputStream;
12  import java.io.PrintWriter;
13  import java.lang.reflect.Constructor;
14  import java.lang.reflect.InvocationTargetException;
15  import java.lang.reflect.Method;
16  import java.net.URL;
17  import java.security.Provider;
18  import java.security.ProviderException;
19  import java.security.Security;
20  import java.security.cert.X509Certificate;
21  import java.util.Collection;
22  import java.util.Date;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.LinkedList;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.StringTokenizer;
29  import java.util.Vector;
30  
31  import es.caib.signatura.api.ParsedCertificate;
32  import es.caib.signatura.api.SMIMEParser;
33  import es.caib.signatura.api.Signature;
34  import es.caib.signatura.api.SignatureCertNotFoundException;
35  import es.caib.signatura.api.SignatureException;
36  import es.caib.signatura.api.SignaturePrivKeyException;
37  import es.caib.signatura.api.SignatureProviderException;
38  import es.caib.signatura.api.SignatureTimestampException;
39  import es.caib.signatura.api.SignatureVerifyException;
40  import es.caib.signatura.api.Signer;
41  import es.caib.signatura.impl.ui.FirefoxSignerProfileChooser;
42  
43  /**
44   * Implementator class of the Signer interface.
45   *
46   */
47  public class CAIBSigner implements Signer {
48  	
49  	private static LinkedHashMap realSigners = null;
50  	private static LinkedHashMap remoteRealSigners = null;
51  	private SignaturaProperties properties = null;
52  	private static ClassLoaderFactory factory;
53  	private static Object lock = new Object();
54  	
55  	private SignerProviderInterface getSigner(String certificateName, String contentType)
56  		throws SignatureCertNotFoundException {
57  		
58  		boolean recognized = properties.needsRecognizedCertificate(contentType);
59  		Iterator signersIterator = realSigners.values().iterator();
60  		Iterator remoteSignersIterator = remoteRealSigners.values().iterator();
61  				
62  		LinkedList providers = new LinkedList(); //important que sigui LinkedList per a mantenir ordre
63  		LinkedList certs = new LinkedList(); //important que sigui LinkedList per a mantenir ordre
64  						
65  		while ( signersIterator.hasNext() ) {
66  			
67  			LocalSignerProviderInterface s = (LocalSignerProviderInterface)signersIterator.next();
68  							
69  			ParsedCertificate[] providerCerts;
70  						
71  			try {
72  				
73  				providerCerts = s.getCertList(recognized);
74  				
75  				for (int j = 0; j < providerCerts.length; j++) {
76  					
77  					if ( providerCerts[j].getCommonName().equals(certificateName) ) {
78  						
79  						// Si cada signer extiende de AbstractSigner, sólo se nos devolverá un certificado
80  						// con el Common Name que buscamos en cada llamada a s.getCertList, ya que se habrán 
81  						// filtrado los duplicados gracias a la lógica implementada en AbstractSigner.
82  						providers.add(s);
83  						certs.add(providerCerts[j]);
84  						break;
85  						
86  					}
87  					
88  				}
89  			
90  			// No relanzamos la excepción para no interrumpir la ejecución.
91  			// Descartamos el signer y su certificado y seguimos.
92  			} catch (SignatureCertNotFoundException e) {
93  			} catch (SignaturePrivKeyException e) {
94  			} catch (ProviderException e){
95  			}
96  							
97  		}
98  		
99  		// Si encontramos el certificado que nos piden en alguno de los proveedores remotos
100 		// (por ahora sólo el que ataca al webservice de firma del ibkey-signer) devolvemos
101 		// ese signer y obviamos, por ahora, el filtro de certificados duplicados entre signers
102 		while ( remoteSignersIterator.hasNext() ) {
103 			
104 			RemoteSignerProviderInterface s = (RemoteSignerProviderInterface)remoteSignersIterator.next();
105 							
106 			String[] providerCerts;
107 			
108 			try {
109 				
110 				providerCerts = s.getCertList(recognized);
111 				
112 				for (int j = 0; j < providerCerts.length; j++) {
113 					
114 					if ( providerCerts[j].equals(certificateName) ) {
115 						System.out.println("Seleccionado "+certificateName+" con el provider "+s.getClass().getName());
116 						return s;							
117 					}
118 					
119 				}
120 			
121 			// No relanzamos la excepción para no interrumpir la ejecución.
122 			// Descartamos el signer y su certificado y seguimos.
123 			} catch (SignatureCertNotFoundException e) {
124 			} catch (SignaturePrivKeyException e) {
125 			} catch (ProviderException e){
126 			}
127 			
128 		}
129 		
130 		if (SigDebug.isActive())
131 			SigDebug.write("CAIBSIGNER: filtrando certificados con el CN '" + certificateName + "' entre signers...");
132 		
133 		// Si hemos encontrado certificados, procedemos a mirar si hay
134 		// alguno repetido (mismo Common Name) entre los diferentes signers.
135 		// Retornamos el signer que contiene el certificado con fecha de emisión más reciente.
136 		if (certs.size() > 0) {
137 			
138 			LocalSignerProviderInterface s = null;
139 			Date validSince = null;
140 			
141 			// Recorremos los signers... MUY IMPORTANTE MANTENER EL ORDEN. SI PKCS11 primero, deve devolver PKCS11 y descartar duplicado de MSCRYPTOAPI
142 			
143 			int i=0;
144 			for(i=0;i<providers.size();i++) {
145 				
146 				LocalSignerProviderInterface key = (LocalSignerProviderInterface)providers.get(i);
147 				ParsedCertificate parsed = (ParsedCertificate)certs.get(i);
148 				
149 				if (SigDebug.isActive()) {
150 					SigDebug.write("CAIBSIGNER: comprobando signer " + key);
151 					SigDebug.write("CAIBSIGNER: Emisión: " + parsed.getValidSince().getTime());
152 				}
153 				
154 				if (validSince != null)
155 					if (SigDebug.isActive())
156 						SigDebug.write("CAIBSIGNER: Stored Emisión: " + validSince.getTime());
157 				else
158 					if (SigDebug.isActive())
159 						SigDebug.write("CAIBSIGNER: Stored Emisión: null");
160 				
161 				// Si es la primera iteración (validSince == null) o si el certificado que
162 				// tratamos tiene una fecha de emisión superior a la que hemos almacedado
163 				// nos quedamos con este signer, ya que, si hay certificados duplicados,
164 				// es el que tiene el certificado más nuevo
165 				if ( validSince == null || validSince.getTime() < parsed.getValidSince().getTime() ) {
166 					
167 					if (SigDebug.isActive()) {
168 						SigDebug.write("CAIBSIGNER: El certificado del signer " + key.getClass().getName() + " tiene una fecha de emisión más reciente que el del signer almacenado " + s.getClass().getName());
169 						SigDebug.write("CAIBSIGNER: Actualizando datos temporales");
170 					}
171 					
172 					validSince = parsed.getValidSince();
173 					s = key;
174 					
175 				} else {
176 					
177 					if (SigDebug.isActive()) {
178 						SigDebug.write("CAIBSIGNER: El certificado del signer " + key.getClass().getName() + " tiene una fecha de emisión menor o igual que el del signer almacenado " + s.getClass().getName());
179 						SigDebug.write("CAIBSIGNER: DESCARTADO...");
180 					}
181 					
182 				}
183 				
184 			}
185 			
186 			if (SigDebug.isActive())
187 				SigDebug.write("CAIBSIGNER: filtro de certificados duplicados para '" + certificateName + "' completado");
188 		
189 			// Retornamos el signer que hayamos encontrado o una excepción, según proceda.
190 			if ( s != null ) {
191 				System.out.println("Seleccionado "+certificateName+" con el provider "+s.getClass().getName());
192 				return s;
193 			
194 			} else {
195 				
196 				throw new SignatureCertNotFoundException();
197 				
198 			}
199 			
200 		} else {
201 			
202 			throw new SignatureCertNotFoundException();
203 						
204 		}
205 				
206 	}
207 
208 	public String[] getCertList(String contentType)
209 		throws SignatureCertNotFoundException, SignaturePrivKeyException {
210 		
211 		synchronized (lock) {
212 			
213 			Vector v = new Vector();
214 			Collection signersCollection = realSigners.values();   
215 			Collection remoteSignersCollection = remoteRealSigners.values();
216 			
217 			if (signersCollection != null) {
218 				
219 				Iterator signersIterator = signersCollection.iterator();
220 				
221 				if (signersIterator != null) {
222 					
223 					while ( signersIterator.hasNext() ) {
224 						
225 						LocalSignerProviderInterface s = (LocalSignerProviderInterface)signersIterator.next();   
226 													
227 						ParsedCertificate certs[];
228 						boolean recognized = properties.needsRecognizedCertificate(contentType);					
229 						
230 						try {
231 							certs = s.getCertList(recognized);                     
232 							for (int j = 0 ; j < certs.length; j++)
233 							{
234 								if (!v.contains(certs[j].getCommonName()))
235 									v.add(certs[j].getCommonName());			
236 							}
237 						} catch (SignatureCertNotFoundException e) {              
238 	//						System.err.println ("Provider "+s.getClass().getName());
239 	//						e.printStackTrace();
240 						} catch (SignaturePrivKeyException e) {
241 							e.printStackTrace();
242 						} catch(Throwable e) {	
243 							System.err.println ("CANNOT LOAD on signature provider " + s.getClass().getName());
244 							e.printStackTrace();
245 						}
246 						
247 					}
248 					
249 				}
250 				
251 			}
252 			
253 			if (remoteSignersCollection != null) {
254 				
255 				Iterator remoteSignersIterator = remoteSignersCollection.iterator();
256 				
257 				if (remoteSignersIterator != null) {
258 					
259 					while ( remoteSignersIterator.hasNext() ) {
260 						
261 						RemoteSignerProviderInterface s = (RemoteSignerProviderInterface)remoteSignersIterator.next();   
262 			
263 						String certs[];
264 						boolean recognized = properties.needsRecognizedCertificate(contentType);					
265 						
266 						try {
267 							certs = s.getCertList(recognized);                     
268 							for (int j = 0 ; j < certs.length; j++)
269 							{
270 								if (!v.contains(certs[j]))
271 									v.add(certs[j]);			
272 							}
273 						} catch (SignatureCertNotFoundException e) {              
274 		//						System.err.println ("Provider "+s.getClass().getName());
275 		//						e.printStackTrace();
276 						} catch (SignaturePrivKeyException e) {
277 							e.printStackTrace();
278 						} catch(Throwable e) {	
279 							System.err.println ("CANNOT LOAD on signature provider " + s.getClass().getName());
280 							e.printStackTrace();
281 						}
282 						
283 					}
284 					
285 				}
286 				
287 			}
288 						
289 			return (String[])v.toArray(new String[v.size()]);
290 			
291 		}
292 		
293 	}
294 
295 	public Signature sign(String fileName, String certificateName,
296 			String password, String contentType) throws IOException,
297 			SignatureException {
298 		return sign (new FileInputStream (fileName), certificateName, password, contentType);
299 	}
300 
301 	public Signature sign(InputStream contentStream, String certificateName,
302 			String password, String contentType) throws IOException,
303 			SignatureException {
304 		synchronized (lock){ 
305 			boolean recognized  = properties.needsRecognizedCertificate(contentType);
306 			boolean timeStamp = properties.needsTimeStamp(contentType);
307 			boolean rawSign = properties.needsRawSignature(contentType);
308 			SignerProviderInterface signer = getSigner (certificateName, contentType);
309 					
310 			if( password == null ) {
311 				if( SigDebug.isActive() ) SigDebug.write( "Password is null." );
312 				password = "";
313 			}
314 			if( SigDebug.isActive() ) {
315 				SigDebug.write( "CAIBSigned.sign: password is" + (password.length() == 0? "" : " not") + " empty." );
316 			}
317 			return signer.sign(contentStream, certificateName, password, contentType, recognized, timeStamp, rawSign);
318 		}
319 	}
320 
321 	public Signature sign(URL url, String certificateName, String password, String contentType) 
322 			throws IOException, SignatureException 
323 	{
324 		return sign (url.openStream(), certificateName, password, contentType);
325 	} 
326   
327   
328     /**
329      * Firma un PDF dejando la opción de si se soportan múltiples firmas o no (parámetro allowMultipleSignature).
330      */
331 	public void signPDF(InputStream contentStream, OutputStream signedStream,
332             String certificateName, String password, String contentType, String url,
333             int position, boolean allowMultipleSignature)
334         throws IOException, SignatureException {
335     	
336         synchronized (lock) { 
337         	
338             boolean recognized  = properties.needsRecognizedCertificate(contentType);
339             SignerProviderInterface signer = getSigner (certificateName, contentType);
340 
341             if ( SigDebug.isActive() ) {
342             	SigDebug.write( "Comprovacio password null." );
343             }
344             
345             if ( password == null ) {
346             	
347                 if ( SigDebug.isActive() )  
348                 	SigDebug.write( "Posem el password a cadena buida." );
349                 
350                 password = "";
351                 
352             }
353             
354             if ( SigDebug.isActive() )  {
355                 SigDebug.write( "CAIBSigned.sign: password " + (password.length() == 0? "es" : "no es") + " buit." );
356             }
357             
358             signer.signPDF(contentStream, signedStream, certificateName, password, contentType, recognized, url, position, allowMultipleSignature);
359         
360         }
361         
362     }
363     
364 	public void signPDF(InputStream pdfInputStream, OutputStream signedStream, String certificateName, String password, String contentType,  String textoAdicional, int stampOptions, float top, float left, float height, float width, float rotation, boolean allowMultipleSignature) throws IOException, SignatureException {
365         synchronized (lock) { 
366         	
367             boolean recognized  = properties.needsRecognizedCertificate(contentType);
368             SignerProviderInterface signer = getSigner (certificateName, contentType);
369 
370             if ( SigDebug.isActive() ) {
371             	SigDebug.write( "Comprovacio password null." );
372             }
373             
374             if ( password == null ) {
375             	
376                 if ( SigDebug.isActive() )  
377                 	SigDebug.write( "Posem el password a cadena buida." );
378                 
379                 password = "";
380                 
381             }
382             
383             if ( SigDebug.isActive() )  {
384                 SigDebug.write( "CAIBSigned.sign: password " + (password.length() == 0? "es" : "no es") + " buit." );
385             }
386             
387             signer.signPDF(pdfInputStream, signedStream, certificateName, password, contentType, recognized, textoAdicional, stampOptions,top, left, height,width,rotation, allowMultipleSignature);
388         
389         }		
390 	}
391 	
392 	/**
393 	 * Firma un PDF sin soporte para múltiples firmas.
394 	 */
395     public void signPDF(InputStream contentStream, OutputStream signedStream,
396             String certificateName, String password, String contentType, String url,
397             int position)
398         throws IOException, SignatureException {
399     	
400     	signPDF(contentStream,signedStream,certificateName,password,contentType,url,position,false);
401         
402     }
403     
404     /**
405      * Genera un PDF firmado, dando la opción de soportar múltiples firmas.
406      * @deprecated
407      */
408     public OutputStream signPDF(InputStream contentStream,
409 			String certificateName, String password, String contentType, String url,
410 			int position, boolean allowMultipleSignature)
411 		throws IOException, SignatureException {
412     	
413         OutputStream out = new ByteArrayOutputStream();
414         signPDF(contentStream, out, certificateName, password, contentType, url, position, allowMultipleSignature);
415         return out;
416 	
417     }
418     
419     /**
420      * Genera un PDF firmado, sin la opción de soportar múltiples firmas.
421      * @deprecated
422      */
423     public OutputStream signPDF(InputStream contentStream,
424 			String certificateName, String password, String contentType, String url,
425 			int position)
426 		throws IOException, SignatureException {
427     	
428 
429     	return signPDF(contentStream,certificateName,password,contentType,url,position,false);
430 	
431     }
432     
433     /**
434      * Compulsa de un documento PDF
435      */
436     public void certifyDigitalCopy (InputStream contentStream, OutputStream signedStream, String certificateName,
437   			String password, String contentType, String url, String localidad, float x, float y, float rotation)
438   	throws IOException, SignatureException {
439     	
440     	synchronized (lock) { 
441         	
442             boolean recognized  = properties.needsRecognizedCertificate(contentType);
443             SignerProviderInterface signer = getSigner (certificateName, contentType);
444 
445             if ( SigDebug.isActive() ) {
446             	SigDebug.write( "Comprovacio password null." );
447             }
448             
449             if ( password == null ) {
450             	
451                 if ( SigDebug.isActive() )  
452                 	SigDebug.write( "Posem el password a cadena buida." );
453                 
454                 password = "";
455                 
456             }
457             
458             if ( SigDebug.isActive() )  {
459                 SigDebug.write( "CAIBSigned.certify: password " + (password.length() == 0? "es" : "no es") + " buit." );
460             }
461             
462             signer.certifyDigitalCopy(contentStream, signedStream, certificateName, password, contentType, recognized, url, localidad, x, y, rotation,properties);
463         
464         }
465     	
466     }
467 	
468 	/*
469 	 * Función principal de verificación de firma
470 	 */
471 	public boolean verify(InputStream contentStream, Signature signatureData)
472 			throws SignatureProviderException, IOException, SignatureVerifyException 
473 	{		
474 		try{
475 			boolean isVerified = false;
476 			isVerified = signatureData.verify(contentStream);
477 			return isVerified;
478 		} catch(SignatureVerifyException e){
479 			throw e;
480 		} catch(Exception e){
481 			throw new SignatureVerifyException(e);
482 		}
483 	}
484 
485 	public  boolean verifyAPosterioriTimeStamp(InputStream contentStream, Signature signatureData)
486 	throws SignatureProviderException, IOException, SignatureVerifyException 
487 	{		
488 		boolean isVerified = false;
489 		try{
490 			isVerified = signatureData.verifyAPosterioriTimestamp(contentStream);			
491 		}catch(SignatureVerifyException e){
492 			throw e;
493 		} catch(Exception e){
494 			throw new SignatureVerifyException(e);
495 		}
496 		return isVerified;
497 	}
498 
499 	public boolean verify(String fileName, Signature signatureData)
500 			throws FileNotFoundException, SignatureProviderException, IOException, SignatureVerifyException 
501 	{
502 		return verify( new FileInputStream(fileName) ,signatureData); 
503 	}
504 
505 	public boolean verify(URL url, Signature signatureData)
506 			throws SignatureProviderException, IOException, SignatureVerifyException 
507 	{
508 		return verify( url.openStream() ,signatureData); 
509 	}
510 
511 	/*
512 	 * Funcions de verificacio a posteriori.
513 	 */
514 
515 	public boolean verifyAPosterioriTimeStamp(String fileName, Signature signatureData) 
516 			throws FileNotFoundException, SignatureProviderException, IOException, SignatureVerifyException 
517 	{
518 		return verifyAPosterioriTimeStamp(new FileInputStream(fileName), signatureData);
519 	}
520 
521 	public boolean verifyAPosterioriTimeStamp(URL url, Signature signatureData) 
522 			throws SignatureProviderException, IOException, SignatureVerifyException 
523 	{
524 		return verifyAPosterioriTimeStamp(url.openStream(), signatureData);
525 	}
526 	
527 	
528 
529 	/*
530 	 * Altres funcions.
531 	 */
532 	public void generateSMIME(InputStream document, Signature signature,
533 			OutputStream smime) throws IOException {
534 		SMIMEGenerator smimeGenerator;
535 		try {
536 			Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.common.SMIMEGeneratorImpl");
537 			smimeGenerator = (SMIMEGenerator)c.newInstance(); 
538 		}
539 		catch(Exception e){
540 			throw new RuntimeException (e); 
541 		}
542 
543 	    InputStream in = smimeGenerator.generateSMIME(document,signature); 
544 		
545 		byte b []= new byte [8192];
546 		int read;
547 		while ( (read = in.read(b)) > 0)
548 		{
549 			smime.write(b, 0, read);
550 		}
551 		in.close();
552 	}
553 	
554   
555   
556   public void generateSMIMEParalell(InputStream document, Signature[] signatures, OutputStream smime) throws IOException, SignatureException {
557   
558     try {    
559     
560       Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.common.GeneradorSMIMEParaleloImpl");    
561       GeneradorSMIMEParalelo generadorSMIMEParalelo = (GeneradorSMIMEParalelo)c.newInstance();      
562       SMIMEInputStream in = generadorSMIMEParalelo.generarSMIMEParalelo(document,signatures); 
563       
564       byte b[] = new byte[8192];
565       int read;
566       while ( (read=in.read(b) ) > 0) {
567         smime.write(b,0,read);
568       }
569       in.close();      
570     
571     }catch (IOException iox) {
572       throw iox;
573     }catch (Exception e) {
574       throw new SignatureException(e.getMessage());
575     }
576   }
577   
578   
579 
580 	public Date getCurrentDate(String certificateName, String password,
581 			String contentType) throws SignatureTimestampException,
582 			SignatureException, IOException {
583 		if( password == null ) password = "";
584 		if( SigDebug.isActive() ) {
585 			SigDebug.write( "CAIBSigned.getCurrentDate: password " + (password.length() == 0? "is" : "is not") + " null." );
586 		}
587 		boolean recognized = properties.needsRecognizedCertificate(contentType);
588 		return getSigner(certificateName, contentType).getCurrentDate(certificateName, password, recognized);
589 	}
590 	
591 	public String getAPIVersion() 
592 	{
593 		try {
594 			InputStream inputStream =  CAIBSigner.class.getResourceAsStream("version.properties");
595 		    if (inputStream==null) 
596 		    {
597 		       throw new FileNotFoundException();
598 		    }
599 		    Properties tempProperties = new Properties();
600 		    tempProperties.load(inputStream);
601 
602 		    inputStream.close();
603 		    return tempProperties.getProperty("Version");
604 		} catch (FileNotFoundException e) {
605 			throw new RuntimeException (e);
606 		} catch (IOException e) {
607 			throw new RuntimeException (e);
608 		}
609 	}
610 
611 	/**
612 	 * @deprecated
613 	 * @throws FileNotFoundException
614 	 */
615 	public CAIBSigner () throws FileNotFoundException {
616 		preInitialize();
617 		
618 		try {
619 			properties = new SignaturaProperties();
620 		
621 		} catch (Exception e1) {
622 			throw new RuntimeException (e1); 
623 		}
624 		initialize();
625 	}
626 	
627 	/**
628 	 * 
629 	 * @param updateSite
630 	 * @param configurationFile
631 	 * @param propertiesCachePath
632 	 * @throws FileNotFoundException
633 	 */
634 	public CAIBSigner(String updateSite,URL configurationFile,String propertiesCachePath) throws FileNotFoundException {
635 		preInitialize();
636 		
637 		//Inicializamos las propiedades de firma 
638 		try {
639 				properties = new SignaturaProperties(updateSite,configurationFile,propertiesCachePath);
640 			
641 		} catch (Exception e1) {
642 			throw new RuntimeException (e1); 
643 		}
644 		initialize();
645 	}
646 	
647 	/**
648 	 * 
649 	 * @param signerConfiguration
650 	 * @throws FileNotFoundException
651 	 */
652 	public CAIBSigner (Map signerConfiguration) throws FileNotFoundException
653 	{
654 		preInitialize();
655 		
656 		//Inicializamos las propiedades de firma 
657 		try {
658 				properties = new SignaturaProperties(signerConfiguration);
659 		} catch (Exception e1) {
660 			throw new RuntimeException (e1); 
661 		}
662 		initialize();
663 	}
664 	
665 	protected void preInitialize() throws FileNotFoundException{
666 		//registramos el security manager
667 		CAIBSecurityManager.register();	
668 	    
669 		//creamos el classloaderFactory que se encarga de gestionar la carga y acceso a las clases
670 		//también inicializamos las listas de signers disponibles (locales y remotos)
671 		if (factory == null) {
672 		    factory = ClassLoaderFactory.getFactory ();
673 		    realSigners = new LinkedHashMap();
674 		    remoteRealSigners = new LinkedHashMap();
675 		}
676 	}
677 	
678 	protected void initialize () throws FileNotFoundException {
679 		
680 
681 
682 		try {
683 			
684 			/*
685 			 * Cargamos dinámicamente los security providers de JCA
686 			 * de BouncyCastle y de LunaPCI
687 			 */
688 			
689 			if (Security.getProvider("BC") == null) {
690 				Provider p = (Provider) factory.getMasterClassLoader().loadClass("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
691 				Security.addProvider(p);
692 			}
693 			
694 			// Sólo intentamos cargar los providers de Luna si tenemos
695 			// disponible el JAR para Luna en la instalación del API de firma
696 			InputStream inputStream = factory.getMasterClassLoader().getResourceAsStream("es/caib/signatura/provider/impl/lunaPCI/LunaPCISigner.class");
697 			if (inputStream != null) {
698 								
699 				if (Security.getProvider("LunaJCE") == null) {
700 					Provider p = (Provider) factory.getMasterClassLoader().loadClass("com.chrysalisits.cryptox.LunaJCEProvider").newInstance();
701 					Security.addProvider(p);
702 				}
703 				
704 				if (Security.getProvider("LunaJCA") == null) {
705 					Provider p = (Provider) factory.getMasterClassLoader().loadClass("com.chrysalisits.crypto.LunaJCAProvider").newInstance();
706 					Security.addProvider(p);
707 				}
708 			
709 			}
710 			
711 		} catch(Exception e) {
712 			
713 			throw new RuntimeException (e); 
714 			
715 		}
716 		
717 
718 
719 		// Si ningu no ha posat el nivell primer, posem el que hi hagi al fitxer
720 		// de properties, sino, deixem el que hi ha.
721 		if ( SigDebug.getLevel() == SigDebug.DEBUG_LEVEL_NONE ) {
722 			SigDebug.setLevel( properties.getProperty("debugLevel") );
723 		}
724 
725 		
726 		/**
727 		 * Empezamos la carga de signers o proveedores de nuestra api
728 		 * 
729 		 */
730 		//TRADISE / TRADISE-DEV
731 		if (System.getProperty("es.caib.provider.unactive") == null 
732 				|| System.getProperty("es.caib.provider.unactive").indexOf("tradise") == -1) {
733 
734 			try {
735 				addTradiseSigner( "tradise");
736 				SigDebug.write( "[DBG] ACTIVAT TRADISE." );
737 			} catch (FileNotFoundException e) {
738 					//quan no est� instal.lat
739 			} catch (Exception e) {
740 				e.printStackTrace();
741 			}
742 	
743 			File dirDev = new File (factory.getLibraryDir(), "tradise-dev");
744 			if (dirDev.isDirectory())
745 			{
746 				try {
747 					addTradiseSigner( "tradise-dev");
748 					SigDebug.write( "[DBG] ACTIVAT TRADISE TEST." );
749 				} catch (Exception e) {
750 					e.printStackTrace();
751 				}
752 			}
753 			
754 		}
755 		
756 		//PKCS11
757 		if (System.getProperty("es.caib.provider.unactive") == null 
758 				|| System.getProperty("es.caib.provider.unactive").indexOf("PKCS11") == -1) {
759 			
760 			String drivers []= properties.getPKCS11Drivers();
761 			
762 			for (int i = 0; i < drivers.length; i++)
763 			{
764 				try{	
765 					if(addPKCS11Signer(drivers[i]))
766 						SigDebug.write( "[DBG] ACTIVAT PKCS11 "+drivers[i]+"." );
767 				}
768 				catch (Throwable e){
769 					e.printStackTrace();
770 				}
771 			}
772 			
773 		}
774 		
775 		//MSCRYPTOAPI / MSCAPI
776 		//Sólo activamos este provider si estamos en un entorno windows
777 		if ( System.getProperty("os.name").contains("Windows") ) {
778 		
779 			if (System.getProperty("es.caib.provider.unactive") == null 
780 					|| System.getProperty("es.caib.provider.unactive").indexOf("MSCRYPTOAPI") == -1) {
781 				
782 				try {
783 					addMscryptoapiSigner();	
784 						
785 				}
786 				catch (Exception e){
787 					e.printStackTrace();
788 				}
789 				
790 			}
791 		
792 		}
793 		
794 		//LUNAPCI HSM
795 		if (System.getProperty("es.caib.provider.unactive") == null 
796 				|| System.getProperty("es.caib.provider.unactive").indexOf("LUNAPCI") == -1) {
797 			
798 			try {
799 				if ( addLunaPCISigner() )
800 					SigDebug.write( "[DBG] ACTIVAT LUNAPCI." );
801 			}
802 			catch (Exception e){
803 				e.printStackTrace();
804 			}
805 			
806 		}
807 		
808 		//WEBSERVICE SIGNER CLIENT
809 		if (System.getProperty("es.caib.provider.unactive") == null
810 				|| System.getProperty("es.caib.provider.unactive").indexOf("WEBSERVICESIGNER") == -1) {
811 		
812 			try {
813 				if ( addWebServiceSigner() )
814 					SigDebug.write( "[DBG] ACTIVAT WEBSERVICESIGNER." );
815 			}
816 			catch (Throwable e){
817 				e.printStackTrace();
818 			}
819 		
820 		}
821 		
822 		//FIREFOX via NSS
823 		if (System.getProperty("es.caib.provider.unactive") == null
824 				|| System.getProperty("es.caib.provider.unactive").indexOf("FIREFOXSIGNER") == -1) {
825 			
826 			try{		
827 				if ( addFirefoxSigner(factory) )
828 					SigDebug.write("[DBG] ACTIVAT FIREFOXSIGNER.");
829 			}
830 			catch (Throwable e){
831 				e.printStackTrace();
832 			}	
833 			
834 			
835 		}
836 		
837 		// Sólo cargamos nuestro provider de BC si las propiedades de sistema necesarias
838 		// para que éste funcione se encuentran presentes.
839 		if ( System.getProperty("es.caib.provider.unactive") == null 
840 				|| System.getProperty("es.caib.provider.unactive").indexOf("BC") == -1 ) {
841 		
842 			if ( System.getProperty("caib-crypto-keystore-password") != null ) {
843 				
844 				try {
845 					
846 					if(addBcCryptoApiSigner())
847 						SigDebug.write("[DBG] ACTIVAT BC.");
848 					
849 				} catch (Exception e) {
850 					
851 					if (e instanceof SignatureException 
852 							&& e.getMessage().contains(".keystore")) {
853 						
854 						SigDebug.write("WARNING (BC): el usuario no posee keystore por defecto. No se cargará el signer de BC.");
855 						
856 					} else {
857 						
858 						e.printStackTrace();
859 						
860 					}
861 					
862 				}
863 				
864 			} else {
865 				
866 				SigDebug.write("[DBG] System properties needed for BC signer are not set");
867 				
868 			}
869 			
870 		}
871 		
872 		
873 	
874 	}
875 
876 	private boolean addPKCS11Signer (String libraryName) throws Exception
877 	{
878 
879 			File f = new File (libraryName);
880 			if ( f.isAbsolute())
881 			{
882 				String name = f.getName();
883 				if (realSigners.get(name) == null)
884 				{
885 					if(addPKCS11Signer (name, f)) return true;
886 				}
887 			} else { 
888 				if (realSigners.get(libraryName) == null)
889 				{
890 					String path = System.getProperty("java.library.path");
891 					StringTokenizer tokenizer = new StringTokenizer (path, File.pathSeparator);
892 					while (tokenizer.hasMoreTokens())
893 					{
894 						File next = new File( tokenizer.nextToken() );
895 						f = new File (next, libraryName);
896 						if (addPKCS11Signer (libraryName, f))
897 							return true;
898 						f = new File (next, libraryName+".dll") ;
899 						if (addPKCS11Signer (libraryName, f))
900 							return true;
901 						
902 					}
903 				}else{
904 					return true;
905 				}
906 			}
907 
908 		return false;
909 	}
910 	
911 	private boolean addPKCS11Signer(String libraryName, File f) throws Exception {
912 		if (f.canRead())
913 		{
914 			String providerName = properties.getPKCS11DriversDescription(libraryName);
915 
916 				File cfg = File.createTempFile(libraryName, "pkcs11.cfg");
917 				PrintWriter writer = new PrintWriter (new FileWriter (cfg));
918 				writer.println("name="+libraryName);
919 				writer.println("library="+f.getAbsolutePath());
920 				writer.close();
921 				cfg.deleteOnExit();
922 				Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.pkcs11.PKCS11Signer");
923 				Constructor constructor = c.getConstructor(new Class [] {String.class,String.class});
924 				realSigners.put(libraryName, constructor.newInstance(new Object [] {cfg.getAbsolutePath(),providerName}));
925 				return true;
926 
927 
928 		}
929 		else
930 			return false;
931 	}
932 
933 	private void addTradiseSigner (String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException, FileNotFoundException 
934 	{
935 
936 		if (realSigners.get(name) == null)
937 		{
938 			ClassLoader loader = factory.getClassLoader(name);
939 			Class c = loader.loadClass("es.caib.signatura.provider.impl.tradise.TradiseSigner");
940 			realSigners.put(name, c.newInstance());
941 		}
942 
943 	}
944 	
945 	private boolean addBcCryptoApiSigner() 
946 			throws SignatureException, ClassNotFoundException, InstantiationException, IllegalAccessException
947 	{
948 		//PJR - si no comentamos esto, es necesario reiniciar jboss para que coja un cambio en la clave
949 		//if (realSigners.get("bccryptoapi") == null)
950 		//{
951 			Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.bccryptoapi.BcCryptoApiSigner");
952 			realSigners.put("bccryptoapi", c.newInstance());
953 		//}		
954 		if ( SigDebug.isActive() )
955 			SigDebug.write("CAIBSigner: addBcCryptoApiSigner: Keystore certificates loaded.");
956 		
957 		return true;
958 	}
959 	
960 	private boolean addWebServiceSigner() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
961 		//PJR - si no comentamos esto, es necesario reiniciar jboss para que coja un cambio en la clave
962 		//if (remoteRealSigners.get("WebServiceSigner") == null)
963 		//{
964 			//AML - Sólo intentamos cargar el provider si existe el JAR del mismo
965 			InputStream inputStream = factory.getMasterClassLoader().getResourceAsStream("es/caib/signatura/provider/impl/webServiceSigner/WebServiceSigner.class");
966 			
967 			if (inputStream != null) {
968 				
969 				Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.webServiceSigner.WebServiceSigner");
970 				remoteRealSigners.put("WebServiceSigner", c.newInstance());
971 				
972 				if ( SigDebug.isActive() )
973 					SigDebug.write("CAIBSigner: addWebServiceSigner: WebServiceSigner loaded.");
974 				
975 				return true;
976 				
977 			}
978 			
979 			return false;
980 		//}
981 		//return true;
982 		
983 	}
984 	
985 	/**
986 	 * Añade mscryptoapi como proveedor de firma de la CAIB. Sólo se hace si se puede cargar la biblioteca dinámica mscryptofunctions.dll,
987 	 * que debe estar en el directorio JAVA_HOME\lib\signaturacaib\ del PC con windows del usuario
988 	 * @author Jesus Reyes (3digits)
989 	 */
990 	private void addMscryptoapiSigner() {
991 		if (realSigners.get("mscryptoapi") == null)
992 		{
993 			// Obtenemos versión completa x.y.z (e.g. 1.6.0_24)
994 			String fullVersion = System.getProperty("java.version");
995 			// Buscamos la posición del último punto
996 			int lastIndex = fullVersion.lastIndexOf(".");
997 			// Obtenemos todo lo que había antes de éste (e.g. 1.6)
998 			String version = fullVersion.substring(0, lastIndex);
999 			// Construímos un float para comparar fácilmente
1000 			float versionNumber = Float.valueOf(version).floatValue();
1001 			// Detectamos si el entorno JRE es de 64bits
1002 			boolean is64bits = (System.getProperty("os.arch").indexOf("64") != -1); 
1003 			
1004 			// Usamos el provider implementado usando API propia del JRE para acceso a la 
1005 			// MSCAPI, introducida a partir de Java 1.6 y si estamos en un sistema de 64 bits.
1006 			// lo de los 64 bits es para no compilar nuestra dll en 64 bits
1007 			
1008 			if (versionNumber >= 1.6 && is64bits) {
1009 			
1010 				try {
1011 					
1012 					//System.out.println("Usando provider MSCAPI");
1013 					
1014 					Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.sunmscapi.SunMSCAPIProvider");
1015 					realSigners.put("mscryptoapi", c.newInstance());
1016 									
1017 					if( SigDebug.isActive() )
1018 						SigDebug.write("CAIBSigner: addMscryptoapiSigner: es.caib.signatura.provider.impl.sunmscapi.SunMSCAPIProvider");
1019 					
1020 					SigDebug.write( "[DBG] ACTIVAT MSCAPI." );
1021 				} catch (Throwable e) {
1022 					
1023 					e.printStackTrace();
1024 					SigDebug.write("es.caib.signatura.provider.impl.sunmscapi.SunMSCAPIProvider cannot be loaded.");
1025 				
1026 				}
1027 				
1028 				
1029 				
1030 			// Si se está usando una versión de Java inferior a la 1.6 o un sistema de 32bits
1031 			// usamos el provider "antiguo" propio del sistema.
1032 			} else {
1033 				
1034 				try {
1035 					
1036 					//System.out.println("Usando provider MSCRYPTOAPI");
1037 					
1038 					String libraryName = System.getProperty("java.home")+"\\lib\\signaturacaib\\mscryptofunctions.dll";
1039 					
1040 					if( SigDebug.isActive() )
1041 						SigDebug.write("CAIBSigner: addMscryptoapiSigner: Path mscryptofunctions = " + libraryName);
1042 					
1043 					File f = new File (libraryName);
1044 					
1045 					if (f.canRead()){
1046 						
1047 						Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.mscryptoapi.MscryptoapiSigner");
1048 						realSigners.put("mscryptoapi", c.newInstance());
1049 						
1050 						if( SigDebug.isActive() )
1051 							SigDebug.write("CAIBSigner: addMscryptoapiSigner: Cargada mscryptofunctions.dll");
1052 					    					
1053 					}
1054 					
1055 					SigDebug.write( "[DBG] ACTIVAT MSCRYPTOAPI." );
1056 				} catch(Exception e)	{
1057 					
1058 					e.printStackTrace();
1059 					SigDebug.write("mscryptofunctions.dll cannot be loaded.");
1060 					
1061 				}
1062 										
1063 			}
1064 		}
1065 		
1066 	}
1067 
1068 	/**
1069 	 * Método que añade el Signer necesario para usar las funciones de los dispositivos LunaPCI.
1070 	 * @throws ClassNotFoundException
1071 	 * @throws InstantiationException
1072 	 * @throws IllegalAccessException
1073 	 */
1074 	private boolean addLunaPCISigner() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
1075 		//PJR - con esta comprobación, es necesario reiniciar para que coja los cambios en configuración
1076 		//if (realSigners.get("lunaPCI") == null)
1077 		//{
1078 			// Sólo intentamos cargar el provider si existe el JAR del mismo
1079 			InputStream inputStream = factory.getMasterClassLoader().getResourceAsStream("es/caib/signatura/provider/impl/lunaPCI/LunaPCISigner.class");
1080 			
1081 			if (inputStream != null) {
1082 				
1083 				Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.lunaPCI.LunaPCISigner");
1084 				realSigners.put("lunaPCI", c.newInstance());
1085 				
1086 				if ( SigDebug.isActive() )
1087 					SigDebug.write("CAIBSigner: addLunaPCISigner: Cargados los certificados del dispositivo");
1088 		
1089 				return true;
1090 				
1091 			}
1092 			
1093 			return false;
1094 		//}
1095 		//return true;
1096 		
1097 	}	
1098   
1099 	public SMIMEParser getSMIMEParser(InputStream smime) throws InstantiationException, IllegalAccessException, IOException, SignatureException {	
1100 		return new SMIMEParserProxy(smime);
1101 	}
1102   
1103 	public ParsedCertificate parseCertificate(X509Certificate certificate) {
1104 		try {
1105 			Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.common.ParsedCertificateImpl");
1106 			Constructor constructor = c.getConstructor(new Class [] {X509Certificate[].class, boolean.class});
1107 			// El ParsedCertificateImp que retornem, sempre estará com si
1108 			// no estigués en un dispositiu segur.
1109 			return (ParsedCertificate) constructor.newInstance(new Object [] {new X509Certificate[] {certificate}, new Boolean(false)});
1110 		} catch (Exception e) {
1111 			throw new RuntimeException(e);
1112 		}
1113 	}
1114 
1115 
1116 
1117 	
1118 	
1119 
1120 		/**
1121 		 * http://download.oracle.com/javase/6/docs/technotes/guides/security/p11guide.html
1122 		 * https://developer.mozilla.org/En/JSS
1123 		 * Añade un nuevo provider para firefox, con ciertas limitaciones
1124 		 * no va en MAC OSX 10.4, y sólo carga certificados software
1125 		 * @return
1126 		 */
1127 		private  boolean addFirefoxSigner(ClassLoaderFactory factory) throws Exception {
1128 			// Comprobar SO
1129 			// Encontrar librería dinámica softokn3.dll o libsoftokn3.so
1130 			// Buscar perfiles
1131 			// Iterar perfiles y cargar un signer por perfil/keystore
1132 			
1133 			//comentamos esto para que cargue más de una vez el provider, y así pida la contraseña de nuevo
1134 			//if (realSigners.get("FIREFOXSIGNER") == null)
1135 			//{
1136 
1137 				
1138 				String osName = null;
1139 				String[] nssDirs = null;
1140 				String nssLibraryDirectory = null;
1141 				String profilesPath = null;
1142 				String[] profileDirs = null;
1143 				
1144 				String homeDir = System.getProperty("user.home").replaceAll("\\\\","/");
1145 				
1146 				if ( System.getProperty("os.name").contains("Windows") ) {
1147 					
1148 					osName = "windows";
1149 					
1150 					String programFiles32=System.getenv("ProgramFiles").replaceAll("\\\\", "/");
1151 					String programFiles64=System.getenv("ProgramFiles(x86)");
1152 					
1153 					//mirar a program files 32 bits i 64 bits !!! (ufff)
1154 					if(programFiles64!=null && !"".equals(programFiles64)){
1155 						//windows 64 bits
1156 						programFiles64=programFiles64.replaceAll("\\\\", "/");
1157 						if(System.getProperty("os.arch").indexOf("64")!=-1){
1158 							//carreguem firefox 64 bits
1159 							nssDirs = new String[] {
1160 									programFiles64+"/Mozilla Firefox"
1161 							};
1162 						}else{
1163 							//carreguem firefox 32 bits
1164 							nssDirs = new String[] {
1165 									programFiles32+"/Mozilla Firefox"
1166 							};							
1167 						}
1168 					}else{
1169 						//windows 32 bits
1170 						nssDirs = new String[] {
1171 								programFiles32+"/Mozilla Firefox"
1172 						};
1173 
1174 					}
1175 					
1176 					String appdata=System.getenv("APPDATA").replaceAll("\\\\", "/");
1177 					profileDirs = new String[] {
1178 							appdata+"/Mozilla/Firefox/Profiles"
1179 					};
1180 					
1181 				} else if ( System.getProperty("os.name").contains("Linux") ) {
1182 					
1183 					osName = "linux";
1184 					
1185 					
1186 					//sabemos que firefox tiene el directorio dependiente de la versión, así que filtramos todos los directorios que contengan firefox para iniciar la búsqueda
1187 					File sysLibPath=new File("/usr/lib");
1188 					File[] firefoxLibDirs=sysLibPath.listFiles(new FileFilter() {
1189 						
1190 						public boolean accept(File pathname) {
1191 							if(pathname.getAbsolutePath().contains("firefox")) return true;
1192 							return false;
1193 						}
1194 					});
1195 					
1196 					//convertimos de File[] a String[]
1197 					nssDirs=new String [firefoxLibDirs.length];
1198 					
1199 					int i=0;
1200 					for(i=0;i<firefoxLibDirs.length;i++){
1201 						nssDirs[i]=firefoxLibDirs[i].getAbsolutePath();
1202 					}
1203 					
1204 	
1205 					//Buscamos los directorios de perfiles
1206 					
1207 					profileDirs = new String[] {
1208 						homeDir + "/.mozilla/firefox"
1209 					};
1210 					
1211 	
1212 					
1213 					
1214 				} else if ( System.getProperty("os.name").contains("Mac OS") ) {
1215 					
1216 					osName = "mac";
1217 					
1218 	
1219 					nssDirs = new String[] {
1220 						"/Applications/Firefox.app/Contents/MacOS"
1221 					};
1222 					
1223 	
1224 					profileDirs = new String[] {
1225 							homeDir+"/Library/Mozilla/Firefox/Profiles",
1226 							homeDir+"/Library/Application Support/Firefox/Profiles"
1227 					};
1228 					
1229 	
1230 					
1231 								
1232 				} else {
1233 					
1234 					SigDebug.write("WARNING: OS not supported yet '" + System.getProperty("os.name") + "'");
1235 					return false;
1236 					
1237 				}
1238 				
1239 				// Buscamos en qué directorio se encuentra la librería dinámica softokn3
1240 				for(int i=0;i<nssDirs.length;i++)
1241 					SigDebug.write("NSSDir: "+nssDirs[i]);
1242 				
1243 				if(nssDirs!=null)
1244 					nssLibraryDirectory = firefoxSignerFindNSSLibraryDirectory(nssDirs, osName);
1245 
1246 				SigDebug.write("nssLibraryDirectory: "+nssLibraryDirectory);
1247 
1248 				if (nssLibraryDirectory == null) {
1249 					SigDebug.write("WARNING: NSS library directory not found");
1250 					return false;
1251 				}
1252 																	
1253 				// Cargamos resto de librerías dinámicas necesarias
1254 				firefoxSignerLoadNSSLibraries(nssLibraryDirectory, osName);
1255 				
1256 				// Buscamos en qué directorio se encuentran los perfiles de Firefox del usuario
1257 				profilesPath = firefoxSignerFindProfilesDirectory(profileDirs);
1258 				if ( profilesPath == null ) {
1259 					SigDebug.write("WARNING: Profiles directory not found");
1260 					return false;
1261 				}
1262 										
1263 				// Listar prerfiles de Firefox
1264 				File profilesDir = new File( profilesPath );
1265 				File[] profilesDirFiles = firefoxSignerFilterProfileDirectories( profilesDir.listFiles() );
1266 								
1267 				if (profilesDirFiles.length > 0) {
1268 					
1269 					int i = 0;
1270 																
1271 					try {
1272 						
1273 						// Si hay más de un perfil toca preguntar al usuario qué perfil
1274 						// quiere cargar como keystore. Si no, usamos el único que hay (índice 0).
1275 						if (profilesDirFiles.length > 1)
1276 							i = firefoxSignerAskForUserProfile(profilesDirFiles);
1277 						
1278 						String providerConfig = firefoxSignerGetProviderConfig(profilesDirFiles[i], profilesPath, nssLibraryDirectory);
1279 						
1280 						Class c = factory.getMasterClassLoader().loadClass("es.caib.signatura.provider.impl.firefox.FirefoxSigner");
1281 						Constructor constructor = c.getConstructor(new Class [] {String.class, String.class});
1282 						realSigners.put(
1283 								"FIREFOXSIGNER", 
1284 								constructor.newInstance(new Object [] {providerConfig, profilesDirFiles[i].getName()})
1285 						);
1286 						
1287 					}catch (InvocationTargetException e) {
1288 						e.getTargetException().printStackTrace();
1289 						return false;
1290 						
1291 					} catch (Exception e) {
1292 						
1293 						e.printStackTrace();
1294 						return false;
1295 						
1296 					}
1297 					
1298 				} else {
1299 					
1300 					SigDebug.write("WARNING: No avalaible Firefox profiles found");
1301 					return false;
1302 					
1303 				}
1304 			//}			
1305 			return true;
1306 
1307 												
1308 		}
1309 		
1310 		/**
1311 		 * Diálogo que se le presenta al usuario para que seleccione el perfil de Firefox
1312 		 * del cual se leerán los certificados almacenados.
1313 		 * @param profilesDirFiles
1314 		 * @return
1315 		 * @throws Exception 
1316 		 */
1317 		private static int firefoxSignerAskForUserProfile(File[] profilesDirFiles) throws Exception {
1318 			
1319 			return FirefoxSignerProfileChooser.getFirefoxProfile(profilesDirFiles);
1320 			
1321 		}
1322 
1323 		/**
1324 		 * Método que devuelve la ruta del directorio que almacena la librería dinámica softokn3.
1325 		 * @param dirs
1326 		 * @param osName
1327 		 * @return
1328 		 */
1329 		private static String firefoxSignerFindNSSLibraryDirectory(String[] dirs, String osName) {
1330 			
1331 			boolean found = false;
1332 			String nssLibraryDirectory = null;
1333 			String libName = null;
1334 			
1335 			if ( "windows".equals(osName) )
1336 				libName = "/softokn3.dll";
1337 			else if ( "linux".equals(osName) )
1338 				libName = "/libsoftokn3.so";
1339 			else if ( "mac".equals(osName) )
1340 				libName = "/libsoftokn3.dylib";
1341 			else
1342 				// Devolvemos null y, al comprobar este valor de retorno, la
1343 				// carga del FirefoxSigner se interrumpirá.
1344 				return null;
1345 					
1346 			for (int i = 0; i < dirs.length; i++) {
1347 				
1348 				found = new File( dirs[i] + libName ).exists();
1349 				
1350 				if ( found ) {
1351 					nssLibraryDirectory = dirs[i];
1352 					break;
1353 				}
1354 				
1355 			}
1356 			
1357 			return nssLibraryDirectory;
1358 			
1359 		}
1360 		
1361 		/**
1362 		 * Método que devuelve la ruta hacia el directorio que contiene los perfiles de usuario
1363 		 * de Firefox del usuario que ejecuta el API de firma.
1364 		 * @param dirs
1365 		 * @return
1366 		 */
1367 		private static String firefoxSignerFindProfilesDirectory(String[] dirs) {
1368 			
1369 			boolean found = false;
1370 			String profilesPath = null;
1371 			
1372 			for (int i = 0; i < dirs.length; i++) {
1373 				
1374 				found = new File( dirs[i] ).exists() && new File( dirs[i] ).isDirectory();
1375 				
1376 				if ( found ) {
1377 					profilesPath = dirs[i];
1378 					break;
1379 				}
1380 				
1381 			}
1382 			
1383 			return profilesPath;
1384 			
1385 		}
1386 		
1387 		/**
1388 		 * Método que carga las librerías dinámicas necesarias, junto a la de softokn3,
1389 		 * para poder acceder al keystore PKCS11 de Firefox mediante NSS.
1390 		 * @param nssLibraryDirectory
1391 		 * @param osName
1392 		 */
1393 		private static void firefoxSignerLoadNSSLibraries(String nssLibraryDirectory, String osName) {
1394 			
1395 			if ( "windows".equals(osName) ) {
1396 				
1397 				try{System.load(nssLibraryDirectory + "/mozcrt19.dll");}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1398 				try{System.load(nssLibraryDirectory + "/sqlite3.dll");}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1399 				try{System.load(nssLibraryDirectory + "/nspr4.dll");}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1400 				try{System.load(nssLibraryDirectory + "/plc4.dll");}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1401 				try{System.load(nssLibraryDirectory + "/plds4.dll");}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1402 				try{System.load(nssLibraryDirectory + "/nssutil3.dll");}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1403 				
1404 			} else if ( "linux".equals(osName) || "mac".equals(osName)  ) {
1405 				//cargamos todas las dlls del directorio
1406 				File nssLibDir=new File(nssLibraryDirectory);
1407 				File [] nssLibs=nssLibDir.listFiles(new FileFilter() {
1408 					public boolean accept(File pathname) {
1409 						if(pathname.getAbsolutePath().endsWith(".so") || pathname.getAbsolutePath().endsWith(".dylib")) return true;
1410 						return false;
1411 					}
1412 				});
1413 				int i=0;
1414 				for(i=0;i<nssLibs.length;i++)
1415 					try{System.load(nssLibs[i].getAbsolutePath());}catch(UnsatisfiedLinkError e){e.printStackTrace();}
1416 				
1417 				
1418 			}
1419 			
1420 		}
1421 		
1422 		/**
1423 		 * Método que se encarga de detectar qué directorios especificados son perfiles de Firefox.
1424 		 * @param dirs
1425 		 * @return
1426 		 */
1427 		private static File[] firefoxSignerFilterProfileDirectories(File[] dirs) {
1428 			
1429 	
1430 			Vector out = new Vector();
1431 			
1432 			for (int i = 0; i < dirs.length; i++) {
1433 				
1434 				String dir = dirs[i].getName();
1435 				
1436 				// Los nombres de los directorios que contiene perfiles siguen un esquema
1437 				// de nombres de este tipo: 8 letras "al azar" + . + nombre del perfil.
1438 				if (dir.length() > 8 && dir.charAt(8) == '.' && !dir.contains("profiles.ini")) 
1439 					out.add( dirs[i] );
1440 							
1441 			}
1442 			
1443 			return (File[])out.toArray(new File[out.size()]);
1444 			
1445 		}
1446 		
1447 		/**
1448 		 * Método que genera la configuración para la carga del provider SunPKCS11, el cual
1449 		 * se usará para acceder al keystore de Firefox mediante NSS.
1450 		 * http://www.forosdelweb.com/f45/como-acceder-keystore-firefox-664947/
1451 		 * @param profileDir
1452 		 * @param profilesPath
1453 		 * @param nssLibraryDirectory
1454 		 * @return
1455 		 * @throws FileNotFoundException 
1456 		 */
1457 		private static String firefoxSignerGetProviderConfig(File profileDir, String profilesPath, String nssLibraryDirectory) throws FileNotFoundException {
1458 			
1459 			String profileDirName = profileDir.getName();
1460 			
1461 //			System.out.println("================================================================================");
1462 //			System.out.println( profileDirName.substring(9) );
1463 //			System.out.println( "Profile directory: " + profileDir.getAbsolutePath() );
1464 //			System.out.println("================================================================================");
1465 			
1466 			String nssSecmodDirectory = profilesPath + "/" + profileDirName;
1467 			
1468 
1469 			if(System.getProperty("os.name").contains("Windows")){
1470 				if(System.getProperty("java.version").contains("7")){
1471 					//tradüim a short path http://bugs.sun.com/view_bug.do?bug_id=6581254
1472 					nssLibraryDirectory=getWindowsShortPath(new File(nssLibraryDirectory)).replaceAll("\\\\", "\\\\");
1473 					nssSecmodDirectory=getWindowsShortPath(new File(nssSecmodDirectory.replaceAll("/","\\\\"))).replaceAll("\\\\", "\\\\").replaceAll("\\\\","/");
1474 				}else{
1475 					nssLibraryDirectory=new File(nssLibraryDirectory).getAbsolutePath().replaceAll("\\\\", "\\\\");
1476 					nssSecmodDirectory=new File(nssSecmodDirectory).getAbsolutePath().replaceAll("\\\\", "\\\\").replaceAll("\\\\","/");
1477 				}
1478 			}
1479 			
1480 			String providerCfg = "name = NSS" + profileDirName + "\n"+
1481 			"nssLibraryDirectory = " + nssLibraryDirectory + "\n"+
1482 			"nssSecmodDirectory = \"" + nssSecmodDirectory + "\"\n"+
1483 			"nssDbMode = readOnly\n" +
1484 			"nssModule = keystore\n";
1485 			
1486 			//System.out.println( providerCfg );
1487 									
1488 			return providerCfg;
1489 			
1490 		}
1491 
1492 		
1493 		public static String getWindowsShortPath(File f) throws FileNotFoundException{
1494 			if(!f.exists()) throw new FileNotFoundException("Cannot get short path of a non existing file");
1495 			
1496 			String shortpath = "";
1497 	        StringTokenizer tokenizer = new StringTokenizer(f.getAbsolutePath(), "\\");
1498 	        String temp = null;
1499 	        String nextTemp=null;
1500 	        while(tokenizer.hasMoreTokens() == true)
1501 	        {
1502 	        	int counter=1;
1503 	        	
1504 	        	temp=(temp==null)?tokenizer.nextToken():nextTemp;
1505 	        	nextTemp=tokenizer.nextToken();
1506 	        	String tempShortpath=null;
1507 	        	do{
1508 	        		if(temp.length() > 8)
1509 	        			tempShortpath = temp.replaceAll(" ","").substring(0, 6) + "~"+counter + "\\";
1510 	        		else
1511 	        			tempShortpath = temp + "\\";
1512 	        		
1513 	        		counter++;
1514 	        		//System.out.println(shortpath+tempShortpath+nextTemp + " exists: "+new File(shortpath+tempShortpath+nextTemp).exists());
1515 	        	}while(!new File(shortpath+tempShortpath+nextTemp).exists() && counter<100);
1516 	        	if(counter==100) throw new FileNotFoundException("Short path counter limit exceeded");
1517 	        	
1518 	        	shortpath+=tempShortpath;
1519 	            
1520 	        }
1521 	        return shortpath+nextTemp;
1522 		}
1523 	}
1524 
1525 	
1526 	
1527 
1528 
1529