gpt4 book ai didi

java - PDFBox 可以签署所有 'Approval Signatures' 而不是总是将第一个签名设为 'Certified' 吗?

转载 作者:行者123 更新时间:2023-11-29 07:28:12 34 4
gpt4 key购买 nike

正如 Adob​​e 文章 "Digital Signatures in a PDF" 所述:

PDF defines two types of signatures: approval and certification. The differences are as follows: Approval: There can be any number of approval signatures in a document. The field may optionally be associated with FieldMDP permissions. Certification: There can be only one certification signature and it must be the first one in a document. The field is always associated with DocMDP.

使用 PDFBox 示例,我能够成功地将多个签名应用于我的文档:https://github.com/apache/pdfbox/blob/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java为了应用多个签名,我只是使用不同的签名占位符和图像多次运行相同的代码。

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

enter image description here

但在我的例子中,我不希望文件得到认证,我只需要所有签名都是 Apploval 类型,包括第一个。我知道我可以先不可见地验证签名,但我仍然根本不想验证文件。

我试图找到一种设置签名的方法,但无法弄清楚。

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

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


}

最佳答案

您的 signPDF 方法包含此代码:

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

如果您不希望以认证签名开头,请删除此 setMDPPermission 调用。

关于java - PDFBox 可以签署所有 'Approval Signatures' 而不是总是将第一个签名设为 'Certified' 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47294044/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com