Hello

I used apache PDFBox 3.0.0-RC1 to produce 2 visible signatures on a PDF.
Signatures are validated by Adobe Reader but only the first signature is visible and I don't know why. I attached a test document and the java source code. The java file is from the official PDFBox docuentation with small modifications.

Thank you



--
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
package PDFSignature;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.io.IOUtils;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import 
org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import 
org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
import 
org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties;
import 
org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSignDesigner;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.apache.pdfbox.util.Hex;

/**
 * This is an example for visual signing a pdf.
 * 
 * @see CreateSignature
 * @author Vakhtang Koroghlishvili
 */
public class CreateVisibleSignature extends CreateSignatureBase {
        private SignatureOptions signatureOptions;
        private PDVisibleSignDesigner visibleSignDesigner;
        private final PDVisibleSigProperties visibleSignatureProperties = new 
PDVisibleSigProperties();
        private boolean lateExternalSigning = false;

        public boolean isLateExternalSigning() {
                return lateExternalSigning;
        }

        /**
         * Set late external signing. Enable this if you want to activate the 
demo
         * code where the signature is kept and added in an extra step without 
using
         * PDFBox methods. This is disabled by default.
         *
         * @param lateExternalSigning
         */
        public void setLateExternalSigning(boolean lateExternalSigning) {
                this.lateExternalSigning = lateExternalSigning;
        }

        public void setVisibleSignDesigner(String filename, int x, int y, int 
zoomPercent, FileInputStream imageStream,
                        int page) throws IOException {
                visibleSignDesigner = new PDVisibleSignDesigner(filename, 
imageStream, page);
                
visibleSignDesigner.xAxis(x).yAxis(y).zoom(zoomPercent).adjustForRotation();
        }

        public void setVisibleSignatureProperties(String name, String location, 
String reason, int preferredSize, int page,
                        boolean visualSignEnabled) throws IOException {
                
visibleSignatureProperties.signerName(name).signerLocation(location).signatureReason(reason)
                                
.preferredSize(preferredSize).page(page).visualSignEnabled(visualSignEnabled)
                                .setPdVisibleSignature(visibleSignDesigner);
        }

        /**
         * Initialize the signature creator with a keystore (pkcs12) and pin 
that
         * should be used for the signature.
         *
         * @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
         * @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 CreateVisibleSignature(KeyStore keystore, char[] pin) throws 
KeyStoreException, UnrecoverableKeyException,NoSuchAlgorithmException, 
IOException, CertificateException {
            super(keystore, pin);
            Enumeration enumeration = keystore.aliases();
            while (enumeration.hasMoreElements()) {
                String alias = (String) enumeration.nextElement();

                if(alias.contains("Sterpu")) {
//                if(alias.contains("Luchian")) {
                   PrivateKey privKey  = (PrivateKey) keystore.getKey(alias, 
null);
                   this.setPrivateKey(privKey);
                   this.setCertificate(keystore.getCertificate(alias));
                }
            }
        }

        /**
         * 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 tsaClient
         *            optional TSA client
         * @throws IOException
         */
        public void signPDF(File inputFile, File signedFile, TSAClient 
tsaClient) throws IOException {
                this.signPDF(inputFile, signedFile, tsaClient, null);
        }

