Validando una firma digital en documentos PDF desde una aplicación en PHP

En este tutorial, se verá la implementación de validación de firma digital en documentos PDF con firma desde una aplicación escrita en PHP. Para este propósito, es necesario realizar las siguientes validaciones:

  • Verificar la autenticidad del documento.- Este paso consiste en comprobar que el documento firmado no haya sido modificado después de la firma o que el mismo sea una falsificación. Esto se logra a partir de la generación de un HASH (código que identifica al documento de manera única, al igual que una huella digital identifica a una persona). La firma digital se constituye en el HASH del documento que se está firmando encriptado con la clave privada del signatario.
    Para validar la autenticidad del documento, se debe desencriptar la firma con la clave pública y comparar el HASH obtenido con un nuevo HASH calculado a partir del documento.
    - Si no se puede desencriptar la firma, significa que no corresponde al propietario de la clave pública.
    - Si el HASH obtenido de la firma no corresponde al HASH del documento, significa que el documento fue modificado.
  • Verificar la propiedad y legalidad de la clave pública.- Una vez verificado el paso anterior podemos estar seguros de que el documento fue firmado por el propietario del par de claves y que el mismo no fue modificado después de la firma. Sin embargo, también es necesario identificar al propietario del par de claves. Esto se logra a través del certificado de firma digital, que contiene los datos de identidad y clave pública del signatario y además está firmado por una ECA (Entidad Certificadora Autorizada) que para nuestro caso es ADSIB.
    Revisando el certificado se puede obtener la identidad del signatario, pero para estar seguros de que los mismos no fueron falseados, se debe verificar la firma de la ECA en el certificado, en este caso con la clave pública de la ADSIB.
    La firma del certificado se verifica como en el paso anterior, pero utilizando certificado de firma digital de la ADSIB, que a su vez contiene la clave pública y se encuentra en: Certificado ADSIB
  • Verificar la vigencia del certificado.- Hasta este punto ya se tiene la certeza de que el documento fue firmado por el propietario del par de claves identificado a través del certificado de firma digital. Sin embargo, para evitar que por ejemplo un tercero firme documentos a nombre de un difunto, se tienen políticas que limitan la vida útil del certificado a un tiemo determinado.
    Por este motivo, es necesario además verificar que el documento fue firmado dentro del periodo de vigencia del certificado, simplemente comparando la fecha en la que se firmó el documento y el periodo de vigencia del certificado.
  • Verificar la revocación del certificado.- Adicionalmente también es posible que el propietario del par de claves pierda el Token encargado de resguardar su par de claves o el mismo sea robado, en cuyo caso lo más aconsejable es reportar este hecho a la ECA. Una vez reportado el hecho, la ECA publica el estado del certificado como REVOCADO, invalidando cualquier firma posterior a ocurrido el hecho.
    Finalmente entonces será necesario revisar la lista para verificar que el certificado no haya sido revocado.

En el caso de los archivos PDF, se maneja el contenido por secciones, ya que si comparamos el documento original con el firmado, serán diferentes por el contenido de la firma. Sin embargo el contenido propiamente del documento no cambia.

Por lo anterior, el cálculo del HASH se realiza sobre el contenido del PDF como se explica en el siguiente enlace: PDF

La manipulación de un PDF no es parte de este tutorial, por lo que se utilizará la librería publicada en: php-pdfsig, que está basada en PoDoFo.

Si no tiene instalado PHP y el compilador de c, puede utilizar la siguiente instrucción:

  1. sudo apt-get install build-essential php7.0 php7.0-dev git

También es requerida la librería de podofo que se puede instalar de la siguiente manera:

  1. curl https://sistemas.adsib.gob.bo/nextcloud/index.php/s/eMXjic45493Strp/download --output /tmp/libpodofo-dev.deb
  2. sudo dpkg -i /tmp/libpodofo-dev.deb
  3. sudo apt install -f
  4. sudo ln -s /usr/lib/libpodofo.so /usr/lib/libpodofo.so.0.9.6

