Java PDFBox是否可以全部签名';批准签字';而不是总是以';认证';?

Java PDFBox是否可以全部签名';批准签字';而不是总是以';认证';?,java,pdf,pdfbox,sign,Java,Pdf,Pdfbox,Sign,正如Adobe文章所述: PDF定义了两种类型的签名:批准和认证。这个 差异如下: 批准:文档中可以有任意数量的批准签名。该字段可以选择性地与FieldMDP关联 权限。 认证:只能有一个认证签名,并且必须是文档中的第一个签名。该字段始终是关联的 使用DocMDP 使用PDFBox示例,我能够成功地将多个签名应用于我的文档: 为了应用多个签名,我只是用不同的签名占位符和图像多次运行相同的代码 但我所区别的是,即使我运行相同的代码,它总是将第一个签名设置为认证,而将所有其他签名设置为批准 但在我

正如Adobe文章所述:

PDF定义了两种类型的签名:批准和认证。这个 差异如下: 批准:文档中可以有任意数量的批准签名。该字段可以选择性地与FieldMDP关联 权限。 认证:只能有一个认证签名,并且必须是文档中的第一个签名。该字段始终是关联的 使用DocMDP

使用PDFBox示例,我能够成功地将多个签名应用于我的文档: 为了应用多个签名,我只是用不同的签名占位符和图像多次运行相同的代码

但我所区别的是,即使我运行相同的代码,它总是将第一个签名设置为认证,而将所有其他签名设置为批准

但在我的情况下,我不希望文档被认证,我只需要所有签名都是Apploval类型,包括第一个。我知道我可以先证明签名,但我还是不想证明文件

我试图找到一种设置签名的方法,但无法找到它

下面是我对示例代码的用法(其他类在上面的GitHub链接中):

