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
|
/*
* 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.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Calendar;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
/**
* An example for signing a PDF with bouncy castle.
* A keystore can be created with the java keytool, for example:
*
* {@code keytool -genkeypair -storepass 123456 -storetype pkcs12 -alias test -validity 365
* -v -keyalg RSA -keystore keystore.p12 }
*
* @author Thomas Chojecki
* @author Vakhtang Koroghlishvili
* @author John Hewson
*/
public class PDFSign extends CreateSignatureBase
{
/**
* Initialize the signature creator with a keystore and certificate password.
*
* @param keystore the pkcs12 keystore containing the signing certificate
* @param pin the password for recovering the key
* @throws KeyStoreException if the keystore has not been initialized (loaded)
* @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found
* @throws UnrecoverableKeyException if the given password is wrong
* @throws CertificateException if the certificate is not valid as signing time
* @throws IOException if no certificate could be found
*/
public PDFSign(KeyStore keystore, char[] pin)
throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, CertificateException, IOException
{
super(keystore, pin);
}
/**
* 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, String password, File outFile) throws IOException
{
if (inFile == null || !inFile.exists())
{
throw new FileNotFoundException("Document for signing does not exist");
}
// sign
FileOutputStream fos = new FileOutputStream(outFile);
PDDocument doc = PDDocument.load(inFile, password);
signDetached(doc, fos);
}
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.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("Example User");
signature.setLocation("Los Angeles, CA");
signature.setReason("Testing");
// TODO extract the above details from the signing certificate? Reason as a parameter?
// the signing date, needed for valid signature
signature.setSignDate(Calendar.getInstance());
// Optional: certify
if (accessPermissions == 0)
{
SigUtils.setMDPPermission(document, signature, 2);
}
if (isExternalSigning())
{
document.addSignature(signature);
ExternalSigningSupport externalSigning =
document.saveIncrementalForExternalSigning(output);
// invoke external signature service
byte[] cmsSignature = sign(externalSigning.getContent());
// set signature bytes received from the service
externalSigning.setSignature(cmsSignature);
}
else
{
SignatureOptions signatureOptions = new SignatureOptions();
// Size can vary, but should be enough for purpose.
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2);
// register signature dictionary and sign interface
document.addSignature(signature, this, signatureOptions);
// write incremental (only for signing purpose)
document.saveIncremental(output);
}
}
public static void main(String[] args) throws IOException, GeneralSecurityException
{
String pdfPassword = null;
boolean externalSig = false;
String keystoreName = "";
char[] keystorePassword = null;
String inName = "";
for(int i=0; i<args.length; i++)
{
if (args[i].equals("-e"))
{
externalSig = true;
} else if (args[i].equals("-k")) {
keystoreName = args[i+1];
keystorePassword = args[i+2].toCharArray();
i += 2;
} else if (args[i].equals("-p")) {
pdfPassword = args[i+1];
i += 1;
} else if (args[i].equals("-i")) {
inName = args[i+1];
i += 1;
}
}
System.err.println(
"keystore:"+keystoreName+"\n"+
"pdfpassword:"+pdfPassword+"\n"+
"inname:"+inName+"\n"
);
// load the keystore
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream(keystoreName), keystorePassword);
// TODO alias command line argument
// sign PDF
PDFSign signing = new PDFSign(keystore, keystorePassword);
signing.setExternalSigning(externalSig);
File inFile = new File(inName);
String name = inFile.getName();
String substring = name.substring(0, name.lastIndexOf('.'));
File outFile = new File(inFile.getParent(), substring + "-signed-java.pdf");
signing.signDetached(inFile, pdfPassword, outFile);
}
private static void usage()
{
System.err.println("usage: java " + PDFSign.class.getName() + " " +
"<pkcs12_keystore> <password> <pdf_to_sign>\n" + "" +
"options:\n" +
" -e sign using external signature creation scenario");
}
}
|