lunes, 15 de diciembre de 2008

Cantidad de Visitas al Articulo:

Firma Digital en PDF

Firma_PDF Días atrás estuve probando las firmas digitales en PDF. Hacía buen tiempo que no trabajaba en temas relacionados con PKI, y me interesé en el tema debido a un artículo de un diario español, donde decían que un sistema de notificaciones a infractores de tránsito sería implementado con PDF firmado digitalmente.

Lo primero fue descargar las herramientas necesarias y luego, hacer un ejemplo básico con un certificado autogenerado. Aquí detallo la problemática, las herramientas utilizadas y los ejemplos desarrollados, para que sirva de punto de partida, si alguien necesita implementar algo del estilo.

Herramientas Utilizadas

Las herramientas que utilicé son:

Ejemplo

El proceso es el siguiente:

  1. Se genera con o lee un archivo PDF.
  2. Se carga un certificado digital en un formato PKCS12.
  3. Se genera un objeto de firma.
  4. Se lo inserta dentro del documento PDF original.

El código sería algo como:

import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;

public boolean sign(String pathpdf, String pathPKCS12, String passwordPKCS12) {
String fileKey = pathPKCS12 ;
String fileKeyPassword = passwordPKCS12 ;
try {
//Crea un KeyStore
KeyStore ks = KeyStore.getInstance("pkcs12");
//Carga Certificado
ks.load(new FileInputStream(fileKey), fileKeyPassword.toCharArray());
String alias = (String)ks.aliases().nextElement();
//Recupera Clave Privada
PrivateKey key = (PrivateKey)ks.getKey(alias, fileKeyPassword.toCharArray());
//Recupera Cadena de Certificacion si existe
Certificate[] chain = ks.getCertificateChain(alias);
//Lee Documento PDF y crea archivo de salida con otro nombre para no pisar el original
PdfReader pdfReader = new PdfReader((new File(pathpdf)).getAbsolutePath());

File outputFile = new File(pathpdf + ".signed.pdf");
// Crea la firma en el objeto PdfStamper de la librería iText
PdfStamper pdfStamper;
pdfStamper = PdfStamper.createSignature(pdfReader, null, '\0', outputFile);
PdfSignatureAppearance sap = pdfStamper.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);
sap.setReason("Test GGS");
sap.setLocation("");
// Posiciona la Firma
sap.setVisibleSignature(new Rectangle(10, 10, 50, 30), 1, "sign_ggs");
pdfStamper.setFormFlattening(true);
pdfStamper.close();
}
catch (Exception key) {
key.printStackTrace();
return false;
}
return true;
}

Pendientes

Lo que quedaría pendiente, y si alguien lo completa les agradecería incluya un comentario es:

  1. Probar con versiones diferentes de JRE.
  2. Investigar en versiones anteriores de iText si ya estaba esta característica incluida.

So far, so good!

Imágenes by Tinypic

Cantidad de Visitas al Articulo:

14 comentarios:

Anónimo dijo...

Hi,

Nice blog. Your header image the layout is good.

Good blog and Keep it up.

:-)
Insurance Agent

Juan dijo...

Hola,
Estoy perdido con la autentificación con certificados. En el pdf parece que del certificado usas el chain y el key para firmar. ¿Con eso es suficiente para realizar la autentificacion?. Si es así, el que debe autentificar, ¿Como autentifica usando el chain y el key?.
Perdona si me ves demasiado verde, pero no encuentro nada que me aclare.
Gracias de antemano.

Gustavo Suhit dijo...

Juan,
Es sustancialmente diferente la firma de la autenticación. En la autenticación podrías considerar diferentes formas, desde comparar por igualdad el certificado presentado, contra uno almacenado en la base de datos (previamente cargado en una configuración de usuarios común) hasta utilizar algún servicio de Online Certificate Status Protocol (OCSP). Algunas de las CA que emiten certificados tienen su sistema de verificación en línea, bajo este standard, y hay empresas como Tumbleweed, que tienen productos como el Validation Authority, que centraliza varias listas de revocaciones (CRL) y permite hacer verificación de estado en línea de un certificado. Claro está, que la verificación en línea del certificado no hace que sepas exactametne quien lo está presentando, a no ser que hagas además la correlación de un usuario de tu sistema con el certificado.
Si lo único que deseas es que se presente un certificado, lo que puedes hacer es tomar los datos que deseas del mismo (Nombre, Compañía, Serial ID del certificado, etc) y almacenarlo para tener referencia de quien ingresó a la página.
Con respecto a sitios, para autneticar via certificados, debes configurarlo. Un link para empezar podría ser: http://www.liferay.com/community/forums/-/message_boards/message/4087024, aunque en cada uno de los foros de los servicores web encontrarás información al respecto.
Espero sea de utilidad,
Saludos,

Juan dijo...

Gracias por responder tan rápido.
Lo que me pidieron exactamente es usar watermarking en una imagen para introducir la autentificacion del usuario y saber que la imagen es de ese usuario, y eso usando un certificado. Además en un principio me pidieron que fuera autosuficiente la aplicación (por lo que supongo que me llega con saber quien la envio). Mi duda es saber que debo enviar, porque no creo que sea muy seguro enviar todo el certificado.
Gracias de nuevo.