公共类SignnerPDFBoxExample扩展CreateSignatureBase{
私人签名签名;
私人PDVisibleSignDesigner visibleSignDesigner;
私有最终PDVisibleSigProperties visibleSignatureProperties=新PDVisibleSigProperties();
私有布尔值lateExternalSigning=false;
公共静态void main(字符串[]args)引发异常{
File ksFile=新文件(“keystore.jks”);
KeyStore KeyStore=KeyStore.getInstance(“JKS”);
char[]pin=“123456”。toCharArray();
load(新文件输入流(ksFile),pin);
SignnerPDFBoxExample signer=new SignnerPDFBoxExample(密钥库,pin.clone());
String inputFilename=“Four\u Signature\u template.pdf”;
文件documentFile=新文件(inputFilename);
文件签名文档文件;
int page=1;
try(FileInputStream imageStream=newfileinputstream(“client\u signature.jpg”))
{
字符串名称=documentFile.getName();
String substring=name.substring(0,name.lastIndexOf('.');
signedDocumentFile=新文件(documentFile.getParent(),子字符串+“_signed.pdf”);
//这里的页面是1-based
setVisibleSignDesigner(inputFilename,0,0,-50,imageStream,第页);
}
signer.setVisibleSignatureProperties(“名称”、“位置”、“使用PDFBox签名”、0,第页,true);
signer.signPDF(documentFile,signedDocumentFile,null,“certifySignature”);
}
公共布尔值isLateExternalSigning()
{
返回外部签名;
}
/**
*设置延迟外部签名。如果要激活
*签名将保留并添加到额外步骤中,而不使用PDFBox方法。这是禁用的
*默认情况下。
*
*@param lateExternalSigning
*/
公共void setLateExternalSigning(布尔lateExternalSigning)
{
this.lateExternalSigning=lateExternalSigning;
}
/**
*为新签名字段设置可见签名设计器。
* 
*@param文件名
*@param x签名字段的位置
*@param y签名字段的位置
*@param zoomPercent
*@param-imageStream
*@param页签名应放在
*@抛出异常
*/
public void setVisibleSignDesigner(字符串文件名,int x,int y,int zoomPercent,
FileInputStream图像流,int页)
抛出IOException
{
visibleSignDesigner=新的PDVisibleSignDesigner(文件名、图像流、页面);
visibleSignDesigner.xAxis(x).yAxis(y).zoom(zoomPercent).adjustForRotation();
}
/**
*为现有签名字段设置可见签名设计器。
* 
*@param zoomPercent
*@param-imageStream
*@抛出异常
*/
public void setVisibleSignDesigner(int zoomPercent,FileInputStream imageStream)
抛出IOException
{
visibleSignDesigner=新的PDVisibleSignDesigner(imageStream);
visibleSignDesigner.zoom(zoomPercent);
}
/**
*为新签名字段设置可见签名属性。
* 
*@param name
*@param位置
*@param原因
*@param preferredSize
*@param页
*@param visualSignEnabled
*@抛出异常
*/
public void setVisibleSignatureProperties(字符串名称、字符串位置、字符串原因、int preferredSize、,
整型页面,布尔型visualSignEnabled)引发IOException
{
visibleSignatureProperties.signerName(名称)。signerLocation(位置)。signatureReason(原因)。
preferredSize(preferredSize).page(page).visualSignEnabled(visualSignEnabled)。
setPdVisibleSignature(visibleSignDesigner);
}
/**
*为现有签名字段设置可见签名属性。
* 
*@param name
*@param位置
*@param原因
*@param visualSignEnabled
*@抛出异常
*/
public void setVisibleSignatureProperties(字符串名称、字符串位置、字符串原因、,
布尔值(已启用)引发IOException
{
visibleSignatureProperties.signerName(名称)。signerLocation(位置)。signatureReason(原因)。
visualSignEnabled(visualSignEnabled).setPdVisibleSignature(visibleSignDesigner);
}
/**
*使用密钥库(pkcs12)初始化签名创建者并将其锁定
*签名时应使用。
*
*@param密钥库是pkcs12密钥库。
*@param pin是用于
public class SignnerPDFBoxExample extends CreateSignatureBase {

    private SignatureOptions signatureOptions;
    private PDVisibleSignDesigner visibleSignDesigner;
    private final PDVisibleSigProperties visibleSignatureProperties = new PDVisibleSigProperties();
    private boolean lateExternalSigning = false;

    public static void main(String[] args) throws Exception {

        File ksFile = new File("keystore.jks");
        KeyStore keystore = KeyStore.getInstance("JKS");
        char[] pin = "123456".toCharArray();
        keystore.load(new FileInputStream(ksFile), pin);

        SignnerPDFBoxExample signer = new SignnerPDFBoxExample(keystore, pin.clone());
        String inputFilename = "Four_Signature_template.pdf";

        File documentFile = new File(inputFilename);
        File signedDocumentFile;
        int page = 1;
        try (FileInputStream imageStream = new FileInputStream("client_signature.jpg"))
        {
            String name = documentFile.getName();
            String substring = name.substring(0, name.lastIndexOf('.'));
            signedDocumentFile = new File(documentFile.getParent(), substring + "_signed.pdf");
            // page is 1-based here
            signer.setVisibleSignDesigner(inputFilename, 0, 0, -50, imageStream, page);
        }
        signer.setVisibleSignatureProperties("name", "location", "Signed using PDFBox", 0, page, true);
        signer.signPDF(documentFile, signedDocumentFile, null, "certifySignature");
    }

    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;
    }

    /**
     * Set visible signature designer for a new signature field.
     * 
     * @param filename
     * @param x position of the signature field
     * @param y position of the signature field
     * @param zoomPercent
     * @param imageStream
     * @param page the signature should be placed on
     * @throws IOException
     */
    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();
    }

    /**
     * Set visible signature designer for an existing signature field.
     * 
     * @param zoomPercent
     * @param imageStream
     * @throws IOException
     */
    public void setVisibleSignDesigner(int zoomPercent, FileInputStream imageStream) 
            throws IOException
    {
        visibleSignDesigner = new PDVisibleSignDesigner(imageStream);
        visibleSignDesigner.zoom(zoomPercent);
    }

    /**
     * Set visible signature properties for new signature fields.
     * 
     * @param name
     * @param location
     * @param reason
     * @param preferredSize
     * @param page
     * @param visualSignEnabled
     * @throws IOException
     */
    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);
    }

    /**
     * Set visible signature properties for existing signature fields.
     * 
     * @param name
     * @param location
     * @param reason
     * @param visualSignEnabled
     * @throws IOException
     */
    public void setVisibleSignatureProperties(String name, String location, String reason,
            boolean visualSignEnabled) throws IOException
    {
        visibleSignatureProperties.signerName(name).signerLocation(location).signatureReason(reason).
                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 SignnerPDFBoxExample(KeyStore keystore, char[] pin)
            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException
    {
        super(keystore, pin);
    }

    /**
     * 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 = 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.

            PDSignature signature;

            // 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
            // 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);
            }

            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())
                {
                    // 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);

            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;
    }

    /**
     * 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");
    }


}
        // 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);
        }