Description
Etre certain de l’identité du signataire d’un PDF c’est déjà bien, mais si on désire avoir la même certitude sur l’horodatage de cette signature nous devons ajouter un autre « service » le « Time-Stamp Protocol » (rfc3161).
En effet, par défaut, quand nous générons une signature numérique c’est la date du poste réalisant l’action qui est pris en compte. Cela ne permet aucune assurance sur la validité de cette date (nous pouvons très facilement modifier la date de notre poste de travail).
Le service TSA permet de rechercher un timestamp sur un serveur externe certifié. Le principe est très simple :
- émission d’une requête vers le serveur TSA
- Construction de la réponse par le serveur TSA
- réception de la réponse du serveur TSA
- inclusion du TimeStamp dans le document
J’ai essayé d’intégrer ce service lors de la signature d’un document PDF pour apposer un timestamp « valide » sur le document et ainsi finaliser mes tests pour « Signer un document PDF ». Hélas, l’API d’iText (et principalement la classe PdfPKCS7) ne semble pas proposer ce service (j’espère que cela sera bientôt implémenté). Diverses recherches sur le Web m’ont donné une base de travail et vous trouverez ci dessous le résultat final de ces tests.
Actuellement les seules solutions que je suis arrivé à faire fonctionner sont des adaptations de la classe PdfPKCS7 (exemple). Je préfèrerais ne pas utiliser de classe modifiée de la librairie iText ou pouvoir spécialiser cette classe.
SignPdf.zip Source de l’exemple
signTSA.pdf Exemple de fichier généré
Le service TSA : Time-Stamp-Authority
Pour faire fonctionner cela vous devez obligatoirement faire appel à une TSA (Time Stamp Authority). Vous pouvez soit en utiliser une sur le net (certaines sont libres d’utilisation pour tests), soit en installer une dans votre entreprise. Vous trouverez ci dessous une liste de liens pouvant vous aider.
http://www.digistamp.com
http://security.polito.it/ts/ ++ beaucoup de liens
http://timestamping.edelweb.fr/
http://www.pfu.fujitsu.com/en/tsa/process/
http://www.opentsa.org/
Source
Le code source de ce test est basé sur diverses pistes trouvées sur le net. A la base c’est une modification de la classe PdfPKCS7 et l’utilisation de la librairie bouncycastle
Exemple
public static final boolean signTSAPdf() throws IOException, DocumentException, Exception { try { // Creation d'un KeyStore KeyStore ks = KeyStore.getInstance("PKCS12"); // Chargement du certificat p12 dans el magasin ks.load(new FileInputStream(fileKey), fileKeyPassword.toCharArray()); String alias = (String)ks.aliases().nextElement(); PrivateKey key = (PrivateKey)ks.getKey(alias, fileKeyPassword.toCharArray()); Certificate[] chain = ks.getCertificateChain(alias); PdfReader pdfReader = new PdfReader((new File(dirname + fname+".pdf")).getAbsolutePath()); File outputFile = new File(dirname + "sign_TSA_" + fname +".pdf"); PdfStamper pdfStamper; pdfStamper = PdfStamper.createSignature(pdfReader, null, '\0', outputFile); PdfSignatureAppearance sap = pdfStamper.getSignatureAppearance(); sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED); sap.setReason("Test SignPDF berthou.mc"); sap.setVisibleSignature(new Rectangle(10, 10, 50, 30), 1, "sign_rbl"); // TSA Service TSAClient tsc = new TSAClientBouncyCastle("http://www.edelweb.fr/cgi-bin/service-tsp") ; // Setup proxy (if you need a proxy) java.net.Proxy p = new java.net.Proxy(java.net.Proxy.Type.HTTP, new java.net.InetSocketAddress("10.2.0.211", 8089) ) ; tsc.setProxy(p) ; // Configure PDF signature dictionary (PdfName.ADOBE_PPKLITE or PPKMS works too) PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1); dic.setReason(sap.getReason()); dic.setLocation(sap.getLocation()); dic.setContact(sap.getContact()); dic.setDate(new PdfDate(sap.getSignDate())); // time-stamp will over-rule this PdfDictionary transformParams = new PdfDictionary(); transformParams.put(PdfName.P, new PdfNumber(1)); transformParams.put(PdfName.V, new PdfName("1.2")); transformParams.put(PdfName.TYPE, PdfName.TRANSFORMPARAMS); PdfDictionary reference = new PdfDictionary(); reference.put(PdfName.TRANSFORMMETHOD, PdfName.DOCMDP ); reference.put(PdfName.TYPE, new PdfName("SigRef")); reference.put(PdfName.TRANSFORMPARAMS, transformParams); PdfArray types = new PdfArray(); types.add(reference); dic.put(PdfName.REFERENCE, types); sap.setCryptoDictionary(dic); // Estimate signature size, creating a 'fake' one using fake data // (SHA1 length does not depend upon the data length) byte[] estSignature = genPKCS7Signature(new ByteArrayInputStream("fake".getBytes()), null, key, chain); int contentEst = estSignature.length + ((tsc == null) ? 0 : tsc.getTokenSizeEstimate()); // Preallocate excluded byte-range for the signature content (hex encoded) HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, new Integer(contentEst * 2 + 2)); sap.preClose(exc); // Get the true data signature, including a true time stamp token byte[] encodedSig = genPKCS7Signature(sap.getRangeStream(), tsc, key, chain) ; if (contentEst + 2 < encodedSig.length) { throw new Exception("Timestamp size estimate " + contentEst + " is too low for actual " + encodedSig.length); } // Copy signature into a zero-filled array, padding it up to estimate byte[] paddedSig = new byte[contentEst]; System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length); // Finally, load zero-padded signature into the signature field /Content PdfDictionary dic2 = new PdfDictionary(); dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true)); sap.close(dic2); return true; } catch (Exception key) { throw new Exception(key); } } /** * Generate the PKCS7 encoded signature * @param data InputStream - data to digest * @param doTimestamp boolean - true to include time-stamp * @return byte[] * @throws Exception */ static protected byte[] genPKCS7Signature(InputStream data, TSAClient tsc, PrivateKey key, Certificate[] chain ) throws Exception { // assume sub-filter is adobe.pkcs7.sha1 TsaPdfPKCS7 sgn = new TsaPdfPKCS7(key, chain, null, "SHA1", null, true); byte[] buff = new byte[2048]; int len = 0; while ((len = data.read(buff)) > 0) { sgn.update(buff, 0, len); } return sgn.getEncodedPKCS7(null, null, tsc); }
http://www…/PDF-Digital-signature-with-timestamp…
http://qaix.com/…pkcs-7-timestamping-sample-read.shtml