Una vez instalado, ejecutar las siguientes instrucciones para descargar la librería, compilarla y activarla en PHP:

  1. git clone https://gitlab.softwarelibre.gob.bo/adsib/php-pdfsig.git
  2. cd php-pdfsig
  3. cd pdfsig
  4. ./make
  5. cd ..
  6. ./make
  7. sudo cp lib/php_pdfsig.so /usr/lib/php/20151012/
  8. sudo nano /etc/php/7.0/apache2/php.ini

Con los pasos anteriores en la consola de comandos, se abrirá un editor de texto con el contenido de php.ini, se deberá agregar la siguiente línea:

  1. ...
  2. extension=php_pdfsig.so
  3. ...

Con este último paso queda configurada la librería libpdfsig que será utilizada para obtener las firmas del documento PDF, a continuación se reinicia el servidor apache2 y se crea el proyecto de validación:

  1. sudo service apache2 restart
  2. sudo mkdir /var/www/html/validar
  3. sudo nano /var/www/html/validar/index.php

Con los pasos anteriores en la consola de comandos, se abrirá un editor de texto donde se puede copiar el siguiente programa:

  1. $json = json_decode(pdfsig_php('./prueba.pdf'));
  2. echo "<b>Nro de firmas: ".count($json)."</b></br></br>";
  3. for ($i = 0; $i < count($json); $i++) {
  4. echo "<b>Estado:</b> ".$json[$i]->estadoFirma."</br>";
  5. echo "<b>Fecha:</b> ".$json[$i]->fechaFirma."</br>";
  6. }

La primera línea recupera el pdf y la información inicial del documento. Una vez hecho esto ya se puede verificar las firmas, es así que la línea 3 obtienen el número de firmas en el documento y muestran este valor en la página. Finalmente se verifica la autenticidad del documento en la línea 6 con el estado de la firma y se muestra el mensaje correspondiente, la línea 7 muestra la fecha de la firma.

Para ejecutar el ejemplo anterior se requiere un archivo pdf firmado que se puede descargar de la siguiente url: PDF para ejecutar el ejemplo, una vez guardado el archivo index.php editado en el paso anterior, se puede ingresar desde un navegador a la URL:

  1. http://localhost/validar/

La librería utilizada retorna admás de algunos datos el certificado del cual se podrá extraer la información adicional requerida:

  1. ...
  2. echo "Usuario: ".$json[$i]->certificados[0]->nombre."</br>";
  3. $str = $json[$i]->certificados[0]->certificado;
  4. $ssl = openssl_x509_parse($str);
  5. echo "CI: ".getOID('1.3.6.1.1.1.1.0', $ssl['name'])."</br>";
  6. ...

El código completo se muestra a continuación:

  1. <?php
  2. function format($fecha) {
  3. $year = substr($fecha, 2, 4);
  4. $month = substr($fecha, 6, 2);
  5. $day = substr($fecha, 8, 2);
  6. $hour = substr($fecha, 10, 2);
  7. $min = substr($fecha, 12, 2);
  8. $seg = substr($fecha, 14, 2);
  9. return $year.'-'.$month.'-'.$day.' '.$hour.':'.$min.':'.$seg;
  10. }
  11. function getOID($OID, $ssl) {
  12. preg_match('/\/' . $OID . '=([^\/]+)/', $ssl, $matches);
  13. return $matches[1];
  14. }
  15. $json = json_decode(pdfsig_php('./prueba.pdf'));
  16. echo "<b>Nro de firmas: ".count($json)."</b></br></br>";
  17. for ($i = 0; $i < count($json); $i++) {
  18. echo "<b>Estado:</b> ".$json[$i]->estadoFirma."</br>";
  19. echo "<b>Fecha:</b> ".format($json[$i]->fechaFirma)."</br>";
  20. echo "<b>Usuario:</b> ".$json[$i]->certificados[0]->nombre."</br>";
  21. $str = $json[$i]->certificados[0]->certificado;
  22. $ssl = openssl_x509_parse($str);
  23. echo "<b>CI:</b> ".getOID('1.3.6.1.1.1.1.0', $ssl['name'])."</br>";
  24. }

Si desea descargar el ejemplo puede hacerlo a través del siguiente link: Código de ejemplo

Si desea crear un contenedor Docker puede hacerlo a través del siguiente link: Dockerfile

© ADSIB 2019 Bolivia