Firma digital

Las clases que implementan los tipos de firma se encuentran en es.accv.arangi.signature.

Los tipos de firma implementados son:

  • CMS: clase CMSSignature.
  • PKCS#7: clase PKCS7Signature.
  • PAdES-LTV: clase PDFSignature.
  • XAdES: clases XADESBESSignature, XADESTSignature y XADESXLSignature.
  • Token de validación ACCV: clase ACCVValidationToken. Este tipo de firma se mantiene por motivos de compatibilidad con lo ofrecido por IDEAS. Se recomienda el uso de XAdES como alternativa.

Todas las clases contienen métodos para firmar, validar y obtener información de la firma.

Como ejemplo de utilización de estos métodos se usará la firma CMS.

Firmar con CMSSignature

La firma supone llamar al método estático sign de la clase correspondiente. Como resultado se obtiene un objeto de la clase.

KeyStoreManager [] managers = new KeyStoreManager [1];
managers[0] = new KeyStoreManager (new File("c:/temp/uactivo951v_firma.p12"), "1234");
InputStreamDocument document = new InputStreamDocument (new FileInputStream ("c:/temp/documento.txt"));

CMSSignature signature = CMSSignature.sign(managers, document);
signature.save(new File ("c:/temp/firma.cms"));

En primer lugar se obtiene un manager de dispositivo (en el ejemplo se trata de un fichero PKCS#12). Si se pasan varios managers de dispositivo se obtendrá una firma múltiple.

Después hay que obtener el documento a firmar (en el ejemplo es un fichero de disco). Finalmente la llamada a sign devuelve un objero CMSSignature que puede ser guardado en disco.

Validar la firma

La validación de una firma supone realizar dos acciones:

  • Validar que la firma se corresponda con el documento (concepto de integridad).
  • Validar que el certificado con el que se firmó era válido en el momento de la firma.

Se ofrecen dos métodos para realizar la primera validación:

  • isValidSignatureOnly(): valida que la firma se corresponda con el documento para firmas attached (que contienen el documento dentro de la firma)
  • isValidSignatureOnly(IDocument): valida que la firma se corresponda con el documento para firmas detached (el documento se pasa como parámetro al método porque no se encuentra dentro de la firma).

Y cuatro métodos que realizan la validación completa (validación de la integridad y de la validez del certificado):

  • isValid(): realiza la validación completa para firmas attached (para firmas realizadas con certificados de la ACCV o del DNIe).
  • isValid(IDocument): realiza la validación completa para firmas detached (para firmas realizadas con certificados de la ACCV o del DNIe).
  • isValid(ListCertificateValidationService): realiza la validación completa para firmas attached utilizando una lista de servicios web para validar los certificados.
  • isValid(IDocument, ListCertificateValidationService): realiza la validación completa para firmas detached utilizando una lista de servicios web para validar los certificados.

Puede ver cómo se utilizan los servicios de validación en el apartado Validación de certificados.

Ejemplo:

CMSSignature signature = new CMSSignature (ClassLoader.getSystemResourceAsStream("signature/signature.cms"));

ValidationResult[] isValidSignatures = signature.isValidSignatureOnly();
ValidationResult[] isValidSignaturesCertificates = signature.isValid();

for(int i=0;i<isValidSignaturesCertificates.length;i++) {
        System.out.println(i + ". Firma valida: " + isValidSignaturesCertificates[i].isValid());
        System.out.println(i + ". Resultado validación: " + isValidSignaturesCertificates[i].getResult());
        if (isValidSignaturesCertificates[i].getResult() == ValidationResult.RESULT_CERTIFICATE_REVOKED) {
                System.out.println(i + ". El certificado de la firma está revocado");
        }
}

CertificateValidationService servicio1 = ...
List<CertificateValidationService> lServicios = new ArrayList<CertificateValidationService>();
lServicios.add(servicio1);

isValidSignaturesCertificates = signature.isValid(lServicios);

for(int i=0;i<isValidSignaturesCertificates.length;i++) {
        System.out.println(i + ". Resultado validación: " + isValidSignaturesCertificates[i].getResult());
        if (isValidSignaturesCertificates[i].getResult() == ValidationResult.RESULT_CERTIFICATE_REVOKED) {
                System.out.println(i + ". El certificado de la firma está revocado");
        }
}

En el ejemplo puede verse como el resultado de la validación es un array de objetos es.accv.arangi.base.util.validation.ValidationResult de la librería base de Arangí, uno por cada firma contenida en el CMS. Para conocer el resultado de la validación podemos llamar al método getResult() que nos devolverá un entero indicando si la validación ha sido satisfactoria o la razón por la que no lo ha sido. El valor del entero viene descrito por las constantes de la clase:

  • ValidationResult.RESULT_VALID:
  • ValidationResult.RESULT_CERTIFICATE_NOT_BELONGS_TRUSTED_CAS: El certificado no pertenece a la lista de CAs de confianza.
  • ValidationResult.RESULT_CERTIFICATE_NOT_ACTIVE: El certificado no ha comenzado su periodo de validez o ya ha expirado.
  • ValidationResult.RESULT_CERTIFICATE_REVOKED: El certificado está revocado.
  • ValidationResult.RESULT_CERTIFICATE_UNKNOWN: Resultado de la validación: el certificado es desconocido.
  • ValidationResult.RESULT_CERTIFICATE_CANNOT_BE_VALIDATED: El certificado no puede ser validado.
  • ValidationResult.RESULT_CERTIFICATE_CHAIN_VALIDATION_INVALID: La cadena de confianza del certificado no es válida.
  • ValidationResult.RESULT_SIGNATURE_NOT_MATCH_DATA: Los datos no se corresponden con el hash de la firma.
  • ValidationResult.RESULT_REFERENCE_DIGEST_NOT_MATCH_DATA: Alguno de los hash de las referencias que luego se firman no se corresponde con la parte del XML a que hacen referencia.
  • ValidationResult.RESULT_INVALID: El resultado no es válido.
  • ValidationResult.RESULT_TIMESTAMP_INVALID: El sello de tiempos no es válido.
  • ValidationResult.RESULT_INVALID_VALIDITY_ITEM: El item de validación (respuesta OCSP o CRL) no es válido.
  • ValidationResult.RESULT_TIMESTAMP_AFTER_VALIDITY_ITEM: La fecha de fin de validez del item de validación (respuesta OCSP o CRL) es anterior a la fecha del sello de tiempos de la firma

Si sólo se quiere saber si la firma es válida o no se puede usar el método isValid() que devuelve un boolean. Además la clase ofrece métodos para obtener la fecha de la firma, el certificado firmante, el sello de tiempos y las respuestas OCSP asociadas.

La validación es exactamente igual para cualquier tipo de firma. Si en este ejemplo cambiasemos el tipo de firma para trabajar por ejemplo con XAdES-BES, el código para validar sería igualmente válido.

Validación genérica de firmas

Lo normal es que al validar una firma se sepa el tipo de firma con el que se está trabajando. Sin embargo, en ciertas ocasiones, puede ser necesario trabajar con diferentes tipos de firma. Arangí disponde de varios métodos estáticos en la clase es.accv.arangi.base.signature.Signature que permiten realizar la validación de un fichero de firma sin que a priori se sepa de qué tipo de firma se trata:

  • validateSignature: realiza la validación completa de una firma. Se puede llamar con distintos parámetros dependiendo de si se desea que la validación de los certificados se realice mediante CAList y/o mediante servicios de validación.
  • validateSignatureOnly: realiza la validación simple de una firma.

    Dadas dos firmas detached sobre el mismo documento, una es un CMS y otra un XAdES-BES, se podría hacer la validación de ambas de la siguiente forma:

    IDocument document = new InputStreamDocument (ClassLoader.getSystemResourceAsStream("document/document.txt"));
    byte[] signature1 = Util.readStream (ClassLoader.getSystemResourceAsStream("signature/cms.cms"));
    byte[] signature2 = Util.readStream (ClassLoader.getSystemResourceAsStream("signature/xades-bes.xml"));
    
    List<CertificateValidationService> validationServices = ...
    CAList caList = ...
    
    ValidationResult[] isValidSignatures1 = Signature.validateSignature (document, signature1, validationServices, caList);
    ValidationResult[] isValidSignaturesCertificates2 = Signature.validateSignature(document, signature1);
    
    ValidationResult[] isValidSignatures2 = Signature.validateSignature (document, signature2, validationServices, caList);
    ValidationResult[] isValidSignaturesCertificates2 = Signature.validateSignature(document, signature2);