Gustavo Suhit dijo...

Juan, si entiendo bien, debes poner la foto de na persona con una marca de agua con información proveniente de un certificado. Si es eso, debes hacer una administración de usuarios, donde identifiques al mismo, a traves de un userid por ejemplo, unico en el sistema, y le asocies una imagen mas un certificado. Luego, cuando lo presente, puedes extraer del certificado lo que necesites y generar la marca.
Es seguro enviar todo el certificado, dado que no envías la clave privada. El certificado contiene la clave pública, pero como su palabra lo dice, es pública, y no genera un problema de seguridad.
Saludos,

Juan dijo...

Gracias de nuevo,
Creo que me explique mal. Se puede decir que la aplicación se divide en 2. Con una un usuario puede introducir su certificado( por watermarking) a una imagen que tiene que enviar. La otra parte se encarga de ver quien le envió una imagen (Se supone que se la envia el que usa la parte 1). Pero como me dijiste que podía enviar sin problemas todo el certificado, será lo que haga. Se introducirá en la imagen el ".p12" y la contraseña con la que se creo el p12.
Muchas gracias.
pd:Si se te ocurre alguna manera de evitar el envio de la contraseña con la que se creó el .p12 será bien aceptada.

Gustavo Suhit dijo...

Juan,
cuando me refería a enviar el certificado, no me refería a .p12 porque alli pierdes absolutamente todo el control de seguridad. La idea es que busques un mecanismo donde no se deba enviar la clave.
Que pasa si lo que se envia es un XML con la imagen (en base64 por ejemplo) y la misma firmada y/o encriptada con firma a través de un certificado. cuando usas el standard XMLDSIG, puedes configurar para que en la firma se envie la clave publica con la cual verificar el contenido del XML firmado, y asi te aseguras que la imagen enviada no fue modificada. Luego la extraes y muestras todo lo que que queiras, pero no se viola ningunanorma de seguridad.
Quiza no aplique esta solucion, pero no conozco el contexto general de la aplicación.
Saludos,

Juan dijo...

Hola,
Creo que voy a preguntar exactamente que es lo que quiere que haga para esta parte, ya que esto último que me comentas me parece más adecuado para lo que tiene que hacer.
Muchas gracias,
Nos vemos

Anónimo dijo...

Gustavo quisiera por favor que puedas explicar de como crear el certificado con extension pfx

Gustavo Suhit dijo...

Anónimo,
Una forma fácil, si tienes un certificado instalado en tu pc, desde el internet explorer, accedes a Herramientas > Opciones de Internet > Contenido > Certificados, Luego seleccionas el que deseas exportar a pfx, presionas Exportar, y sigues el wizard. Cuando pregunte si exportas la clave privada, selecciona que si, luego pide una clave, y el path para poner el nombre del archivo donde se generará el pfx. Das ok y se genera.
Hay otras formas con programación, pero esta es la mas fácil.

Saludos,

Carlos dijo...

Hola, disculpa el atrevimiento, pero es algo relacionado, en mi caso no tengo problemas con la firma digital, sino con los permisos, necesito generar un documento con firma digital y que solo tenga permisos de impresión. El problema es que no puedo, ya intente con el código, pero siempre que genero la firma aún en un documento que solo tiene permisos de impresión por contraseña, el documento que genera el stamper siempre tiene otros permisos y no encuientro como incluir la firma desde el writer. Alguna sugerencia.

MaNu dijo...

Hola, he seguido es manual tal cual pero no me funciona :( al ejecutarlo me da un error.. únicamente falla en la línea "pdfStamper.close();" si comento esa línea no me da error, lo que me crea un pdf pero que ocupa 0kb y si intento abrirlo me dice que el archivo está dañado.
Este es el error que me aparece si no comento pdfStamper.close() :

Exception in thread "main" java.lang.NoClassDefFoundError: org/bouncycastle/asn1/DEREncodable
at com.lowagie.text.pdf.PdfSignatureAppearance.getAppearance(Unknown Source)
at com.lowagie.text.pdf.PdfSignatureAppearance.preClose(Unknown Source)
at com.lowagie.text.pdf.PdfSignatureAppearance.preClose(Unknown Source)
at com.lowagie.text.pdf.PdfStamper.close(Unknown Source)
at FirmaDigital.FirmaDigital.Firmar(FirmaDigital.java:63)
at ConvPDF.Principal.main(Principal.java:48)
Caused by: java.lang.ClassNotFoundException: org.bouncycastle.asn1.DEREncodable
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 6 more
Java Result: 1

Gracias de antemano.

Unknown dijo...

Hola, pude utilizar el código que indicas y el único inconveniente es que el archivo firmado en pdf sale con la firma digital pero con una imagen de interrogante e indicando validez desconocida.
Se tiene que tener en cuenta otras lineas de código.
he utilizado las librerías que indicas , pero no he tenido suerte.
te agradeceré si me indicas que es lo que tengo que tener en cuenta.

Unknown dijo...

Oscar Jesus Rojas Antonio, porfa me explicas como utilizaste el codigo :)

 
Este Weblog, InforMateando..., está licenciado bajo Licencia Creative Common - por Gustavo Suhit