Im vorherigen Artikel PDF-Signatur Timestamp habe ich Euch gezeigt, wie ein PDF-Dokument mit einem Zeitstempel versehen wird, zugleich haben wir das PDF-Dokument mit unserer eigenen digitalen Signatur versehen.
In diesem Artikel zeige ich Euch, wie Ihr die PDF-Signierung mit einem Java-Programm realisieren könnt. Wir nutzen hierfür das Programm bzw. Java-Bibliothek „iText 7“ und im Speziellen die Community-Version. Die Beschreibung zur Bibliothek ist auf der Anbieterseite „https://itextpdf.com/de/products/itext-7/itext-7-community“ zu finden, die neueste Version erreicht Ihr via GitHub-Bibliothek unter „https://github.com/itext/itext7“ – schaut in den „Release“-Bereich. Bitte beachtet, dass die Nutzung der Bibliothek und der damit entwickelten Programme unter der „GNU Affero General Public License Version 3“ steht!
Neben einiger iText7-Jar-Dateien benötigt Ihr noch zwei Bouncy Castle-Bibliotheken sowie zwei Jar-Dateien für den Logger, Ihr findet alle notwendigen Bibliotheken zusammen mit dem Programm i, GitHub-Archiv „https://github.com/java-crypto/Timestamping/tree/master/iText7_PDF-Signatur„.
Weiterhin braucht Ihr einen Keystore mit einem gültigen Zertifikat, entweder erstellt Ihr Euch einen eigenen Zertifikatespeicher oder Ihr nutzt den fertigen Keystore „Michael_Fehr.pfx“, welchen ich im o.g. GitHub-Archiv ebenfalls zum Download anbiete. Im Programm nutze ich meinen eigenen Keystore, bitte editiert ggfls. den Dateinamen, das Passwort sowie den Alias in den Zeilen 88-90.
Das Programm erzeugt zuerst eine Zwischendatei, bei der am Ende des PDF-Dokumentes eine leere Zeile eingefügt wird. Im zweiten Schritt wird auf diese neue Seite eine Signatur auf Basis des Zertifikates eingefügt und danach mit einem Zeitstempel versehen. Hier nun der Sourcecode und die wenigen Ausgabezeilen in der Konsole:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
/* * Herkunft/Origin: http://java-crypto.bplaced.net/ * Programmierer/Programmer: Michael Fehr * Basis Programmierer/Basic Programmer: Bruno Lowagie * This class is part of the white paper entitled "Digital Signatures for PDF documents" * For more info, go to: http://itextpdf.com/learn * Lizenz/License: GNU Affero General Public License Version 3 * Lizenttext/Licence: <https://www.gnu.org/licenses/agpl-3.0.de.html> * getestet mit/tested with: Java Runtime Environment 11.0.5 x64 * verwendete IDE/used IDE: intelliJ IDEA 2019.3.1 * Datum/Date (dd.mm.jjjj): 21.04.2020 * Funktion: signiert ein PDF Dokument und fuegt einen Zeitstempel hinzu * Function: signs a PDF document and adds a timestamp * Beschreibung in / Description in * http://java-crypto.bplaced.net/ * * Sicherheitshinweis/Security notice * Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion, * insbesondere mit Blick auf die Sicherheit ! * Pruefen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird. * The program routines just show the function but please be aware of the security part - * check yourself before using in the real world ! * * Sie benoetigen mehrere Bibliotheken / you need several libraries: * iText7 Community Version: https://github.com/itext/itext7/releases/tag/7.1.11 * Bouncy Castle: * bcprov-jdk15on-1.65.jar https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on/1.65 * bcpkix-jdk15on-1.65.jar https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on/1.65 * Logger: * slf4j-api-1.7.30.jar https://mvnrepository.com/artifact/org.slf4j/slf4j-api/1.7.30 * slf4j-simple-1.7.30.jar https://mvnrepository.com/artifact/org.slf4j/slf4j-simple/1.7.30 * * Alle Bibliotheken sind auch in meinem GitHub-Archiv zu finden: / * All libraries are available under my GitHub-Repository: * https://github.com/java-crypto/Timestamping/tree/master/iText7_PDF-Signatur * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.StampingProperties; import com.itextpdf.signatures.*; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.*; import java.security.cert.Certificate; import java.util.Collection; public class Timestamping_itext7 { private static Logger LOGGER = LoggerFactory.getLogger(Timestamping_itext7.class); public static Provider provider = new BouncyCastleProvider(); public static void main(String[] args) throws IOException, GeneralSecurityException { // bouncy castle aufnehmen if (Security.getProvider("BC") == null) { Security.addProvider(provider); } // setup daten start String orgFilename = "java-crypto_bplaced_net.pdf"; String timestampedFilename = "java-crypto_bplaced_net_itext7_ts.pdf"; String timestampedFilenamePath = ""; String timestampReason = "Test fuer java-crypto.bplaced.net"; String timestampLocation = "Ratingen"; // datei fuer das zertifikat/pfx-datei vom Foxit Reader String keystoreFilename = "Michael_Fehr.pfx"; String keystorePassword = "123456"; String keystoreAlias = "1"; // this alias holds the certificate char[] keystorePass = keystorePassword.toCharArray(); // timestamping service String tsaUrl = "http://tsa.swisssign.net"; String tsaUser = ""; String tsaPass = ""; // setup daten ende System.out.println("Timestamping PDF mit iText 7 Community Version"); // erzeuge zielverzeichnis falls gewuenscht if (timestampedFilenamePath != "") { File file = new File(timestampedFilenamePath); file.mkdirs(); } // erzeuge temporaere datei mit einer zusaetzlichen seite am ende String tempFilename = orgFilename.replace(".pdf", "_temp.pdf"); PdfDocument document = new PdfDocument(new PdfReader(orgFilename), new PdfWriter(tempFilename)); document.addNewPage(); int pageCount = document.getNumberOfPages(); // ergaenze metadaten wegen lizenzauflagen String metadata = "modified with iText7 Community Version, " + "License AGPL - GNU AFFERO GENERAL PUBLIC LICENSE, " + "see https://itextpdf.com/en/how-buy/legal/agpl-gnu-affero-general-public-license"; document.getDocumentInfo().setKeywords(metadata); document.close(); // einlesen des zertifikates System.out.println("\nZertifikatspeicher: " + keystoreFilename + " Passwort: " + keystorePassword + " Alias: " + keystoreAlias); KeyStore ks = KeyStore.getInstance("pkcs12"); ks.load(new FileInputStream(keystoreFilename), keystorePass); PrivateKey pk = null; try { pk = (PrivateKey) ks.getKey(keystoreAlias, keystorePass); } catch (NullPointerException e) { System.out.println("Kein Schluessel/Key unter Alias " + keystoreAlias + " gefunden, das Programm wird beendet."); System.exit(0); } Certificate[] chain = ks.getCertificateChain(keystoreAlias); // erzeugung eines ocsp-clients IOcspClient ocspClient = new OcspClientBouncyCastle(null); // erzeuge eine instanz von TSAClientBouncyCastle, eine implenetierung eines TSAClients // parameter: timestamp authority server url // nicht alle TSA benoetigen einen benutzer und ein passwort ITSAClient tsaClient = new TSAClientBouncyCastle(tsaUrl, tsaUser, tsaPass); new Timestamping_itext7() .sign(tempFilename, timestampedFilenamePath + timestampedFilename, chain, pk, DigestAlgorithms.SHA256, provider.getName(), PdfSigner.CryptoStandard.CMS, timestampReason, timestampLocation, null, ocspClient, tsaClient, 0, pageCount); // loesche das tempfile File tempFile = new File(tempFilename); if (tempFile.exists()) { tempFile.delete(); System.out.println("Temporaere Datei geloescht: " + tempFilename); } // finale ausgabe System.out.println("Die signierte und mit einem Timestamp versehene PDF-Datei wurde erstellt: " + timestampedFilenamePath + timestampedFilename); // lizenztext System.out.println("\nThis program is free software: you can redistribute it and/or modify it under the terms of the" + "\nGNU Affero General Public License as published by the Free Software Foundation," + "\neither version 3 of the License, or (at your option) any later version.\n" + "\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;" + "\nwithout even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + "\nSee the GNU Affero General Public License for more details.\n" + "\nYou should have received a copy of the GNU Affero General Public License along with this program." + "\nIf not, see https://www.gnu.org/licenses/.\n" + "\nThe program was created with iText7 Community - get it here: https://github.com/itext/itext7 "); } public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter, String reason, String location, Collection<ICrlClient> crlList, IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize, int signatureOnPage) throws GeneralSecurityException, IOException { PdfReader reader = new PdfReader(src); PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties()); System.out.println("dest = timestamp file: " + dest); // erzeuge das aussehen der signatur Rectangle rect = new Rectangle(36, 648, 300, 100); PdfSignatureAppearance appearance = signer.getSignatureAppearance(); appearance .setReason(reason) .setLocation(location) // Specify if the appearance before field is signed will be used // as a background for the signed field. The "false" value is the default value. .setReuseAppearance(false) .setPageRect(rect) .setPageNumber(signatureOnPage); // pageCount letzte seite signer.setFieldName("sig"); // erzeugung der signatur IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider); IExternalDigest digest = new BouncyCastleDigest(); // signiere das dokument mit dem detached mode, CMS oder CAdES Modus // ergaenze den erzeugten TSAClient zur signier methode signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Timestamping PDF mit iText 7 Community Version Zertifikatspeicher: Michael_Fehr.pfx Passwort: 123456 Alias: 1 dest = timestamp file: java-crypto_bplaced_net_itext7_ts.pdf [main] INFO com.itextpdf.signatures.TSAClientBouncyCastle - Timestamp generated: Tue Apr 21 16:39:14 CEST 2020 Temporaere Datei geloescht: java-crypto_bplaced_net_temp.pdf Die signierte und mit einem Timestamp versehene PDF-Datei wurde erstellt: java-crypto_bplaced_net_itext7_ts.pdf This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/. The program was created with iText7 Community - get it here: https://github.com/itext/itext7 |
Hier noch zwei Screenshots aus einem PDF-Reader – zuerst die Signatur und dann das Ergebnis der Überprüfung:
Alle Quellcodes zu PDF-iText 7 Signatur Timestamp findet Ihr zum Download in meinem GitHub-Repository Timestamping, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/Timestamping/tree/master/iText7_PDF-Signatur. Alle Programme sind unter Java 11 lauffähig (vermutlich auch unter Java 8) und wurden mit intelliJ IDEA entwickelt, welches für dieses Programm aber nicht notwendig ist.
Zur Nutzung der Programme benötigt Ihr diverse Bibliotheken – ladet Euch diese aus dem Unterordner „lib“ des oben genannten GitHub-Archives herunter und bindet Sie über Eure Entwicklungsumgebung ein.
Noch ein Wort zum Thema „Lizenz“: Das Programm steht unter unterschiedlichen Lizenzen, die Ihr bitte beachten solltet. Die wesentliche Lizenz für iText 7 ist eine „GNU Affero General Public License Version 3 Lizenz„.
Letzte Bearbeitung: 21.04.2020