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 }