All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.somospnt.signature.TimeStamperEmbedder Maven / Gradle / Ivy

Go to download

This is a libray to digital signing a PDF and a PFX valid certificate. Based in PDFBox official SVN example.

The newest version!
/*
 * Copyright 2015 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.somospnt.signature;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.util.Hex;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;

/**
 * A simple wrapper class for signing PDF files using PDFBOX Example usage:
 *
 * 
 * {@code
 * FileInputStream inputStream = new FileInputStream(INPUT_FILE);
 * FileOutputStream outputStream = new FileOutputStream(OUTPUT_FILE);
 * Signer signer = new Signer(KEYSTORE_PATH, PASSWORD, LOCATION, REASON);
 * signer.sign(inputStream, outputStream);
 * }
 * 
* * @author SomosPNT */ class TimeStamperEmbedder { private final String tsaUrl; private PDDocument document; private PDSignature signature; private byte[] changedEncodedSignature; public TimeStamperEmbedder(String tsaUrl) { this.tsaUrl = tsaUrl; } /** * Embeds signed timestamp(s) into existing signatures of the given document * * @param inputStream containing the bytes of the input pdf * @param outputStream Where the changed document will be saved * @throws IOException */ void embedTimeStamp(InputStream inputStream, OutputStream outputStream) throws IOException { byte[] inputBytes = inputStream.readAllBytes(); // sign try (PDDocument doc = PDDocument.load(inputBytes)) { document = doc; processTimeStamping(inputBytes, outputStream); } } /** * Processes the time-stamping of the Signature. * * @param documentBytes of the existing file containing the pdf * @param outputStream Where the new file will be written to * @throws IOException */ private void processTimeStamping(byte[] documentBytes, OutputStream outputStream) throws IOException { try { processRelevantSignatures(documentBytes); if (changedEncodedSignature != null) { embedNewSignatureIntoDocument(documentBytes, outputStream); } else { throw new IOException("No signatures were found in the document."); } } catch (IOException | NoSuchAlgorithmException | CMSException e) { throw new IOException(e); } } /** * Create changed Signature with embedded TimeStamp from TSA * * @param documentBytes byte[] of the input file * @throws IOException * @throws CMSException * @throws NoSuchAlgorithmException */ private void processRelevantSignatures(byte[] documentBytes) throws IOException, CMSException, NoSuchAlgorithmException { getRelevantSignature(document); if (signature != null) { byte[] sigBlock = signature.getContents(documentBytes); CMSSignedData signedData = new CMSSignedData(sigBlock); System.out.println("INFO: Byte Range: " + Arrays.toString(signature.getByteRange())); ValidationTimeStamp validation = new ValidationTimeStamp(tsaUrl); signedData = validation.addSignedTimeStamp(signedData); byte[] newEncoded = Hex.getBytes(signedData.getEncoded()); int maxSize = signature.getByteRange()[2] - signature.getByteRange()[1]; System.out.println( "INFO: New Signature has Size: " + newEncoded.length + " maxSize: " + maxSize); if (newEncoded.length > maxSize - 2) { throw new IOException( "New Signature is too big for existing Signature-Placeholder. Max Place: " + maxSize); } else { changedEncodedSignature = newEncoded; } } } /** * Extracts last Document-Signature from the document. The signature will be * set on the signature-field. * * @param document to get the Signature from * @throws IOException */ private void getRelevantSignature(PDDocument document) throws IOException { // we can't use getLastSignatureDictionary() because this will fail (see PDFBOX-3978) // if a signature is assigned to a pre-defined empty signature field that isn't the last. // we get the last in time by looking at the offset in the PDF file. SortedMap sortedMap = new TreeMap<>(); for (PDSignature sig : document.getSignatureDictionaries()) { int sigOffset = sig.getByteRange()[1]; sortedMap.put(sigOffset, sig); } if (sortedMap.size() > 0) { PDSignature lastSignature = sortedMap.get(sortedMap.lastKey()); COSBase type = lastSignature.getCOSObject().getItem(COSName.TYPE); if (type.equals(COSName.SIG)) { signature = lastSignature; } } } /** * Embeds the new signature into the document, by copying the rest of the * document * * @param docBytes byte array of the document * @param output target, where the file will be written * @throws IOException */ private void embedNewSignatureIntoDocument(byte[] docBytes, OutputStream output) throws IOException { int[] byteRange = signature.getByteRange(); output.write(docBytes, byteRange[0], byteRange[1] + 1); output.write(changedEncodedSignature); int addingLength = byteRange[2] - byteRange[1] - 2 - changedEncodedSignature.length; byte[] zeroes = Hex.getBytes(new byte[(addingLength + 1) / 2]); output.write(zeroes); output.write(docBytes, byteRange[2] - 1, byteRange[3] + 1); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy