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.base.util;
22  
23  import java.io.BufferedReader;
24  import java.io.ByteArrayInputStream;
25  import java.io.ByteArrayOutputStream;
26  import java.io.DataOutputStream;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.FileNotFoundException;
30  import java.io.FileOutputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.InputStreamReader;
34  import java.io.OutputStream;
35  import java.io.OutputStreamWriter;
36  import java.io.PrintStream;
37  import java.io.RandomAccessFile;
38  import java.math.BigInteger;
39  import java.net.HttpURLConnection;
40  import java.net.URL;
41  import java.security.Provider;
42  import java.security.Security;
43  import java.security.cert.CertificateException;
44  import java.security.cert.CertificateFactory;
45  import java.security.cert.X509Certificate;
46  import java.text.SimpleDateFormat;
47  import java.util.Arrays;
48  import java.util.Map;
49  import java.util.Properties;
50  
51  import org.apache.log4j.Logger;
52  import org.bouncycastle.asn1.ASN1InputStream;
53  import org.bouncycastle.asn1.ASN1Primitive;
54  import org.bouncycastle.cert.X509CertificateHolder;
55  import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
56  
57  import es.accv.arangi.base.ArangiObject;
58  import es.accv.arangi.base.device.KeyStoreManager;
59  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
60  import es.accv.arangi.base.exception.certificate.validation.ServiceException;
61  import es.accv.arangi.base.exception.certificate.validation.ServiceNotFoundException;
62  import es.accv.arangi.base.util.base64.BASE64Decoder;
63  import es.accv.arangi.base.util.base64.BASE64Encoder;
64  
65  /**
66   * Diversas utilidades necesarias en los proyectos que integran Arangi
67   * 
68   * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
69   */
70  public class Util {
71  
72  	/**
73  	 * Logger de la clase
74  	 */
75  	private static Logger logger = Logger.getLogger(Util.class);
76  	
77  	/**
78  	 * Parseador generico para fechas en formato español: dd/MM/yyyy HH:mm
79  	 */
80  	public static final SimpleDateFormat dateFormat = new SimpleDateFormat ("dd/MM/yyyy HH:mm");
81  
82  	/**
83  	 * Parseador generico para fechas en formato español con segundos: dd/MM/yyyy HH:mm
84  	 */
85  	public static final SimpleDateFormat dateFormatAccurate = new SimpleDateFormat ("dd/MM/yyyy HH:mm:ss");
86  
87  	/**
88  	 * Tamaño por defecto de las claves generadas por Arangi
89  	 */
90  	public static final int DEFAULT_KEY_LENGTH = 1024;
91  
92  	/*
93  	 * Fichero PKCS12 de prueba 
94  	 */
95  	private static final String PRUEBA_PKCS12_FILE = "es/accv/arangi/base/resource/prueba.p12";
96  
97  	/*
98  	 * PIN del fichero PKCS12 de prueba 
99  	 */
100 	private static final String PRUEBA_PKCS12_PIN = "Mu4lWyke";
101 
102   	/**
103 	* Instala el proveedor criptográfico pasado como parámetro
104 	*/
105   	public static void setProvider(String providerName, Provider provider) {
106   		
107   		//-- Test if this provider is installed 
108   		if (!existsCryptographicProvider(providerName)) {
109   			//-- Install Provider
110   			logger.debug ("[Util.setProvider] :: Installed " + provider + "provider");
111   			Security.addProvider(provider);
112   		}
113   	}
114   	
115   	/**
116 	* Test if a cryptographic provider is installed in the system
117 	* 
118 	* @param providerID ID of the provider
119 	* @return Result of test
120 	*/
121   	public static boolean existsCryptographicProvider(String providerID) {
122   		
123   		Provider[] arrayProviders = Security.getProviders();
124 		for (int i = 0; i < arrayProviders.length; i++) {
125 			if (arrayProviders[i].getName().equalsIgnoreCase(providerID)) {
126 				return true;
127 			}
128 		}
129 		
130 		return false;
131   	}
132 
133 	/**
134 	 * Obtiene un objeto X509Certificate a partir de su contenido
135 	 * 
136 	 * @param bCertificate Bytes del contenido del fichero certificado
137 	 * @return X.509 Certificate
138 	 * @throws Exception Error generando el certificado
139 	 */
140 	public static X509Certificate getCertificate(byte [] bCertificate) throws NormalizeCertificateException{
141 		//-- Get inputstream
142 		ByteArrayInputStream bais = new ByteArrayInputStream (bCertificate);
143 		
144 		//-- Get certificate
145 		return getCertificate(bais);
146 	}
147 
148 	/**
149 	 * Obtiene un objeto X509Certificate a partir de su contenido
150 	 * 
151 	 * @param isCertificate Stream de lectura del contenido del fichero del certificado
152 	 * @return X.509 Certificate
153 	 * @throws NormalizeCertificateException Error generando el certificado
154 	 */
155 	public static X509Certificate getCertificate(InputStream isCertificate) throws NormalizeCertificateException{
156 		try {
157 			//-- BC provider
158 			setProvider (ArangiObject.CRYPTOGRAPHIC_PROVIDER_NAME, ArangiObject.CRYPTOGRAPHIC_PROVIDER);
159 			
160 			//-- Get X509Certificate
161 			CertificateFactory certFactory = CertificateFactory.getInstance("X.509", ArangiObject.CRYPTOGRAPHIC_PROVIDER);
162 			return (X509Certificate) certFactory.generateCertificate(isCertificate);
163 		
164 		} catch (CertificateException e) {
165 			  logger.info ("Error generando el certificado");
166 			  throw new NormalizeCertificateException ("Error generando el certificado", e);
167 		} 
168 	}
169 
170 	/**
171 	 * Gets a X509Certificate object from a file.
172 	 * 
173 	 * @param file File whose content is a X.509 certificate in DER or PEM format
174 	 * @return X.509 Certificate
175 	 * @throws FileNotFoundException El fichero no existe
176 	 * @throws NormalizeCertificateException Error generating the certificate
177 	 */
178 	public static X509Certificate getCertificate(File file) throws FileNotFoundException, NormalizeCertificateException{
179 		try {
180 			return getCertificate (new FileInputStream (file));
181 		} catch (NormalizeCertificateException e) {
182 			  logger.info ("Error generando el certificado desde " + file.getAbsolutePath());
183 			  throw new NormalizeCertificateException ("Error generando el certificado desde " + file.getAbsolutePath(), e);
184 		} 
185 	}
186 	
187 	/**
188 	 * Obtiene un X509Certificate en base a una estructura de certificado de
189 	 * Bouncy Castle.
190 	 * 
191 	 * @param certificateHolder Estructura de certificado
192 	 * @return X509Certificate
193 	 * @throws CertificateException No se puede obtener el X509Certificate
194 	 */
195 	public static X509Certificate getCertificate(X509CertificateHolder certificateHolder) throws CertificateException {
196 		return new JcaX509CertificateConverter().getCertificate(certificateHolder);
197 	}
198 
199 	/**
200 	* Guarda en disco un array de bytes.
201 	*
202 	* @param file Fichero donde se guardará el contenido
203 	* @param contenido Contenido a guardar
204 	* @throws Exception No se puede escribir
205 	*/
206 	public static void saveFile(File file, byte[] contenido) throws IOException {
207 		
208 		try {
209 			// Los guardamos a disco.
210 			RandomAccessFile lObjSalida = new RandomAccessFile(file, "rw");
211         	lObjSalida.write(contenido,0,contenido.length);
212 			lObjSalida.close();
213 		} catch (IOException e){
214 			  logger.info ("Error saving file at " + file, e);
215 			  throw e;
216 		}
217 	}
218 
219 	/**
220 	* Lee un stream de lectura y escribe en el fichero destino
221 	*
222 	* @param file Fichero destino 
223 	* @param iStream Stream de lectura
224 	* @throws Exception No se puede leer o escribir
225 	*/
226 	public static void saveFile(File file, InputStream iStream) throws IOException {
227 		
228 		FileOutputStream fos = null;
229 		try {
230 			fos = new FileOutputStream (file);
231 	    	byte[] buffer = new byte [1024];
232 	        int len;
233 	        while ((len = iStream.read(buffer)) > -1) {
234 	        	fos.write(buffer, 0, len);
235 	        }
236 		} catch (IOException e){
237 			  logger.info ("Error saving file at " + file, e);
238 			  throw e;
239 		} finally {
240 			if (fos != null) {
241 				fos.close();
242 			}
243 		}
244 	}
245 
246 	/**
247 	* Guarda en un stream de escritura lo contenido en un stream de lectura
248 	*
249 	* @param out Stream de escritura 
250 	* @param iStream Stream de lectura
251 	* @throws Exception No se puede leer o escribir
252 	*/
253 	public static void save(OutputStream out, InputStream iStream) throws IOException {
254 		
255 		try {
256 	    	byte[] buffer = new byte [1024];
257 	        int len;
258 	        while ((len = iStream.read(buffer)) > -1) {
259 	        	out.write(buffer, 0, len);
260 	        }
261 		} catch (IOException e){
262 			  logger.info ("Error guardando en stream de escritura", e);
263 			  throw e;
264 		} 
265 	}
266 
267 	/**
268 	* Guarda en un stream de escritura un array de bytes
269 	*
270 	* @param out Stream de escritura 
271 	* @param contenido Contenido a guardar
272 	* @throws Exception No se puede leer o escribir
273 	*/
274 	public static void save(OutputStream out, byte[] contenido) throws IOException {
275 		
276 		try {
277         	out.write(contenido, 0, contenido.length);
278 		} catch (IOException e){
279 			  logger.info ("Error guardando en stream de escritura", e);
280 			  throw e;
281 		} 
282 	}
283 
284 	/**
285 	* Lee un fichero en el classpath y escribe en el fichero destino
286 	*
287 	* @param file Fichero destino 
288 	* @param classPathFile Path al fichero dentro del classpath. Recordar que estos
289 	* 	path tienen separadores '/' y no '.'. O sea que si el recurso se encuentra
290 	* 	en el paquete org.java y se llama recurso.rec el path sería org/java/recurso.rec.
291 	* @throws FileNotFoundException No es posible leer el recurso dentro del classpath
292 	* @throws IOException No se puede escribir
293 	*/
294 	public static void saveFileFromClasspath(File file, String classPathFile) throws FileNotFoundException, IOException {
295 		
296 		InputStream iStream = new Util().getClass().getClassLoader().getResourceAsStream(classPathFile);
297 		if (iStream == null) {
298 			throw new FileNotFoundException("No es posible leer el fichero '" + classPathFile + "'dentro del classpath");
299 		}
300 		saveFile(file, iStream);
301 	}
302 	
303     /**
304 	* Carga en un array de bytes la información contenida en el fichero.  
305 	*
306 	* @param file Fichero que contiene la información que se va a cargar	
307 	* @return Array de bytes que contienen la información guardada en el fichero	
308 	* @throws IOException No se puede leer
309 	*/
310 	public static byte[] loadFile(File file) throws IOException {
311 
312 		try {
313 			// Leemos el fichero de disco.
314 			RandomAccessFile lObjFile = new RandomAccessFile(file, "r");
315 			byte lBytDatos[] = new byte[(int)lObjFile.length()];
316             lObjFile.read(lBytDatos);
317             lObjFile.close();
318             
319             return lBytDatos;
320             	
321 		} catch (IOException e) {
322   			  logger.info ("Error cargando el fichero de " + file, e);
323   			  throw e;
324   		}
325 	}
326 	
327     /**
328 	* Carga en un array de bytes la información contenida en un stream de lectura
329 	*
330 	* @param is Stream de lectura que contiene la información que se va a cargar	
331 	* @return Array de bytes que contienen la información guardada en el fichero	
332 	* @throws IOException No se puede leer
333 	*/
334 	public static byte[] readStream(InputStream is) throws IOException {
335 
336 		try {
337 	    	//-- Leer el mensaje SOAP 
338 	    	byte[] buffer = new byte [1024];
339 	    	ByteArrayOutputStream baos = new ByteArrayOutputStream();
340 	        int len;
341 	        while ((len = is.read(buffer)) > -1) {
342 	            baos.write(buffer, 0, len);
343 	        }
344 	    	
345             return baos.toByteArray();
346             	
347 		} catch (IOException e) {
348   			logger.info ("Error cargando el stream de lectura ", e);
349   			throw e;
350   		}
351 	}
352 	
353 	/**
354 	 * Copia de ficheros
355 	 * 
356 	 * @param srcFile Fichero origen
357 	 * @param dstFile Fichero destino
358 	 * @throws FileNotFoundException No existe el fichero origen
359 	 * @throws IOException Error de entrada / salida
360 	 */
361 	public static void copyFile (File srcFile, File dstFile) throws FileNotFoundException, IOException {
362 
363 		InputStream in = null;
364 	    OutputStream out = null;
365 	    try {
366 	    	in = new FileInputStream(srcFile);
367 	    	out = new FileOutputStream(dstFile);
368 		    // Transfer bytes from in to out
369 		    byte[] buf = new byte[1024];
370 		    int len;
371 		    while ((len = in.read(buf)) > 0) {
372 		        out.write(buf, 0, len);
373 		    }
374 	    } finally {
375 	    	if (in != null) { in.close(); }
376 	    	if (out != null) { out.close(); }
377 	    }
378 	}
379 	
380 	/**
381 	 * Método para codificar en base64
382 	 * 
383 	 * @param toCode Array de bytes a codificar
384 	 * @return Resultado de codificar en base64 el array de bytes pasado como parámetro
385 	 */
386 	public static String encodeBase64 (byte [] toCode) {
387 		
388 		BASE64Encoder encoder = new BASE64Encoder();
389 		return encoder.encode(toCode);
390 	}
391 	
392 	/**
393 	 * Método para codificar en base64
394 	 * 
395 	 * @param isToEncode Stream de entrada para codificar
396 	 * @return Resultado de codificar en base64 el array de bytes pasado como parámetro
397 	 */
398 	public static String encodeBase64 (InputStream isToEncode) {
399 		
400 		BASE64Encoder encoder = new BASE64Encoder();
401 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
402 		try {
403 			encoder.encode(isToEncode, baos);
404 		} catch (IOException e) {
405 			return null;
406 		}
407 		
408 		return baos.toString();
409 	}
410 	
411 	/**
412 	 * Método para descodificar de base64
413 	 * 
414 	 * @param toDecode Cadena a descodificar
415 	 * @return Resultado de descodificar de base64 la cadena pasada como parámetro
416 	 */
417 	public static byte[] decodeBase64 (String toDecode) {
418 		
419 		BASE64Decoder decoder = new BASE64Decoder();
420 		try {
421 			return decoder.decodeBuffer(toDecode);
422 		} catch (IOException e) {
423 			return null;
424 		}
425 	}
426 	
427 	/**
428 	 * Método para descodificar de base64
429 	 * 
430 	 * @param isToDecode Cadena a descodificar
431 	 * @return Resultado de descodificar de base64 la cadena pasada como parámetro
432 	 */
433 	public static byte[] decodeBase64 (InputStream isToDecode) {
434 		
435 		BASE64Decoder decoder = new BASE64Decoder();
436 		try {
437 			return decoder.decodeBuffer(isToDecode);
438 		} catch (IOException e) {
439 			return null;
440 		}
441 	}
442 	
443 	/**
444 	 * Método que a partir de un stream de lecture en formato DER obtiene un objeto.
445 	 * 
446 	 * @param  isDER Stream de lectura a un objeto DER
447 	 * @throws IOException Los bytes no contienen un objeto en formato DER
448 	 */
449 	public static ASN1Primitive toDER(InputStream isDER) throws IOException {
450 		
451 		ASN1Primitive lObjRes = null;
452 	
453 		ASN1InputStream  lObjDerOut = new ASN1InputStream(isDER);
454 		lObjRes = lObjDerOut.readObject();
455 		
456 		return lObjRes;							
457 	}
458 
459 	/**
460 	 * Método que a partir de un array de bytes en formato DER obtiene un objeto.
461 	 * 
462 	 * @param  bytesDER Bytes con el objeto DER
463 	 * @throws IOException Los bytes no contienen un objeto en formato DER
464 	 */
465 	public static ASN1Primitive getDER(byte[] bytesDER) throws IOException {
466 		
467 		ByteArrayInputStream bais = new ByteArrayInputStream(bytesDER);
468 		
469 		return toDER (bais);							
470 	}
471 	
472 	/**
473 	* Dado un array de bytes en formato <a href="http://www.unicode.org/">Unicode</a> 
474 	* lo devuelve sin este formato.
475 	* 
476 	* @param bytesUnicode Array de bytes en formato Unicode. 
477 	* @return Array de bytes sin caracteres Unicode 
478 	*/		
479 	public static byte[] fromUnicode(byte[] bytesUnicode){
480 		byte[] bytes = new byte[bytesUnicode.length/2];
481 		for (int i = 0; i< bytes.length; i++) {
482 			bytes[i] = bytesUnicode[2*i];
483 		}
484 		return bytes;
485 	}
486 	
487 	/**
488 	* Dado un contenido en formato <a href="http://www.unicode.org/">Unicode</a> 
489 	* lo devuelve sin este formato.<br><br>
490 	* 
491 	* Este método permite realizar la conversión sin tener que pasar por un
492 	* array de bytes, que en el caso de documentos grandes podría llegar a 
493 	* desbordar la memoria.
494 	* 
495 	* @param isUnicode Stream de lectura a un texto en formato Unicode. 
496 	* @return Stream de lectura sin caracteres Unicode 
497 	*/		
498 	public static InputStream fromUnicode(InputStream isUnicode){
499 		
500 		logger.debug ("[Util.fromUnicode]::Entrada");
501 		
502 		//-- Crear el fichero temporal, y en él escribir uno de cada dos bytes
503 		//-- del documento original
504 		FileOutputStream fos = null;
505 		try {
506 			File fileTemp = File.createTempFile("fromUnicode", null);
507 			fos = new FileOutputStream (fileTemp);
508 			byte[] buffer = new byte[2];
509 			while (isUnicode.read(buffer) >= 0) {
510 				fos.write(buffer[0]);
511 			}
512 			
513 			return new FileInputStream (fileTemp);
514 
515 		} catch (IOException e) {
516 			logger.info("[Util.fromUnicode]::Un error de entrada/salida impide el paso desde Unicode", e);
517 			return null;
518 		} finally {
519 			if (fos != null) {
520 				try {
521 					fos.close();
522 				} catch (IOException e) {
523 					logger.info("[Util.fromUnicode]::No se puede cerrar el stream de escritura al fichero temporal", e);
524 					return null;
525 				}
526 			}
527 		}
528 	}
529 	
530 	/**
531 	* Dado un array de bytes en formato estándar lo devuelve en formato 
532 	* <a href="http://www.unicode.org/">Unicode</a>. 
533 	* 
534 	* @param bytes Array de bytes en formato estándar. 
535 	* @return Array de bytes en Unicode 
536 	*/	
537 	public static byte[] toUnicode(byte[] bytes){
538 		byte[] bytesUnicode = new byte[2*bytes.length];
539 		for (int j = 0; j< bytes.length; j++) {
540 			bytesUnicode[2*j] = bytes[j];
541 			bytesUnicode[2*j+1] = 0x00;   
542 		}
543 		return bytesUnicode;
544 	}
545 	
546 	/**
547 	* Dado un contenido en formato estándar lo devuelve en formato 
548 	* <a href="http://www.unicode.org/">Unicode</a>. 
549 	* 
550 	* Este método permite realizar la conversión sin tener que pasar por un
551 	* array de bytes, que en el caso de documentos grandes podría llegar a 
552 	* desbordar la memoria.
553 	* 
554 	* @param isNormal Stream de lectura a un texto sin formato Unicode. 
555 	* @return Stream de lectura con caracteres Unicode 
556 	*/		
557 	public static File toUnicode(InputStream isNormal){
558 		
559 		logger.debug ("[Util.toUnicode]::Entrada");
560 		
561 		//-- Crear el fichero temporal, y en él ir escribiendo un byte del documento original
562 		//-- y otro vacío
563 		FileOutputStream fos = null;
564 		try {
565 			File fileTemp = File.createTempFile("toUnicode", null);
566 			fos = new FileOutputStream (fileTemp);
567 			byte[] buffer = new byte[1];
568 			while (isNormal.read(buffer) >= 0) {
569 				fos.write(buffer[0]);
570 				fos.write(0x00);
571 			}
572 			
573 			return fileTemp;
574 
575 		} catch (IOException e) {
576 			logger.info("[Util.toUnicode]::Un error de entrada/salida impide el paso a Unicode", e);
577 			return null;
578 		} finally {
579 			if (fos != null) {
580 				try {
581 					fos.close();
582 				} catch (IOException e) {
583 					logger.info("[Util.toUnicode]::No se puede cerrar el stream de escritura al fichero temporal", e);
584 					return null;
585 				}
586 			}
587 		}
588 	}
589 	
590 	/**
591 	 * Comprueba si las Java Cryptographic Extension (JCE) se encuentran instaladas
592 	 * 
593 	 * @return Cierto si las JCE se encuentran instaladas y falso en caso contrario
594 	 */
595 	public static boolean isJCEInstalled () {
596 		logger.debug ("[Util.isJCEInstalled]::Entrada");
597 		
598 		KeyStoreManager manager;
599 		try {
600 			
601 			manager = new KeyStoreManager(ClassLoader.getSystemResourceAsStream(PRUEBA_PKCS12_FILE), PRUEBA_PKCS12_PIN);
602 			
603 		} catch (Exception e) {
604 			logger.debug ("[Util.isJCEInstalled]::Error abriendo el PKCS12: " + e.getMessage());
605 			return false;
606 		} 
607 
608 		logger.debug ("[Util.isJCEInstalled]::No ha habido error abriendo el PKCS12");
609 		return true;
610 		
611 	}
612 	
613 	/**
614 	 * Convierte un String a la codificación indicada
615 	 * 
616 	 * @param text Texto al que cambiar la codificación
617 	 * @param newCharset Nueva codificación
618 	 * @return Texto con la nueva codificación
619 	 * @throws IOException Errores durante la codificación
620 	 */
621 	public static String convertCharSet (String text, String newCharset) throws IOException {
622 		logger.debug ("[Util.convertCharSet]::Entrada");
623 		
624 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
625 		OutputStreamWriter out = new OutputStreamWriter(baos, newCharset);
626 		out.write(text);
627 		out.flush();
628 		out.close();
629 		
630 		return new String (baos.toByteArray(), newCharset);
631 	}
632 	
633 	/**
634 	 * Obtiene la representación hexadecimal del array de bytes que se
635 	 * pasa como parámetro
636 	 * 
637 	 * @param bytes Array de bytes
638 	 * @return Representación hexadecimal del array
639 	 */
640 	public static String toHexadecimal (byte[] bytes) {
641 		logger.debug ("[Util.toHexadecimal]::Entrada::" + bytes);
642 		
643 		BigInteger bi = new BigInteger(1, bytes);
644 		String result = bi.toString(16).toUpperCase();            
645 		if (result.length() % 2 != 0) {
646 			result = "0" + result;
647 		}
648 		
649 		return result;
650 	}
651 	
652 	/**
653 	 * Lee el contenido de un fichero PEM y lo decodifica de base64 a un array de bytes.
654 	 * Los ficheros PEM contienen una linea de inicio y otra de fin que circunscriben
655 	 * el contenido en base64. Por ejemplo:
656 	 * <code>-----BEGIN CERTIFICATE REQUEST-----
657 	 * base64 encoded PKCS10 certification request -----END CERTIFICATE REQUEST----- </code>
658 	 * 
659 	 *  @param pemContent Contenido íntegro del fichero PEM
660 	 *  @return Contenido del fichero PEM sin cabeceras y decodificado de base64
661 	 *  @throws IOException Excepción si el fichero PEM no tiene el contenido correcto
662 	 */
663 	public static byte[] getBytesFromPEM(String pemContent) throws IOException {
664 		logger.debug("[Util.getBytesFromPEM]::Entrada::" + pemContent);
665 	
666 	    //-- Lineas de principio y fin de pem
667 	    String[] PKCS10_INIT  = new String[] {"-----BEGIN CERTIFICATE REQUEST-----", "-----BEGIN NEW CERTIFICATE REQUEST-----"};
668 	    String[] PKCS10_END   = new String[] {"-----END CERTIFICATE REQUEST-----", "-----END NEW CERTIFICATE REQUEST-----"};
669 	
670 	    //-- Detección del tipo de PEM
671 	    byte[] csrBin = null;
672 	    int headerType = -1;
673 	    //-- Deteccion del tipo de cabecera
674 	    for (int i = 0; i < PKCS10_INIT.length; i++) {
675 	      if (pemContent.startsWith(PKCS10_INIT[i])) {
676 	        logger.debug("[Util.getBytesFromPEM]:: Tipo de cabecera encontrado [" + i + "]");
677 	        headerType = i;
678 	        break;
679 	      } else {
680 	        logger.debug("[[Util.getBytesFromPEM]:: El pem no comienza por... '" + PKCS10_INIT[i] + "'");
681 	      }
682 	    }
683 	    
684 	    //-- Comprobar si el fichero contiene las cabeceras
685 	    if (headerType == -1) {
686 	    	logger.debug("[Util.getBytesFromPEM]::No se han encontrado cabeceras, se decodifica de base64");
687 	    	return decodeBase64(pemContent);
688 	    }
689 	    
690 	    //-- Tiene las cabeceras, leer lo que hay entre ellas
691 	    ByteArrayInputStream instream = new ByteArrayInputStream(pemContent.getBytes());
692 	    BufferedReader bufRdr = new BufferedReader(new InputStreamReader(instream));
693 	    ByteArrayOutputStream ostr = new ByteArrayOutputStream();
694 	    PrintStream opstr = new PrintStream(ostr);
695 	    String temp;
696 	
697 	    while (((temp = bufRdr.readLine()) != null) && !temp.equals(PKCS10_INIT[headerType])) {
698 	      continue;
699 	    }
700 	
701 	    if (temp == null) {
702 	      throw new IOException("[Util.getBytesFromPEM]::Error in input buffer, missing " + PKCS10_INIT[headerType] + " boundary");
703 	    }
704 	
705 	    while (((temp = bufRdr.readLine()) != null) && !temp.equals(PKCS10_END[headerType])) {
706 	      opstr.print(temp);
707 	    }
708 	
709 	    if (temp == null) {
710 	      throw new IOException("[Util.getBytesFromPEM]::Error in input buffer, missing " + PKCS10_END[headerType] + " boundary");
711 	    }
712 	
713 	    opstr.close();
714 	    byte[] internalBytes = decodeBase64(new String (ostr.toByteArray()));
715 	    logger.info("[Util.getBytesFromPEM]::Devuelve" + internalBytes.length);
716 	    return internalBytes;
717 	}
718 	
719 	/**
720 	 * Método que realiza una conexión a la URL pasándole con POST el mensaje.
721 	 * 
722 	 *  @param message Mensaje a enviar por post
723 	 *  @param url URL de la conexión
724 	 *  @return Respuesta devuelta en la conexión
725 	 *  @throws IOException
726 	 */
727 	public static StringBuffer sendPost (String message, URL url) throws ServiceNotFoundException, ServiceException {
728 		logger.debug("[Util.getPostConnectionResponse]::Entrada::" + url);
729 		
730 		HttpURLConnection uc = null;
731 		//-- Conectarse
732 		try {
733 			uc = (HttpURLConnection) url.openConnection();
734 	
735 			uc.setDoInput(true);
736 			uc.setDoOutput(true);
737 			uc.setUseCaches(false);
738 			uc.setRequestProperty("Content-Length", "" + message.length());
739 			uc.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
740 			uc.setRequestProperty("SOAPAction", "");
741 			uc.setRequestMethod("POST");
742 	
743 			uc.connect();
744 		} catch (IOException e) {
745 			throw new ServiceNotFoundException(e);
746 		}
747 
748 		//-- Escribir el mensaje
749 		StringBuffer sb = new StringBuffer ("");
750 		try {
751 			byte[] data = message.getBytes("UTF-8");
752 			DataOutputStream dos = new DataOutputStream(uc.getOutputStream());
753 			dos.write(data, 0, data.length);
754 			dos.flush();
755 			dos.close();
756 			
757 			//-- Obtener la respuesta
758 			BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));
759 			String res = null;
760 			while ((res = br.readLine()) != null) {
761 				sb.append(res);
762 			}
763 			br.close();
764 			logger.debug("[Util.getPostConnectionResponse]::Response::" + sb.toString());
765 		} catch (IOException e) {
766 			throw new ServiceException(e);
767 		}
768 		
769 		return sb;
770 	}
771 	
772 	/**
773 	 * Rellena un template con los parámetros que se pasan
774 	 * 
775 	 * @param template Plantilla
776 	 * @param parameters Parámetros para rellenar la plantilla
777 	 * @return Plantill rellenada
778 	 * @throws IOException Error leyendo el template
779 	   */
780 	  public static String fillTemplate (InputStream template, Map<String,String> parameters) throws IOException {
781 		  logger.debug("[Util.fillTemplate]::Entrada::" + Arrays.asList(new Object[] { template, parameters }));
782 		  
783 		  StringBuffer sb = new StringBuffer();
784 		  StringBuffer palabra = new StringBuffer();
785 		  int letra;
786 		  boolean leyendoPalabra = false;
787 		  while ((letra = template.read()) > -1) {
788 			  if ((char)letra == '$' && (char) template.read() == '{') {
789 				  leyendoPalabra = true;
790 			  } else {
791 				  if (leyendoPalabra) {
792 					  if ((char)letra != '}') {
793 						  palabra.append((char) letra);
794 					  } else {
795 						  if (parameters.containsKey(palabra.toString())) {
796 							  sb.append (parameters.get(palabra.toString()));
797 						  }
798 						  palabra = new StringBuffer();
799 						  leyendoPalabra = false;
800 					  }
801 				  } else {
802 					  sb.append((char) letra);
803 				  }
804 			  }
805 		  }
806 		  
807 		  logger.debug("[Util.fillTemplate]::Entrada::" + Arrays.asList(new Object[] { template, parameters }));
808 		  return sb.toString();
809 	
810 	  }
811 
812 }