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.signature;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  
29  import org.apache.log4j.Logger;
30  
31  import es.accv.arangi.base.algorithm.DigitalSignatureAlgorithm;
32  import es.accv.arangi.base.certificate.Certificate;
33  import es.accv.arangi.base.device.DeviceManager;
34  import es.accv.arangi.base.document.FileDocument;
35  import es.accv.arangi.base.document.IDocument;
36  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
37  import es.accv.arangi.base.exception.device.AliasNotFoundException;
38  import es.accv.arangi.base.exception.device.LoadingObjectException;
39  import es.accv.arangi.base.exception.device.SearchingException;
40  import es.accv.arangi.base.exception.document.HashingException;
41  import es.accv.arangi.base.exception.document.InitDocumentException;
42  import es.accv.arangi.base.exception.signature.NoDocumentToSignException;
43  import es.accv.arangi.base.exception.signature.SignatureException;
44  import es.accv.arangi.base.util.Util;
45  import es.accv.arangi.base.util.validation.ValidationResult;
46  
47  /**
48   * Clase que se encarga de realizar firmas en formato CMS de acuerdo a la
49   * <a href="http://tools.ietf.org/rfc/rfc3852.txt" target="rfc">RFC-3852</a>.
50   * 
51   * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
52   */
53  public class CMSSignature extends CMSPKCS7Signature{
54  
55  	/**
56  	 * Logger de la clase
57  	 */
58  	static Logger logger = Logger.getLogger(CMSSignature.class);
59  	
60  	/**
61  	 * OID para CMS
62  	 */
63  	public static final String OID_FORMATO_FIRMA = "1.2.840.113549.1.7.2";
64  	
65  	/**
66  	 * Obtiene la firma de un fichero.
67  	 * 
68  	 * @param fileSignature Fichero con la firma en formato CMS
69  	 * @throws IOException Error leyendo el fichero o la firma proporcionada no parece estar en formato DER
70  	 * @throws NormalizeCertificateException El certificado de la firma no puede ser normalizado
71  	 * 	al formato esperado por el proveedor criptográfico de Arangi
72  	 * @throws SignatureException Error construyendo la firma
73  	 */
74  	public CMSSignature(File fileSignature) throws NormalizeCertificateException, SignatureException, IOException {
75  		super(fileSignature);
76  	}
77  
78  	/**
79  	 * Obtiene la firma de un stream de lectura.
80  	 * 
81  	 * @param isSignature Stream de lectura a la firma en formato CMS
82  	 * @throws IOException Error leyendo el stream de lectura o la firma proporcionada no parece 
83  	 * 	estar en formato DER
84  	 * @throws NormalizeCertificateException El certificado de la firma no puede ser normalizado
85  	 * 	al formato esperado por el proveedor criptográfico de Arangi
86  	 * @throws SignatureException Error construyendo la firma
87  	 */
88  	public CMSSignature(InputStream isSignature) throws IOException,
89  			NormalizeCertificateException, SignatureException {
90  		super(isSignature);
91  	}
92  
93  	/**
94  	 * Obtiene la firma de un array de bytes.
95  	 * 
96  	 * @param signature Firma en formato CMS
97  	 * @throws NormalizeCertificateException El certificado de la firma no puede ser normalizado
98  	 * 	al formato esperado por el proveedor criptográfico de Arangi
99  	 * @throws SignatureException Error construyendo la firma
100 	 */
101 	public CMSSignature(byte[] signature) throws NormalizeCertificateException, SignatureException {
102 		super(signature);
103 	}
104 
105 	/**
106 	 * Construye un firma en formato CMS en base a los bytes de las firmas y
107 	 * los certificados con los que se realizaron éstas, con el algoritmo de 
108 	 * firma indicado. El documento se añadira a la firma (attached).
109 	 * 
110 	 * @param signatureBytes Bytes de la firma
111 	 * @param certificates Certificados con los que se realizó la firma
112 	 * @param digitalSignatureAlgorithms Algoritmos de firma
113 	 * @param document Documento que se ha firmado
114 	 * @throws SignatureException Error construyendo la firma
115 	 */
116 	public CMSSignature(byte[][] signatureBytes, Certificate[] certificates,
117 			IDocument document, String[] digitalSignatureAlgorithms) throws SignatureException {
118 		super(signatureBytes, certificates, document, digitalSignatureAlgorithms);
119 	}
120 
121 	/**
122 	 * Construye un firma en formato CMS en base a los bytes de las firmas y
123 	 * los certificados con los que se realizaron éstas, con el algoritmo de 
124 	 * firma por defecto (SHA1WithRSA). El documento se añadira a la firma 
125 	 * (attached).
126 	 * 
127 	 * @param signatureBytes Bytes de las firmas
128 	 * @param certificates Certificados con los que se realizó la firma
129 	 * @param document Documento que se ha firmado
130 	 * @throws SignatureException Error construyendo la firma
131 	 */
132 	public CMSSignature(byte[][] signatureBytes, Certificate[] certificates,
133 			IDocument document) throws SignatureException {
134 		super(signatureBytes, certificates, document);
135 	}
136 
137 	/**
138 	 * Construye un firma en formato CMS en base a los bytes de las firmas y
139 	 * los certificados con los que se realizaron éstas, con los algoritmos de 
140 	 * firma indicados. 
141 	 * 
142 	 * @param signatureBytes Bytes de las firmas
143 	 * @param certificates Certificados con los que se realizó la firma
144 	 * @param digitalSignatureAlgorithms Algoritmos de firma
145 	 * @throws SignatureException Error construyendo la firma
146 	 */
147 	public CMSSignature(byte[][] signatureBytes, Certificate[] certificates,
148 			String[] digitalSignatureAlgorithms) throws SignatureException{
149 		super(signatureBytes, certificates, digitalSignatureAlgorithms);
150 	}
151 
152 	/**
153 	 * Construye una firma en formato CMS en base a los bytes de las firmas y
154 	 * los certificados con los que se realizaron éstas, con el algoritmo de 
155 	 * firma por defecto (SHA1WithRSA).
156 	 * 
157 	 * @param signatureBytes Bytes de las firmas
158 	 * @param certificates Certificados con los que se realizó la firma
159 	 * @throws SignatureException Error construyendo la firma
160 	 */
161 	public CMSSignature(byte[][] signatureBytes, Certificate[] certificates)
162 			throws SignatureException {
163 		super(signatureBytes, certificates);
164 	}
165 	
166 	/**
167 	 * Comprueba que las firmas son correctas en firmas attached, sin validar los certificados 
168 	 * de las mismas.<br><br>
169 	 * 
170 	 * Las firmas CMS producidas por IExplorer tienen la particularidad de que, en un paso
171 	 * previo a la firma, se realiza una transformación del documento a formato 
172 	 * <a href="http://www.unicode.org/">Unicode</a>. En este método se probará la validación
173 	 * con el documento original y, si este se da por no válido, se probará con el documento
174 	 * en formato Unicode.<br><br>
175 	 * 
176 	 * IMPORTANTE: este método sólo puede ser utilizado si la firma es attached (el documento
177 	 * que originó la firma se incluye en ésta). Si no es así utilizar este mismo método pero 
178 	 * pasándole el documento que originó la firma.
179 	 * 
180 	 * @return Para cada certificado cierto si la firma es correcta
181 	 * @throws SignatureException Error tratando el objeto firma
182 	 * @throws HashingException Error obteniendo el hash del documento
183 	 * @throws NoDocumentToSignException La firma no es attached por lo que no hay documento con
184 	 * 	el que validarla. Utilizar este mismo método pero pasándole el documento que originó la
185 	 * 	firma
186 	 */
187 	public ValidationResult[] isValidSignatureOnly () throws HashingException, SignatureException, NoDocumentToSignException {
188 		if (this.document == null) {
189 			throw new NoDocumentToSignException ("La firma no es attached por lo que no se puede realizar la " +
190 					"validación si no se facilita el documento que originó la firma");
191 		}
192 		return isValidSignatureOnly(this.document);
193 	}
194 	
195 	/**
196 	 * Comprueba que las firmas son correctas, sin validar los certificados de las mismas.<br><br>
197 	 * 
198 	 * Las firmas CMS producidas por IExplorer tienen la particularidad de que, en un paso
199 	 * previo a la firma, se realiza una transformación del documento a formato 
200 	 * <a href="http://www.unicode.org/">Unicode</a>. En este método se probará la validación
201 	 * con el documento original y, si este se da por no válido, se probará con el documento
202 	 * en formato Unicode.
203 	 * 
204 	 * @param document Documento que originó la firma
205 	 * @return Para cada certificado cierto si la firma es correcta
206 	 * @throws SignatureException Error tratando el objeto firma
207 	 * @throws HashingException Error obteniendo el hash del documento
208 	 */
209 	public ValidationResult[] isValidSignatureOnly(IDocument document) throws HashingException, SignatureException {
210 		
211 		logger.debug ("[CMSSignature.isValidSignatureOnly]::Entrada::" + document);
212 		
213 		//-- Obtener la validación con el documento original
214 		ValidationResult[] result = super.isValidSignatureOnly(document);
215 		
216 		//-- Comprobar si todas las firmas son válidas
217 		boolean allValid = true;
218 		for (int i = 0; i < result.length; i++) {
219 			if (!result [i].isValid()) {
220 				allValid = false;
221 				break;
222 			}
223 		}
224 		if (allValid) {
225 			//-- Todo es válido, así que el documento no estaba en Unicode
226 			return result;
227 		}
228 		
229 		//-- Obtener el documento en Unicode
230 		File fileUnicode = null;
231 		ValidationResult[] result2;
232 		try {
233 			fileUnicode = Util.toUnicode(document.getInputStream());
234 			IDocument unicodeDocument = new FileDocument (fileUnicode);
235 			result2 = super.isValidSignatureOnly(unicodeDocument);
236 		} catch (InitDocumentException e) {
237 			// No se debe dar porque el fichero se creó en el método toUnicode
238 			throw new HashingException ("No existe el fichero temporal unicode", e);
239 		} finally {
240 			if (fileUnicode != null) {
241 				fileUnicode.delete();
242 			}
243 		}
244 		
245 		//-- Juntar los dos arrays
246 		for (int i = 0; i < result.length; i++) {
247 			if (!result [i].isValid() && result2[i].isValid()) {
248 				logger.debug ("[CMSSignature.isValidSignatureOnly]::La firma era válida en unicode");
249 				result[i] = result2[i];
250 			}
251 		}
252 		
253 		return result;
254 		
255 	}
256 
257 	/**
258 	 * Obtiene un objeto {@link CMSSignature CMSSignature}. Los arrays que se pasan como
259 	 * parámetro deben tener el mismo tamaño. Dado un elemento i del array de managers, se 
260 	 * debe corresponder con el elemento i de los alias.<br><br>
261 	 * 
262 	 * La firma se realizará con el algoritmo SHA1withRSA.
263 	 * 
264 	 * @param managers Dispositivos criptográficos
265 	 * @param alias Alias donde se encuentrann las claves privada dentro de los dispositivos
266 	 * @param document Documento a firmar
267 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
268 	 * 	realizar la firma no existe
269 	 * @throws HashingException No es posible obtener el hash del documento o su versión en 
270 	 * 	formato DER durante el proceso de firma
271 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
272 	 * 	realizar la firma
273 	 * @throws SignatureException Error durante el proceso de firma
274 	 * @throws SearchingException No se ha podido encontrar el certificado asociado a la clave 
275 	 * 	privada
276 	 * @throws NormalizeCertificateException No se puede normalizar el certificado de acuerdo
277 	 *  al proveedor criptográfico
278 	 */
279 	public static CMSSignature sign (DeviceManager[] managers, String[] alias, IDocument document, boolean isAttached) throws AliasNotFoundException, HashingException, LoadingObjectException, SignatureException, SearchingException, NormalizeCertificateException {
280 		String[] digitalSignatureAlgorithms = new String[managers.length]; 
281 		for (int i = 0; i < digitalSignatureAlgorithms.length; i++) {
282 			digitalSignatureAlgorithms[i] = DigitalSignatureAlgorithm.SHA1_RSA;
283 		}
284 		return sign(managers, alias, document, digitalSignatureAlgorithms, isAttached);
285 	}
286 	
287 	/**
288 	 * Obtiene un objeto {@link CMSSignature CMSSignature}. Los arrays que se pasan como
289 	 * parámetro deben tener el mismo tamaño. Dado un elemento i del array de managers, se 
290 	 * debe corresponder con el elemento i de los alias y de los algoritmos de firma
291 	 * 
292 	 * @param managers Dispositivos criptográficos
293 	 * @param alias Alias donde se encuentrann las claves privada dentro de los dispositivos
294 	 * @param document Documento a firmar
295 	 * @param digitalSignatureAlgorithms Algoritmos de firma a utilizar
296 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
297 	 * 	realizar la firma no existe
298 	 * @throws HashingException No es posible obtener el hash del documento o su versión en 
299 	 * 	formato DER durante el proceso de firma
300 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
301 	 * 	realizar la firma
302 	 * @throws SignatureException Error durante el proceso de firma
303 	 * @throws SearchingException No se ha podido encontrar el certificado asociado a la clave 
304 	 * 	privada
305 	 * @throws NormalizeCertificateException No se puede normalizar el certificado de acuerdo
306 	 *  al proveedor criptográfico
307 	 */
308 	public static CMSSignature sign (DeviceManager[] managers, String[] alias, IDocument document, String[] digitalSignatureAlgorithms, boolean isAttached) throws AliasNotFoundException, HashingException, LoadingObjectException, SignatureException, SearchingException, NormalizeCertificateException {
309 		
310 		logger.debug ("[CMSSignature.sign]::Entrada::" + Arrays.asList(new Object[] { managers, alias, document, digitalSignatureAlgorithms, new Boolean (isAttached) }));
311 		
312 		//-- Obtener la lista de bytes de firma y certificados con los que se realizan
313 		ArrayList alSignatureBytes = new ArrayList ();
314 		ArrayList alCertificates = new ArrayList ();
315 		for (int i = 0; i < managers.length; i++) {
316 			//-- Obtener la firma
317 			alSignatureBytes.add(managers[i].signDocument(document, alias[i]));
318 			
319 			//-- Obtener el certificado
320 			try {
321 				alCertificates.add (new Certificate (managers[i].getCertificate(alias[i])));
322 			} catch (NormalizeCertificateException e) {
323 				logger.info("[CMSSignature.sign]::El certificado de la firma no ha podido ser normalizado a un formato reconocido " +
324 						"por el proveedor criptográfico de Arangi ", e);
325 				throw new SignatureException ("El certificado de la firma no ha podido ser normalizado a un formato reconocido por el " +
326 						"proveedor criptográfico de Arangi ", e);
327 			}
328 		}
329 		byte[][] signaturesBytes = (byte[][])alSignatureBytes.toArray(new byte[0][0]);
330 		Certificate [] certificates = (Certificate[]) alCertificates.toArray (new Certificate[0]);
331 		
332 		//-- Obtener la firma CMS
333 		byte[] cmsSignature;
334 		if (isAttached) {
335 			cmsSignature = createPKCS7CMS (signaturesBytes, certificates, document, digitalSignatureAlgorithms, OID_FORMATO_FIRMA);
336 		} else {
337 			cmsSignature = createPKCS7CMS (signaturesBytes, certificates, null, digitalSignatureAlgorithms, OID_FORMATO_FIRMA);
338 		}
339 		return new CMSSignature (cmsSignature);
340 	}
341 
342 	/*
343 	 * (non-Javadoc)
344 	 * @see es.accv.arangi.base.signature.CMSPKCS7Signature#getOIDFormatoFirma()
345 	 */
346 	protected String getOIDFormatoFirma() {
347 		return OID_FORMATO_FIRMA;
348 	}
349 	
350 	/**
351 	 * Devuelve una cadena de texto con el tipo de la firma
352 	 * 
353 	 * @return Cadena de texto con el tipo de la firma
354 	 */
355 	public String getSignatureType () {
356 		return "CMS";
357 	}
358 }