        /**
         * 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 tsaClient
         *            optional TSA client
         * @param signatureFieldName
         *            optional name of an existing (unsigned) signature field
         * @throws IOException
         */
        public void signPDF(File inputFile, File signedFile, TSAClient 
tsaClient, String signatureFieldName)
                        throws IOException {
                setTsaClient(tsaClient);

                if (inputFile == null || !inputFile.exists()) {
                        throw new IOException("Document for signing does not 
exist");
                }

                // creating output document and prepare the IO streams.
                FileOutputStream fos = new FileOutputStream(signedFile);

                try (PDDocument doc = Loader.loadPDF(inputFile)) {
                        int accessPermissions = 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.
//                        PDPage blankPage = new PDPage();
//                        doc.addPage(blankPage);
//                        int countPages = doc.getNumberOfPages();
                        
                        PDSignature signature = null;

                        // sign a PDF with an existing empty signature, as 
created by the
                        // CreateEmptySignatureForm example.
                        //signature = findExistingSignature(doc, 
signatureFieldName);

                        if (signature == null) {
                            // create signature dictionary
                            signature = new PDSignature();
                        }

                        // Optional: certify
//                      if (accessPermissions == 0) {
//                            setMDPPermission(doc, signature, 2);
//                      }

                        PDAcroForm acroForm = 
doc.getDocumentCatalog().getAcroForm();
                        if (acroForm != null && acroForm.getNeedAppearances()) {
                                // PDFBOX-3738 NeedAppearances true results in 
visible signature
                                // becoming invisible
                                // with Adobe Reader
                                if (acroForm.getFields().isEmpty()) {
                                    
System.err.println("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.err.println("acroForm.getFields().isnotEmpty()");
                                        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);

                        if (visibleSignatureProperties != null) {
                                // this builds the signature structures in a 
separate document
                                visibleSignatureProperties.buildSignature();

                                
signature.setName(visibleSignatureProperties.getSignerName());
                                
signature.setLocation(visibleSignatureProperties.getSignerLocation());
                                
signature.setReason(visibleSignatureProperties.getSignatureReason());
                        }

                        // 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
                        if (visibleSignatureProperties != null && 
visibleSignatureProperties.isVisualSignEnabled()) {
                            signatureOptions = new SignatureOptions();
                            
signatureOptions.setVisualSignature(visibleSignatureProperties.getVisibleSignature());
                            
signatureOptions.setPage(visibleSignatureProperties.getPage() - 1);
                            doc.addSignature(signature, signatureInterface, 
signatureOptions);
                        } else {
                            doc.addSignature(signature, signatureInterface);
                        }

                        if (isExternalSigning()) {
                                System.out.println("Signing externally " + 
signedFile.getName());
                                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
                                // PODDocument.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);
                        }
                }

                // Do not close signatureOptions before saving, because some 
COSStream
                // objects within
                // are transferred to the signed document.
                // Do not allow signatureOptions get out of scope before 
saving, because
                // then the COSDocument
                // in signature options might by closed by gc, which would close
                // COSStream objects prematurely.
                // See https://issues.apache.org/jira/browse/PDFBOX-3743
                IOUtils.closeQuietly(signatureOptions);
        }

        // Find an existing signature (assumed to be empty). You will usually 
not
        // need this.
        private PDSignature findExistingSignature(PDDocument doc, String 
sigFieldName) {
                PDSignature signature = null;
                PDSignatureField signatureField;
                PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
                if (acroForm != null) {
                        signatureField = (PDSignatureField) 
acroForm.getField(sigFieldName);
                        if (signatureField != null) {
                                // retrieve signature dictionary
                                signature = signatureField.getSignature();
                                if (signature == null) {
                                        signature = new PDSignature();
                                        // after solving PDFBOX-3524
                                        // signatureField.setValue(signature)
                                        // until then:
                                        
signatureField.getCOSObject().setItem(COSName.V, signature);
                                } else {
                                        throw new IllegalStateException("The 
signature field " + sigFieldName + " is already signed.");
                                }
                        }
                }
                return signature;
        }

        /**
         * Arguments are [0] key store [1] pin [2] document that will be signed 
[3]
         * image of visible signature
         *
         * @param args
         * @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, IOException,
                        NoSuchAlgorithmException, UnrecoverableKeyException {
                // generate with
                // keytool -storepass 123456 -storetype PKCS12 -keystore 
file.p12
                // -genkey -alias client -keyalg RSA
                args = new String[4];
                args[0]="-";
                args[1]="-";
                args[2]="C:\\Users\\Administrator\\Desktop\\5172.pdf";
                args[3]="signature_frame.png";
                if (args.length < 4) {
                        usage();
                        System.exit(1);
                }

                String tsaUrl = null;
                // External signing is needed if you are using an external 
signing
                // service, e.g. to sign
                // several files at once.
                boolean externalSig = false;
//              for (int i = 0; i < args.length; i++) {
//                      if (args[i].equals("-tsa")) {
//                              i++;
//                              if (i >= args.length) {
//                                      usage();
//                                      System.exit(1);
//                              }
//                              tsaUrl = args[i];
//                      }
//                      if (args[i].equals("-e")) {
//                              externalSig = true;
//                      }
//              }

//              File ksFile = new File(args[0]);
//              KeyStore keystore = KeyStore.getInstance("PKCS12");
//              char[] pin = args[1].toCharArray();
//              keystore.load(new FileInputStream(ksFile), pin);
                KeyStore keystore = KeyStore.getInstance("Windows-MY");
                keystore.load(null, null);

                // TSA client
//              TSAClient tsaClient = null;
//              if (tsaUrl != null) {
//                      MessageDigest digest = 
MessageDigest.getInstance("SHA-256");
//                      tsaClient = new TSAClient(new URL(tsaUrl), null, null, 
digest);
//              }
                BufferedImage image = ImageIO.read(new File(args[3]));
                Font font = new Font("Arial", Font.BOLD, 11);
                Graphics g = image.getGraphics();
//                BufferedImage bg = ImageIO.read(new File("cnas.png"));
//                g.drawImage(bg, 0, 0, null);

                g.setFont(font);
                g.setColor(Color.BLACK);
                g.drawString("Semnat electronic de Sterpu Victor Silviu", 10, 
20);
                g.drawString("Casa de Asigurări de Sănătate Neamț", 10, 40);
                g.drawString("Titular contract asistență medicală primară", 10, 
60);
                final long unixTime = System.currentTimeMillis() / 1000L;
                String[] data = args[3].split("\\.");
                String extension = data[data.length-1];
                String datedImagePath = "image_"+unixTime+"."+extension;
                ImageIO.write(image, extension, new File(datedImagePath));
                
                File documentFile = new File(args[2]);
                int page;
                File outputFile;
                try (PDDocument doc = Loader.loadPDF(documentFile)) {
//                    PDPage blankPage = new PDPage(PDRectangle.A4);
//                    doc.addPage(blankPage);
                    page = doc.getNumberOfPages();
                    String name1 = documentFile.getName();
                    String substring1 = name1.substring(0, 
name1.lastIndexOf('.'));
                    outputFile = new File(documentFile.getParent(), substring1 
+ "_out.pdf");
                    FileOutputStream fileOS = new FileOutputStream(outputFile);
                    doc.save(fileOS);
                }

//              CreateVisibleSignature signing = new 
CreateVisibleSignature(keystore, pin.clone());
                File nf = sign(outputFile, keystore, datedImagePath, 
externalSig, page, 10, 10);
                sign(nf, keystore, datedImagePath, externalSig, page, 10, 280);

        }
        
        public static File sign(File outputFile, KeyStore keystore, String 
datedImagePath, boolean externalSig, int page, int x, int y) {
            try {
                CreateVisibleSignature signing = new 
CreateVisibleSignature(keystore, null);

                File signedDocumentFile;
                try (FileInputStream imageStream = new 
FileInputStream(datedImagePath)) {
                    String name = outputFile.getName();
                    String substring = name.substring(0, name.lastIndexOf('.'));
                    signedDocumentFile = new File(outputFile.getParent(), 
substring + "_signed.pdf");
                    
signing.setVisibleSignDesigner(outputFile.getAbsolutePath(), x, y, -10, 
imageStream, page);
                }
                signing.setVisibleSignatureProperties("name", "location", 
"Security", 0, page, true);
                signing.setExternalSigning(externalSig);
                signing.signPDF(outputFile, signedDocumentFile, null);
                return signedDocumentFile;
            } catch (Exception ex) {
                
Logger.getLogger(CreateVisibleSignature.class.getName()).log(Level.SEVERE, 
null, ex);
                return null;
            }
        }

        /**
         * This will print the usage for this program.
         */
        private static void usage() {
                System.err.println("Usage: java " + 
CreateVisibleSignature.class.getName()
                                + " <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");
        }

}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to