CreateSignedTimeStamp.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.pdfbox.examples.signature;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
/**
* An example for timestamp-signing a PDF for PADeS-Specification. The document will be extended by
* a signed TimeStamp (another kind of signature) (Signed TimeStamp and Hash-Value of the document
* are signed by a Time Stamp Authority (TSA)).
*
* @author Thomas Chojecki
* @author Vakhtang Koroghlishvili
* @author John Hewson
* @author Alexis Suter
*/
public class CreateSignedTimeStamp implements SignatureInterface
{
private static final Logger LOG = LogManager.getLogger(CreateSignedTimeStamp.class);
private final String tsaUrl;
/**
* Initialize the signed timestamp creator
*
* @param tsaUrl The url where TS-Request will be done.
*/
public CreateSignedTimeStamp(String tsaUrl)
{
this.tsaUrl = tsaUrl;
}
/**
* Signs the given PDF file. Alters the original file on disk.
*
* @param file the PDF file to sign
* @throws IOException if the file could not be read or written
*/
public void signDetached(File file) throws IOException
{
signDetached(file, file);
}
/**
* Signs the given PDF file.
*
* @param inFile input PDF file
* @param outFile output PDF file
* @throws IOException if the input file could not be read
*/
public void signDetached(File inFile, File outFile) throws IOException
{
if (inFile == null || !inFile.exists())
{
throw new FileNotFoundException("Document for signing does not exist");
}
// sign
try (PDDocument doc = Loader.loadPDF(inFile);
FileOutputStream fos = new FileOutputStream(outFile))
{
signDetached(doc, fos);
}
}
/**
* Prepares the TimeStamp-Signature and starts the saving-process.
*
* @param document given Pdf
* @param output Where the file will be written
* @throws IOException
*/
public void signDetached(PDDocument document, OutputStream output) throws IOException
{
int accessPermissions = SigUtils.getMDPPermission(document);
if (accessPermissions == 1)
{
throw new IllegalStateException(
"No changes to the document are permitted due to DocMDP transform parameters dictionary");
}
// create signature dictionary
PDSignature signature = new PDSignature();
signature.setType(COSName.DOC_TIME_STAMP);
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(COSName.getPDFName("ETSI.RFC3161"));
// No certification allowed because /Reference not allowed in signature directory
// see ETSI EN 319 142-1 Part 1 and ETSI TS 102 778-4
// http://www.etsi.org/deliver/etsi_en%5C319100_319199%5C31914201%5C01.01.00_30%5Cen_31914201v010100v.pdf
// http://www.etsi.org/deliver/etsi_ts/102700_102799/10277804/01.01.01_60/ts_10277804v010101p.pdf
// register signature dictionary and sign interface
document.addSignature(signature, this);
// write incremental (only for signing purpose)
document.saveIncremental(output);
}
@Override
public byte[] sign(InputStream content) throws IOException
{
ValidationTimeStamp validation;
try
{
validation = new ValidationTimeStamp(tsaUrl);
return validation.getTimeStampToken(content);
}
catch (NoSuchAlgorithmException | URISyntaxException e)
{
LOG.error("Hashing-Algorithm not found for TimeStamping", e);
}
return new byte[] {};
}
public static void main(String[] args) throws IOException
{
if (args.length != 3)
{
usage();
System.exit(1);
}
String tsaUrl = null;
if ("-tsa".equals(args[1]))
{
tsaUrl = args[2];
}
else
{
usage();
System.exit(1);
}
// sign PDF
CreateSignedTimeStamp signing = new CreateSignedTimeStamp(tsaUrl);
File inFile = new File(args[0]);
String name = inFile.getName();
String substring = name.substring(0, name.lastIndexOf('.'));
File outFile = new File(inFile.getParent(), substring + "_timestamped.pdf");
signing.signDetached(inFile, outFile);
}
private static void usage()
{
System.err.println("usage: java " + CreateSignedTimeStamp.class.getName() + " "
+ "<pdf_to_sign>\n" + "mandatory options:\n"
+ " -tsa <url> sign timestamp using the given TSA server\n");
}
}