Ya hemos hablado anteriormente sobre cómo empezar a utilizar certificados digitales en un entorno de desarrollo de programación, ahora vamos a ver la primera aplicación real con éstos certificados.
Firma digital de documentos PDF
La firma verifica la autoría y validez de una persona, por lo que es especialmente útil cuando hablamos de PDF que contienen facturas o documentos oficiales.
También lo podemos usar para validar un certificado en entornos Web de manera transparente al navegador. Quien lo haya hecho, sabrá que si queremos usar certificados en una aplicación web, hacerlo funcionar en todos los navegadores web puede causar bastantes dolores de cabeza, pero una sencilla manera es firmar un PDF dummy mediante CryptoApplet y validar si esa firma es válida, pero este procedimiento ya lo trataremos más adelante en otras entradas de blog.
Creación y modificación de PDFs con la librería iText
La librería iText disponible para Java y .NET permite crear y modificar PDFs dinámicamente desde un entorno de programación. En nuestro caso vamos a descargarnos las librerías OpenSource y añadirlas a nuestro proyecto Java, en nuestro caso hemos utlizado NetBeans.
Creando PDFs dinámicamente mediante PHP
En anteriores entradas del blog ya tratamos la manera de cómo crear PDFs dinámicamente mediante la librería mPDF, que podría ser interesante para esta aplicación.
Preparando nuestro certificado digital
El paso previo es tener preparado nuestro certificado digital. Éste debe estar en formato con extensión PFX (estándar PKCS12), que en el caso de no tener ninguno disponible, ya hemos explicado anteriormente en el artículo cómo crear certificados digitales como crear uno.
Un paso previo muy importante, a la finalización de nuestro proyecto, es añadir la autoridad certificadora a nuestra KeyStore de Java. En el caso de seguir el artículo de blog será obligado hacerlo si queremos que funcione correctamente, al igual que si por ejemplo queremos utilizar un certificado digital del DNIe, ya que el KeyStore de Java sólo contempla los típicos usados para HTTPS y algunos otros como Verisign.
Podéis encontrar más información de cómo añadir autoridades certificadoras a nuestro KeyStore de Java en este artículo de blog.
Firmando PDFs en Java
Crearemos el proyecto en NetBeans PDFSigner y pegaremos en el Main.java el siguiente código:
package pdfsigner; import java.security.*; import java.security.cert.Certificate; import java.io.*; import com.lowagie.text.pdf.*; import com.lowagie.text.*; /** * @author Imaginanet */ public class Main { public static void main(String[] args) { try { if(args.length == 0) { System.out.print("Necesito el nombre del PDF a firmar"); System.exit(1); } KeyStore ks = KeyStore.getInstance("pkcs12"); ks.load(new FileInputStream("RUTA_CERTIFICADO_PFX"), "CLAVE_PRIVADA_CERTIFICADO".toCharArray()); String alias = (String)ks.aliases().nextElement(); PrivateKey key = (PrivateKey)ks.getKey(alias, "CLAVE_PRIVADA_CERTIFICADO".toCharArray()); Certificate[] chain = ks.getCertificateChain(alias); // Recibimos como parámetro de entrada el nombre del archivo PDF a firmar PdfReader reader = new PdfReader(args[0]); FileOutputStream fout = new FileOutputStream("RUTA_ARCHIVO_PDF_FIRMADO"); // Añadimos firma al documento PDF PdfStamper stp = PdfStamper.createSignature(reader, fout, '?'); PdfSignatureAppearance sap = stp.getSignatureAppearance(); sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED); sap.setReason("Firma PKCS12"); sap.setLocation("Imaginanet"); // Añade la firma visible. Podemos comentarla para que no sea visible. sap.setVisibleSignature(new Rectangle(100,100,200,200),1,null); stp.close(); } catch(Exception e) { e.printStackTrace(); } } }
Donde esperamos como parámetro de entrada al programa la ruta al archivo PDF original sin firmar y modificar las rutas al certificado digital PFX y la clave privada de éste.
Verificando firmas en PDFs desde Java
Ahora comprobaremos la vericidad de la firma con otro proyecto NetBeans PDFVerifier y colocaremos en su Main.java:
package pdfverifier; import java.security.*; import java.security.cert.Certificate; import java.io.*; import com.lowagie.text.pdf.*; import java.util.Calendar; import java.util.ArrayList; import java.util.Random; /** * @author Imaginanet */ public class Main { public static void main(String[] args) { try { if(args.length == 0) { System.out.print("Necesito el nombre del PDF a comprobar"); System.exit(1); } Random rnd = new Random(); KeyStore kall = PdfPKCS7.loadCacertsKeyStore(); PdfReader reader = new PdfReader(args[0]); AcroFields af = reader.getAcroFields(); ArrayList names = af.getSignatureNames(); for (int k = 0; k < names.size(); ++k) { String name = (String)names.get(k); int random = rnd.nextInt(); FileOutputStream out = new FileOutputStream("revision_" + random + "_" + af.getRevision(name) + ".pdf"); byte bb[] = new byte[8192]; InputStream ip = af.extractRevision(name); int n = 0; while ((n = ip.read(bb)) > 0) out.write(bb, 0, n); out.close(); ip.close(); PdfPKCS7 pk = af.verifySignature(name); Calendar cal = pk.getSignDate(); Certificate pkc[] = pk.getCertificates(); Object fails[] = PdfPKCS7.verifyCertificates(pkc, kall, null, cal); if (fails == null) { System.out.print(pk.getSignName()); } else { System.out.print("Firma no válida"); } File f = new File("revision_" + random + "_" + af.getRevision(name) + ".pdf"); f.delete(); } } catch(Exception e) { e.printStackTrace(); } } }
Al igual que el anterior programa, necesita como parámetro de entrada el archivo PDF a comprobar, mostrando en caso de ser verificada la firma los datos del propietario y en caso contrario el mensaje de error "Firma no válida".
Llamada a los programas desde una aplicación
Hemos separado ambos programas para que actúen como programas como cajas negras y sea independiente del lenguaje de programación del que se llame.
En el caso de firmar, llamaremos como java pdfsigner archivo.pdf generando un archivo pdf firmado.
Tras ello, podemos vertificar la firma con java pdfverifier firmado.pdf mostrando la salida de la firma en caso positivo. Ya que la firma puede guardarse en varios formatos según el certificado PFX, recomendamos que esa salida hagamos el parseo necesario desde nuestra aplicación buscando datos que estemos seguros que vayan a salir en la firma, como es el caso del DNI, CIF, dirección email, etc.
Comunicando nuestro programa de firma digital de PDF con aplicaciones PHP
Una de las aplicaciones más interesantes es la de usar esta aplicación de firma con PHP. Las librerías que permiten hacer ésto en PHP no son libres, pero sin embargo podemos llamar a nuestras aplicaciones Java mediante passthru, exec, etc. permitiendo tener firma digital open source en PHP.
Comentarios