|
@@ -16,7 +16,26 @@
|
|
|
*/
|
|
*/
|
|
|
package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
|
|
package com.x.processplatform.assemble.surface.jaxrs.attachment.signature;
|
|
|
|
|
|
|
|
-import org.apache.pdfbox.Loader;
|
|
|
|
|
|
|
+import java.awt.Color;
|
|
|
|
|
+import java.awt.geom.AffineTransform;
|
|
|
|
|
+import java.awt.geom.Rectangle2D;
|
|
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
|
|
+import java.io.File;
|
|
|
|
|
+import java.io.FileInputStream;
|
|
|
|
|
+import java.io.FileOutputStream;
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.io.InputStream;
|
|
|
|
|
+import java.io.RandomAccessFile;
|
|
|
|
|
+import java.security.KeyStore;
|
|
|
|
|
+import java.security.KeyStoreException;
|
|
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
|
|
+import java.security.UnrecoverableKeyException;
|
|
|
|
|
+import java.security.cert.CertificateException;
|
|
|
|
|
+import java.security.cert.X509Certificate;
|
|
|
|
|
+import java.util.Calendar;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
import org.apache.pdfbox.cos.COSName;
|
|
import org.apache.pdfbox.cos.COSName;
|
|
|
import org.apache.pdfbox.io.IOUtils;
|
|
import org.apache.pdfbox.io.IOUtils;
|
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
@@ -41,19 +60,10 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField;
|
|
|
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
|
|
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
|
|
|
import org.apache.pdfbox.util.Hex;
|
|
import org.apache.pdfbox.util.Hex;
|
|
|
import org.apache.pdfbox.util.Matrix;
|
|
import org.apache.pdfbox.util.Matrix;
|
|
|
-
|
|
|
|
|
-import javax.imageio.ImageIO;
|
|
|
|
|
-import java.awt.geom.AffineTransform;
|
|
|
|
|
-import java.awt.geom.Rectangle2D;
|
|
|
|
|
-import java.awt.image.BufferedImage;
|
|
|
|
|
-import java.io.*;
|
|
|
|
|
-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 java.util.List;
|
|
|
|
|
|
|
+import org.bouncycastle.asn1.x500.RDN;
|
|
|
|
|
+import org.bouncycastle.asn1.x500.X500Name;
|
|
|
|
|
+import org.bouncycastle.asn1.x500.style.BCStyle;
|
|
|
|
|
+import org.bouncycastle.asn1.x500.style.IETFUtils;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* This is a second example for visual signing a pdf. It doesn't use the "design pattern" influenced
|
|
* This is a second example for visual signing a pdf. It doesn't use the "design pattern" influenced
|
|
@@ -66,37 +76,42 @@ import java.util.List;
|
|
|
* @author Vakhtang Koroghlishvili
|
|
* @author Vakhtang Koroghlishvili
|
|
|
* @author Tilman Hausherr
|
|
* @author Tilman Hausherr
|
|
|
*/
|
|
*/
|
|
|
-public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
|
|
|
|
+public class CreateVisibleSignature2 extends CreateSignatureBase
|
|
|
|
|
+{
|
|
|
private SignatureOptions signatureOptions;
|
|
private SignatureOptions signatureOptions;
|
|
|
- private boolean lateExternalSigning = false;
|
|
|
|
|
- private BufferedImage image = null;
|
|
|
|
|
|
|
+ private boolean lateExternalSigning = false;
|
|
|
|
|
+ private File imageFile = null;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Initialize the signature creator with a keystore (pkcs12) and pin that
|
|
* Initialize the signature creator with a keystore (pkcs12) and pin that
|
|
|
* should be used for the signature.
|
|
* should be used for the signature.
|
|
|
*
|
|
*
|
|
|
* @param keystore is a pkcs12 keystore.
|
|
* @param keystore is a pkcs12 keystore.
|
|
|
- * @param pin is the pin for the keystore / private key
|
|
|
|
|
- * @throws KeyStoreException if the keystore has not been initialized (loaded)
|
|
|
|
|
- * @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found
|
|
|
|
|
|
|
+ * @param pin is the pin for the keystore / private 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 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
|
|
|
|
|
|
|
+ * @throws CertificateException if the certificate is not valid as signing time
|
|
|
|
|
+ * @throws IOException if no certificate could be found
|
|
|
*/
|
|
*/
|
|
|
public CreateVisibleSignature2(KeyStore keystore, char[] pin)
|
|
public CreateVisibleSignature2(KeyStore keystore, char[] pin)
|
|
|
- throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException {
|
|
|
|
|
|
|
+ throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException
|
|
|
|
|
+ {
|
|
|
super(keystore, pin);
|
|
super(keystore, pin);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public BufferedImage getImage() {
|
|
|
|
|
- return image;
|
|
|
|
|
|
|
+ public File getImageFile()
|
|
|
|
|
+ {
|
|
|
|
|
+ return imageFile;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public void setImage(BufferedImage image) {
|
|
|
|
|
- this.image = image;
|
|
|
|
|
|
|
+ public void setImageFile(File imageFile)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.imageFile = imageFile;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public boolean isLateExternalSigning() {
|
|
|
|
|
|
|
+ public boolean isLateExternalSigning()
|
|
|
|
|
+ {
|
|
|
return lateExternalSigning;
|
|
return lateExternalSigning;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -107,153 +122,174 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
*
|
|
*
|
|
|
* @param lateExternalSigning
|
|
* @param lateExternalSigning
|
|
|
*/
|
|
*/
|
|
|
- public void setLateExternalSigning(boolean lateExternalSigning) {
|
|
|
|
|
|
|
+ public void setLateExternalSigning(boolean lateExternalSigning)
|
|
|
|
|
+ {
|
|
|
this.lateExternalSigning = lateExternalSigning;
|
|
this.lateExternalSigning = lateExternalSigning;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Sign pdf file and create new file that ends with "_signed.pdf".
|
|
* Sign pdf file and create new file that ends with "_signed.pdf".
|
|
|
*
|
|
*
|
|
|
- * @param inputFile The source pdf document file.
|
|
|
|
|
|
|
+ * @param inputFile The source pdf document file.
|
|
|
* @param signedFile The file to be signed.
|
|
* @param signedFile The file to be signed.
|
|
|
- * @param humanRect rectangle from a human viewpoint (coordinates start at top left)
|
|
|
|
|
- * @param tsaUrl optional TSA url
|
|
|
|
|
|
|
+ * @param humanRect rectangle from a human viewpoint (coordinates start at top left)
|
|
|
|
|
+ * @param tsaUrl optional TSA url
|
|
|
* @throws IOException
|
|
* @throws IOException
|
|
|
*/
|
|
*/
|
|
|
- public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl) throws IOException {
|
|
|
|
|
|
|
+ public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl) throws IOException
|
|
|
|
|
+ {
|
|
|
this.signPDF(inputFile, signedFile, humanRect, tsaUrl, null);
|
|
this.signPDF(inputFile, signedFile, humanRect, tsaUrl, null);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Sign pdf file and create new file that ends with "_signed.pdf".
|
|
* Sign pdf file and create new file that ends with "_signed.pdf".
|
|
|
*
|
|
*
|
|
|
- * @param inputFile The source pdf document file.
|
|
|
|
|
- * @param signedFile The file to be signed.
|
|
|
|
|
- * @param humanRect rectangle from a human viewpoint (coordinates start at top left)
|
|
|
|
|
- * @param tsaUrl optional TSA url
|
|
|
|
|
|
|
+ * @param inputFile The source pdf document file.
|
|
|
|
|
+ * @param signedFile The file to be signed.
|
|
|
|
|
+ * @param humanRect rectangle from a human viewpoint (coordinates start at top left)
|
|
|
|
|
+ * @param tsaUrl optional TSA url
|
|
|
* @param signatureFieldName optional name of an existing (unsigned) signature field
|
|
* @param signatureFieldName optional name of an existing (unsigned) signature field
|
|
|
* @throws IOException
|
|
* @throws IOException
|
|
|
*/
|
|
*/
|
|
|
- public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl, String signatureFieldName) throws IOException {
|
|
|
|
|
- if (inputFile == null || !inputFile.exists()) {
|
|
|
|
|
|
|
+ public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl, String signatureFieldName) throws IOException
|
|
|
|
|
+ {
|
|
|
|
|
+ if (inputFile == null || !inputFile.exists())
|
|
|
|
|
+ {
|
|
|
throw new IOException("Document for signing does not exist");
|
|
throw new IOException("Document for signing does not exist");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
setTsaUrl(tsaUrl);
|
|
setTsaUrl(tsaUrl);
|
|
|
|
|
|
|
|
// creating output document and prepare the IO streams.
|
|
// creating output document and prepare the IO streams.
|
|
|
- try (FileOutputStream fos = new FileOutputStream(signedFile);
|
|
|
|
|
- PDDocument doc = Loader.loadPDF(inputFile)) {
|
|
|
|
|
- int accessPermissions = SigUtils.getMDPPermission(doc);
|
|
|
|
|
- if (accessPermissions == 1) {
|
|
|
|
|
- throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
|
|
|
|
|
- }
|
|
|
|
|
- // Note that PDFBox has a bug that visual signing on certified files with permission 2
|
|
|
|
|
- // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to
|
|
|
|
|
- // be careful with such files.
|
|
|
|
|
-
|
|
|
|
|
- PDSignature signature = null;
|
|
|
|
|
- PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
|
|
|
|
|
- PDRectangle rect = null;
|
|
|
|
|
-
|
|
|
|
|
- // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
|
|
|
|
|
- if (acroForm != null) {
|
|
|
|
|
- signature = findExistingSignature(acroForm, signatureFieldName);
|
|
|
|
|
- if (signature != null) {
|
|
|
|
|
- rect = acroForm.getField(signatureFieldName).getWidgets().get(0).getRectangle();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ FileOutputStream fos = new FileOutputStream(signedFile);
|
|
|
|
|
|
|
|
- if (signature == null) {
|
|
|
|
|
- // create signature dictionary
|
|
|
|
|
- signature = new PDSignature();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ PDDocument doc = PDDocument.load(inputFile);
|
|
|
|
|
+ int accessPermissions = SigUtils.getMDPPermission(doc);
|
|
|
|
|
+ if (accessPermissions == 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
|
|
|
|
|
+ }
|
|
|
|
|
+ // Note that PDFBox has a bug that visual signing on certified files with permission 2
|
|
|
|
|
+ // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to
|
|
|
|
|
+ // be careful with such files.
|
|
|
|
|
|
|
|
- if (rect == null) {
|
|
|
|
|
- rect = createSignatureRectangle(doc, humanRect);
|
|
|
|
|
|
|
+ PDSignature signature = null;
|
|
|
|
|
+ PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
|
|
|
|
|
+ PDRectangle rect = null;
|
|
|
|
|
+
|
|
|
|
|
+ // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
|
|
|
|
|
+ if (acroForm != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ signature = findExistingSignature(acroForm, signatureFieldName);
|
|
|
|
|
+ if (signature != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ rect = acroForm.getField(signatureFieldName).getWidgets().get(0).getRectangle();
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Optional: certify
|
|
|
|
|
- // can be done only if version is at least 1.5 and if not already set
|
|
|
|
|
- // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
|
|
|
|
|
- // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
|
|
|
|
|
- if (doc.getVersion() >= 1.5f && accessPermissions == 0) {
|
|
|
|
|
- SigUtils.setMDPPermission(doc, signature, 2);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (signature == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ // create signature dictionary
|
|
|
|
|
+ signature = new PDSignature();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (acroForm != null && acroForm.getNeedAppearances()) {
|
|
|
|
|
- // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
|
|
|
|
|
- // with Adobe Reader
|
|
|
|
|
- if (acroForm.getFields().isEmpty()) {
|
|
|
|
|
- // we can safely delete it if there are no fields
|
|
|
|
|
- acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
|
|
|
|
|
- // note that if you've set MDP permissions, the removal of this item
|
|
|
|
|
- // may result in Adobe Reader claiming that the document has been changed.
|
|
|
|
|
- // and/or that field content won't be displayed properly.
|
|
|
|
|
- // ==> decide what you prefer and adjust your code accordingly.
|
|
|
|
|
- } else {
|
|
|
|
|
- System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (rect == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ rect = createSignatureRectangle(doc, humanRect);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // default filter
|
|
|
|
|
- signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
|
|
|
|
|
-
|
|
|
|
|
- // subfilter for basic and PAdES Part 2 signatures
|
|
|
|
|
- signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
|
|
|
|
|
-
|
|
|
|
|
- signature.setName("Name");
|
|
|
|
|
- signature.setLocation("Location");
|
|
|
|
|
- signature.setReason("Reason");
|
|
|
|
|
-
|
|
|
|
|
- // the signing date, needed for valid signature
|
|
|
|
|
- signature.setSignDate(Calendar.getInstance());
|
|
|
|
|
-
|
|
|
|
|
- // do not set SignatureInterface instance, if external signing used
|
|
|
|
|
- SignatureInterface signatureInterface = isExternalSigning() ? null : this;
|
|
|
|
|
-
|
|
|
|
|
- // register signature dictionary and sign interface
|
|
|
|
|
- signatureOptions = new SignatureOptions();
|
|
|
|
|
- signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect, signature));
|
|
|
|
|
- signatureOptions.setPage(doc.getNumberOfPages());
|
|
|
|
|
- doc.addSignature(signature, signatureInterface, signatureOptions);
|
|
|
|
|
-
|
|
|
|
|
- if (isExternalSigning()) {
|
|
|
|
|
- ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
|
|
|
|
|
- // invoke external signature service
|
|
|
|
|
- byte[] cmsSignature = sign(externalSigning.getContent());
|
|
|
|
|
-
|
|
|
|
|
- // Explanation of late external signing (off by default):
|
|
|
|
|
- // If you want to add the signature in a separate step, then set an empty byte array
|
|
|
|
|
- // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
|
|
|
|
|
- // you can write the ascii hex signature at a later time even if you don't have this
|
|
|
|
|
- // PDDocument object anymore, with classic java file random access methods.
|
|
|
|
|
- // If you can't remember the offset value from ByteRange because your context has changed,
|
|
|
|
|
- // then open the file with PDFBox, find the field with findExistingSignature() or
|
|
|
|
|
- // PDDocument.getLastSignatureDictionary() and get the ByteRange from there.
|
|
|
|
|
- // Close the file and then write the signature as explained earlier in this comment.
|
|
|
|
|
- if (isLateExternalSigning()) {
|
|
|
|
|
- // this saves the file with a 0 signature
|
|
|
|
|
- externalSigning.setSignature(new byte[0]);
|
|
|
|
|
-
|
|
|
|
|
- // remember the offset (add 1 because of "<")
|
|
|
|
|
- int offset = signature.getByteRange()[1] + 1;
|
|
|
|
|
-
|
|
|
|
|
- // now write the signature at the correct offset without any PDFBox methods
|
|
|
|
|
- try (RandomAccessFile raf = new RandomAccessFile(signedFile, "rw")) {
|
|
|
|
|
- raf.seek(offset);
|
|
|
|
|
- raf.write(Hex.getBytes(cmsSignature));
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // set signature bytes received from the service and save the file
|
|
|
|
|
- externalSigning.setSignature(cmsSignature);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // write incremental (only for signing purpose)
|
|
|
|
|
- doc.saveIncremental(fos);
|
|
|
|
|
|
|
+ // Optional: certify
|
|
|
|
|
+ // can be done only if version is at least 1.5 and if not already set
|
|
|
|
|
+ // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
|
|
|
|
|
+ // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
|
|
|
|
|
+ if (doc.getVersion() >= 1.5f && accessPermissions == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ SigUtils.setMDPPermission(doc, signature, 2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (acroForm != null && acroForm.getNeedAppearances())
|
|
|
|
|
+ {
|
|
|
|
|
+ // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
|
|
|
|
|
+ // with Adobe Reader
|
|
|
|
|
+ if (acroForm.getFields().isEmpty())
|
|
|
|
|
+ {
|
|
|
|
|
+ // we can safely delete it if there are no fields
|
|
|
|
|
+ acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
|
|
|
|
|
+ // note that if you've set MDP permissions, the removal of this item
|
|
|
|
|
+ // may result in Adobe Reader claiming that the document has been changed.
|
|
|
|
|
+ // and/or that field content won't be displayed properly.
|
|
|
|
|
+ // ==> decide what you prefer and adjust your code accordingly.
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // default filter
|
|
|
|
|
+ signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
|
|
|
|
|
+
|
|
|
|
|
+ // subfilter for basic and PAdES Part 2 signatures
|
|
|
|
|
+ signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
|
|
|
|
|
+
|
|
|
|
|
+ signature.setName("Name");
|
|
|
|
|
+ signature.setLocation("Location");
|
|
|
|
|
+ signature.setReason("Reason");
|
|
|
|
|
+
|
|
|
|
|
+ // the signing date, needed for valid signature
|
|
|
|
|
+ signature.setSignDate(Calendar.getInstance());
|
|
|
|
|
+
|
|
|
|
|
+ // do not set SignatureInterface instance, if external signing used
|
|
|
|
|
+ SignatureInterface signatureInterface = isExternalSigning() ? null : this;
|
|
|
|
|
+
|
|
|
|
|
+ // register signature dictionary and sign interface
|
|
|
|
|
+ signatureOptions = new SignatureOptions();
|
|
|
|
|
+ signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect, signature));
|
|
|
|
|
+ signatureOptions.setPage(0);
|
|
|
|
|
+ doc.addSignature(signature, signatureInterface, signatureOptions);
|
|
|
|
|
+
|
|
|
|
|
+ if (isExternalSigning())
|
|
|
|
|
+ {
|
|
|
|
|
+ ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
|
|
|
|
|
+ // invoke external signature service
|
|
|
|
|
+ byte[] cmsSignature = sign(externalSigning.getContent());
|
|
|
|
|
+
|
|
|
|
|
+ // Explanation of late external signing (off by default):
|
|
|
|
|
+ // If you want to add the signature in a separate step, then set an empty byte array
|
|
|
|
|
+ // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
|
|
|
|
|
+ // you can write the ascii hex signature at a later time even if you don't have this
|
|
|
|
|
+ // PDDocument object anymore, with classic java file random access methods.
|
|
|
|
|
+ // If you can't remember the offset value from ByteRange because your context has changed,
|
|
|
|
|
+ // then open the file with PDFBox, find the field with findExistingSignature() or
|
|
|
|
|
+ // PDDocument.getLastSignatureDictionary() and get the ByteRange from there.
|
|
|
|
|
+ // Close the file and then write the signature as explained earlier in this comment.
|
|
|
|
|
+ if (isLateExternalSigning())
|
|
|
|
|
+ {
|
|
|
|
|
+ // this saves the file with a 0 signature
|
|
|
|
|
+ externalSigning.setSignature(new byte[0]);
|
|
|
|
|
+
|
|
|
|
|
+ // remember the offset (add 1 because of "<")
|
|
|
|
|
+ int offset = signature.getByteRange()[1] + 1;
|
|
|
|
|
+
|
|
|
|
|
+ // now write the signature at the correct offset without any PDFBox methods
|
|
|
|
|
+ RandomAccessFile raf = new RandomAccessFile(signedFile, "rw");
|
|
|
|
|
+ raf.seek(offset);
|
|
|
|
|
+ raf.write(Hex.getBytes(cmsSignature));
|
|
|
|
|
+ raf.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // set signature bytes received from the service and save the file
|
|
|
|
|
+ externalSigning.setSignature(cmsSignature);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // write incremental (only for signing purpose)
|
|
|
|
|
+ doc.saveIncremental(fos);
|
|
|
|
|
+ }
|
|
|
|
|
+ doc.close();
|
|
|
|
|
+
|
|
|
// Do not close signatureOptions before saving, because some COSStream objects within
|
|
// Do not close signatureOptions before saving, because some COSStream objects within
|
|
|
// are transferred to the signed document.
|
|
// are transferred to the signed document.
|
|
|
// Do not allow signatureOptions get out of scope before saving, because then the COSDocument
|
|
// Do not allow signatureOptions get out of scope before saving, because then the COSDocument
|
|
@@ -262,7 +298,8 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
IOUtils.closeQuietly(signatureOptions);
|
|
IOUtils.closeQuietly(signatureOptions);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect) {
|
|
|
|
|
|
|
+ private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect)
|
|
|
|
|
+ {
|
|
|
float x = (float) humanRect.getX();
|
|
float x = (float) humanRect.getX();
|
|
|
float y = (float) humanRect.getY();
|
|
float y = (float) humanRect.getY();
|
|
|
float width = (float) humanRect.getWidth();
|
|
float width = (float) humanRect.getWidth();
|
|
@@ -271,7 +308,8 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
PDRectangle pageRect = page.getCropBox();
|
|
PDRectangle pageRect = page.getCropBox();
|
|
|
PDRectangle rect = new PDRectangle();
|
|
PDRectangle rect = new PDRectangle();
|
|
|
// signing should be at the same position regardless of page rotation.
|
|
// signing should be at the same position regardless of page rotation.
|
|
|
- switch (page.getRotation()) {
|
|
|
|
|
|
|
+ switch (page.getRotation())
|
|
|
|
|
+ {
|
|
|
case 90:
|
|
case 90:
|
|
|
rect.setLowerLeftY(x);
|
|
rect.setLowerLeftY(x);
|
|
|
rect.setUpperRightY(x + width);
|
|
rect.setUpperRightY(x + width);
|
|
@@ -303,142 +341,148 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
|
|
|
|
|
// create a template PDF document with empty signature and return it as a stream.
|
|
// create a template PDF document with empty signature and return it as a stream.
|
|
|
private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum,
|
|
private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum,
|
|
|
- PDRectangle rect, PDSignature signature) throws IOException {
|
|
|
|
|
- try (PDDocument doc = new PDDocument()) {
|
|
|
|
|
- PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
|
|
|
|
|
- doc.addPage(page);
|
|
|
|
|
- PDAcroForm acroForm = new PDAcroForm(doc);
|
|
|
|
|
- doc.getDocumentCatalog().setAcroForm(acroForm);
|
|
|
|
|
- PDSignatureField signatureField = new PDSignatureField(acroForm);
|
|
|
|
|
- PDAnnotationWidget widget = signatureField.getWidgets().get(0);
|
|
|
|
|
- List<PDField> acroFormFields = acroForm.getFields();
|
|
|
|
|
- acroForm.setSignaturesExist(true);
|
|
|
|
|
- acroForm.setAppendOnly(true);
|
|
|
|
|
- acroForm.getCOSObject().setDirect(true);
|
|
|
|
|
- acroFormFields.add(signatureField);
|
|
|
|
|
-
|
|
|
|
|
- widget.setRectangle(rect);
|
|
|
|
|
-
|
|
|
|
|
- // from PDVisualSigBuilder.createHolderForm()
|
|
|
|
|
- PDStream stream = new PDStream(doc);
|
|
|
|
|
- PDFormXObject form = new PDFormXObject(stream);
|
|
|
|
|
- PDResources res = new PDResources();
|
|
|
|
|
- form.setResources(res);
|
|
|
|
|
- form.setFormType(1);
|
|
|
|
|
- PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
|
|
|
|
|
- float height = bbox.getHeight();
|
|
|
|
|
- Matrix initialScale = null;
|
|
|
|
|
- switch (srcDoc.getPage(pageNum).getRotation()) {
|
|
|
|
|
- case 90:
|
|
|
|
|
- form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
|
|
|
|
|
- initialScale = Matrix
|
|
|
|
|
- .getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
|
|
|
|
|
- height = bbox.getWidth();
|
|
|
|
|
- break;
|
|
|
|
|
- case 180:
|
|
|
|
|
- form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
|
|
|
|
|
- break;
|
|
|
|
|
- case 270:
|
|
|
|
|
- form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
|
|
|
|
|
- initialScale = Matrix
|
|
|
|
|
- .getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
|
|
|
|
|
- height = bbox.getWidth();
|
|
|
|
|
- break;
|
|
|
|
|
- case 0:
|
|
|
|
|
- default:
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- form.setBBox(bbox);
|
|
|
|
|
- PDFont font = PDType1Font.HELVETICA_BOLD;
|
|
|
|
|
-
|
|
|
|
|
- // from PDVisualSigBuilder.createAppearanceDictionary()
|
|
|
|
|
- PDAppearanceDictionary appearance = new PDAppearanceDictionary();
|
|
|
|
|
- appearance.getCOSObject().setDirect(true);
|
|
|
|
|
- PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
|
|
|
|
|
- appearance.setNormalAppearance(appearanceStream);
|
|
|
|
|
- widget.setAppearance(appearance);
|
|
|
|
|
-
|
|
|
|
|
- try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream)) {
|
|
|
|
|
- // for 90° and 270° scale ratio of width / height
|
|
|
|
|
- // not really sure about this
|
|
|
|
|
- // why does scale have no effect when done in the form matrix???
|
|
|
|
|
- if (initialScale != null) {
|
|
|
|
|
- cs.transform(initialScale);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // show background (just for debugging, to see the rect size + position)
|
|
|
|
|
-// cs.setNonStrokingColor(Color.yellow);
|
|
|
|
|
-// cs.addRect(-5000, -5000, 10000, 10000);
|
|
|
|
|
-// cs.fill();
|
|
|
|
|
-
|
|
|
|
|
- if (image != null) {
|
|
|
|
|
- // show background image
|
|
|
|
|
- // save and restore graphics if the image is too large and needs to be scaled
|
|
|
|
|
- cs.saveGraphicsState();
|
|
|
|
|
- cs.transform(Matrix
|
|
|
|
|
- .getScaleInstance(rect.getHeight() / (float) image.getHeight(), rect
|
|
|
|
|
- .getHeight() / (float) image.getHeight()));
|
|
|
|
|
- ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
|
|
- ImageIO.write(image, "png", out);
|
|
|
|
|
- PDImageXObject img = PDImageXObject.createFromByteArray(doc, out.toByteArray(), "sign.png");
|
|
|
|
|
- cs.drawImage(img, 0, 0);
|
|
|
|
|
- cs.restoreGraphicsState();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // show text
|
|
|
|
|
- float fontSize = 10;
|
|
|
|
|
- float leading = fontSize * 1.5f;
|
|
|
|
|
-// cs.beginText();
|
|
|
|
|
-// cs.setFont(font, fontSize);
|
|
|
|
|
-// cs.setNonStrokingColor(Color.black);
|
|
|
|
|
-// cs.newLineAtOffset(fontSize, height - leading);
|
|
|
|
|
-// cs.setLeading(leading);
|
|
|
|
|
-//
|
|
|
|
|
-// X509Certificate cert = (X509Certificate) getCertificateChain()[0];
|
|
|
|
|
-//
|
|
|
|
|
-// // https://stackoverflow.com/questions/2914521/
|
|
|
|
|
-// X500Name x500Name = new X500Name(cert.getSubjectX500Principal().getName());
|
|
|
|
|
-// RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
|
|
|
|
|
-// String name = IETFUtils.valueToString(cn.getFirst().getValue());
|
|
|
|
|
-//
|
|
|
|
|
-// // See https://stackoverflow.com/questions/12575990
|
|
|
|
|
-// // for better date formatting
|
|
|
|
|
-// String date = signature.getSignDate().getTime().toString();
|
|
|
|
|
-// String reason = signature.getReason();
|
|
|
|
|
-//
|
|
|
|
|
-// cs.showText("Signer: " + name);
|
|
|
|
|
-// cs.newLine();
|
|
|
|
|
-// cs.showText(date);
|
|
|
|
|
-// cs.newLine();
|
|
|
|
|
-// cs.showText("Reason: " + reason);
|
|
|
|
|
-//
|
|
|
|
|
-// cs.endText();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // no need to set annotations and /P entry
|
|
|
|
|
|
|
+ PDRectangle rect, PDSignature signature) throws IOException
|
|
|
|
|
+ {
|
|
|
|
|
+ PDDocument doc = new PDDocument();
|
|
|
|
|
+
|
|
|
|
|
+ PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
|
|
|
|
|
+ doc.addPage(page);
|
|
|
|
|
+ PDAcroForm acroForm = new PDAcroForm(doc);
|
|
|
|
|
+ doc.getDocumentCatalog().setAcroForm(acroForm);
|
|
|
|
|
+ PDSignatureField signatureField = new PDSignatureField(acroForm);
|
|
|
|
|
+ PDAnnotationWidget widget = signatureField.getWidgets().get(0);
|
|
|
|
|
+ List<PDField> acroFormFields = acroForm.getFields();
|
|
|
|
|
+ acroForm.setSignaturesExist(true);
|
|
|
|
|
+ acroForm.setAppendOnly(true);
|
|
|
|
|
+ acroForm.getCOSObject().setDirect(true);
|
|
|
|
|
+ acroFormFields.add(signatureField);
|
|
|
|
|
+
|
|
|
|
|
+ widget.setRectangle(rect);
|
|
|
|
|
+
|
|
|
|
|
+ // from PDVisualSigBuilder.createHolderForm()
|
|
|
|
|
+ PDStream stream = new PDStream(doc);
|
|
|
|
|
+ PDFormXObject form = new PDFormXObject(stream);
|
|
|
|
|
+ PDResources res = new PDResources();
|
|
|
|
|
+ form.setResources(res);
|
|
|
|
|
+ form.setFormType(1);
|
|
|
|
|
+ PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
|
|
|
|
|
+ float height = bbox.getHeight();
|
|
|
|
|
+ Matrix initialScale = null;
|
|
|
|
|
+ switch (srcDoc.getPage(pageNum).getRotation())
|
|
|
|
|
+ {
|
|
|
|
|
+ case 90:
|
|
|
|
|
+ form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
|
|
|
|
|
+ initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
|
|
|
|
|
+ height = bbox.getWidth();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 180:
|
|
|
|
|
+ form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 270:
|
|
|
|
|
+ form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
|
|
|
|
|
+ initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
|
|
|
|
|
+ height = bbox.getWidth();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ form.setBBox(bbox);
|
|
|
|
|
+ PDFont font = PDType1Font.HELVETICA_BOLD;
|
|
|
|
|
+
|
|
|
|
|
+ // from PDVisualSigBuilder.createAppearanceDictionary()
|
|
|
|
|
+ PDAppearanceDictionary appearance = new PDAppearanceDictionary();
|
|
|
|
|
+ appearance.getCOSObject().setDirect(true);
|
|
|
|
|
+ PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
|
|
|
|
|
+ appearance.setNormalAppearance(appearanceStream);
|
|
|
|
|
+ widget.setAppearance(appearance);
|
|
|
|
|
+
|
|
|
|
|
+ PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream);
|
|
|
|
|
+
|
|
|
|
|
+ // for 90° and 270° scale ratio of width / height
|
|
|
|
|
+ // not really sure about this
|
|
|
|
|
+ // why does scale have no effect when done in the form matrix???
|
|
|
|
|
+ if (initialScale != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ cs.transform(initialScale);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
|
- doc.save(baos);
|
|
|
|
|
- return new ByteArrayInputStream(baos.toByteArray());
|
|
|
|
|
|
|
+ // show background (just for debugging, to see the rect size + position)
|
|
|
|
|
+ cs.setNonStrokingColor(Color.yellow);
|
|
|
|
|
+ cs.addRect(-5000, -5000, 10000, 10000);
|
|
|
|
|
+ cs.fill();
|
|
|
|
|
+
|
|
|
|
|
+ if (imageFile != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ // show background image
|
|
|
|
|
+ // save and restore graphics if the image is too large and needs to be scaled
|
|
|
|
|
+ cs.saveGraphicsState();
|
|
|
|
|
+ cs.transform(Matrix.getScaleInstance(0.25f, 0.25f));
|
|
|
|
|
+ PDImageXObject img = PDImageXObject.createFromFileByExtension(imageFile, doc);
|
|
|
|
|
+ cs.drawImage(img, 0, 0);
|
|
|
|
|
+ cs.restoreGraphicsState();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // show text
|
|
|
|
|
+ float fontSize = 10;
|
|
|
|
|
+ float leading = fontSize * 1.5f;
|
|
|
|
|
+ cs.beginText();
|
|
|
|
|
+ cs.setFont(font, fontSize);
|
|
|
|
|
+ cs.setNonStrokingColor(Color.black);
|
|
|
|
|
+ cs.newLineAtOffset(fontSize, height - leading);
|
|
|
|
|
+ cs.setLeading(leading);
|
|
|
|
|
+
|
|
|
|
|
+ X509Certificate cert = (X509Certificate) getCertificateChain()[0];
|
|
|
|
|
+
|
|
|
|
|
+ // https://stackoverflow.com/questions/2914521/
|
|
|
|
|
+ X500Name x500Name = new X500Name(cert.getSubjectX500Principal().getName());
|
|
|
|
|
+ RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
|
|
|
|
|
+ String name = IETFUtils.valueToString(cn.getFirst().getValue());
|
|
|
|
|
+
|
|
|
|
|
+ // See https://stackoverflow.com/questions/12575990
|
|
|
|
|
+ // for better date formatting
|
|
|
|
|
+ String date = signature.getSignDate().getTime().toString();
|
|
|
|
|
+ String reason = signature.getReason();
|
|
|
|
|
+
|
|
|
|
|
+ cs.showText("Signer: " + name);
|
|
|
|
|
+ cs.newLine();
|
|
|
|
|
+ cs.showText(date);
|
|
|
|
|
+ cs.newLine();
|
|
|
|
|
+ cs.showText("Reason: " + reason);
|
|
|
|
|
+
|
|
|
|
|
+ cs.endText();
|
|
|
|
|
+
|
|
|
|
|
+ cs.close();
|
|
|
|
|
+
|
|
|
|
|
+ // no need to set annotations and /P entry
|
|
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
|
+ doc.save(baos);
|
|
|
|
|
+ doc.close();
|
|
|
|
|
+ return new ByteArrayInputStream(baos.toByteArray());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Find an existing signature (assumed to be empty). You will usually not need this.
|
|
// Find an existing signature (assumed to be empty). You will usually not need this.
|
|
|
- private PDSignature findExistingSignature(PDAcroForm acroForm, String sigFieldName) {
|
|
|
|
|
|
|
+ private PDSignature findExistingSignature(PDAcroForm acroForm, String sigFieldName)
|
|
|
|
|
+ {
|
|
|
PDSignature signature = null;
|
|
PDSignature signature = null;
|
|
|
PDSignatureField signatureField;
|
|
PDSignatureField signatureField;
|
|
|
- if (acroForm != null) {
|
|
|
|
|
|
|
+ if (acroForm != null)
|
|
|
|
|
+ {
|
|
|
signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
|
|
signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
|
|
|
- if (signatureField != null) {
|
|
|
|
|
|
|
+ if (signatureField != null)
|
|
|
|
|
+ {
|
|
|
// retrieve signature dictionary
|
|
// retrieve signature dictionary
|
|
|
signature = signatureField.getSignature();
|
|
signature = signatureField.getSignature();
|
|
|
- if (signature == null) {
|
|
|
|
|
|
|
+ if (signature == null)
|
|
|
|
|
+ {
|
|
|
signature = new PDSignature();
|
|
signature = new PDSignature();
|
|
|
// after solving PDFBOX-3524
|
|
// after solving PDFBOX-3524
|
|
|
// signatureField.setValue(signature)
|
|
// signatureField.setValue(signature)
|
|
|
// until then:
|
|
// until then:
|
|
|
signatureField.getCOSObject().setItem(COSName.V, signature);
|
|
signatureField.getCOSObject().setItem(COSName.V, signature);
|
|
|
- } else {
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
|
|
throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -454,15 +498,17 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
* [3] image of visible signature
|
|
* [3] image of visible signature
|
|
|
*
|
|
*
|
|
|
* @param args
|
|
* @param args
|
|
|
- * @throws KeyStoreException
|
|
|
|
|
- * @throws CertificateException
|
|
|
|
|
- * @throws IOException
|
|
|
|
|
- * @throws NoSuchAlgorithmException
|
|
|
|
|
- * @throws UnrecoverableKeyException
|
|
|
|
|
|
|
+ * @throws java.security.KeyStoreException
|
|
|
|
|
+ * @throws java.security.cert.CertificateException
|
|
|
|
|
+ * @throws java.io.IOException
|
|
|
|
|
+ * @throws java.security.NoSuchAlgorithmException
|
|
|
|
|
+ * @throws java.security.UnrecoverableKeyException
|
|
|
*/
|
|
*/
|
|
|
public static void main(String[] args) throws KeyStoreException, CertificateException,
|
|
public static void main(String[] args) throws KeyStoreException, CertificateException,
|
|
|
- IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
|
|
|
|
|
- if (args.length < 3) {
|
|
|
|
|
|
|
+ IOException, NoSuchAlgorithmException, UnrecoverableKeyException
|
|
|
|
|
+ {
|
|
|
|
|
+ if (args.length < 3)
|
|
|
|
|
+ {
|
|
|
usage();
|
|
usage();
|
|
|
System.exit(1);
|
|
System.exit(1);
|
|
|
}
|
|
}
|
|
@@ -471,16 +517,20 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
// External signing is needed if you are using an external signing service, e.g. to sign
|
|
// External signing is needed if you are using an external signing service, e.g. to sign
|
|
|
// several files at once.
|
|
// several files at once.
|
|
|
boolean externalSig = false;
|
|
boolean externalSig = false;
|
|
|
- for (int i = 0; i < args.length; i++) {
|
|
|
|
|
- if ("-tsa".equals(args[i])) {
|
|
|
|
|
|
|
+ for (int i = 0; i < args.length; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (args[i].equals("-tsa"))
|
|
|
|
|
+ {
|
|
|
i++;
|
|
i++;
|
|
|
- if (i >= args.length) {
|
|
|
|
|
|
|
+ if (i >= args.length)
|
|
|
|
|
+ {
|
|
|
usage();
|
|
usage();
|
|
|
System.exit(1);
|
|
System.exit(1);
|
|
|
}
|
|
}
|
|
|
tsaUrl = args[i];
|
|
tsaUrl = args[i];
|
|
|
}
|
|
}
|
|
|
- if ("-e".equals(args[i])) {
|
|
|
|
|
|
|
+ if (args[i].equals("-e"))
|
|
|
|
|
+ {
|
|
|
externalSig = true;
|
|
externalSig = true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -494,8 +544,9 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
|
|
|
|
|
CreateVisibleSignature2 signing = new CreateVisibleSignature2(keystore, pin.clone());
|
|
CreateVisibleSignature2 signing = new CreateVisibleSignature2(keystore, pin.clone());
|
|
|
|
|
|
|
|
- if (args.length >= 4 && !"-tsa".equals(args[3])) {
|
|
|
|
|
- signing.setImage(ImageIO.read(new File(args[3])));
|
|
|
|
|
|
|
+ if (args.length >= 4 && !"-tsa".equals(args[3]))
|
|
|
|
|
+ {
|
|
|
|
|
+ signing.setImageFile(new File(args[3]));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
File signedDocumentFile;
|
|
File signedDocumentFile;
|
|
@@ -518,12 +569,13 @@ public class CreateVisibleSignature2 extends CreateSignatureBase {
|
|
|
/**
|
|
/**
|
|
|
* This will print the usage for this program.
|
|
* This will print the usage for this program.
|
|
|
*/
|
|
*/
|
|
|
- private static void usage() {
|
|
|
|
|
|
|
+ private static void usage()
|
|
|
|
|
+ {
|
|
|
System.err.println("Usage: java " + CreateVisibleSignature2.class.getName()
|
|
System.err.println("Usage: java " + CreateVisibleSignature2.class.getName()
|
|
|
+ " <pkcs12-keystore-file> <pin> <input-pdf> <sign-image>\n" + "" +
|
|
+ " <pkcs12-keystore-file> <pin> <input-pdf> <sign-image>\n" + "" +
|
|
|
- "options:\n" +
|
|
|
|
|
- " -tsa <url> sign timestamp using the given TSA server\n" +
|
|
|
|
|
- " -e sign using external signature creation scenario");
|
|
|
|
|
|
|
+ "options:\n" +
|
|
|
|
|
+ " -tsa <url> sign timestamp using the given TSA server\n"+
|
|
|
|
|
+ " -e sign using external signature creation scenario");
|
|
|
|
|
|
|
|
// generate pkcs12-keystore-file with
|
|
// generate pkcs12-keystore-file with
|
|
|
// keytool -storepass 123456 -storetype PKCS12 -keystore file.p12 -genkey -alias client -keyalg RSA
|
|
// keytool -storepass 123456 -storetype PKCS12 -keystore file.p12 -genkey -alias client -keyalg RSA
|