存在批准签名时的证明文件
Certified Document when Approval Signature exist
与java一起使用Apache PDFBox进行签名和认证,如果有签名则认证无效,如果有批准则使用JsignPDF可以认证,过程是签名后做认证(盖章)文件
使用 JsignPDF 签名
JsignPDF 认证后的文档:
PDFBox认证无效
PDFBox 的功能经过一些编辑后尝试:
public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl, int page, String SignatureField, boolean isWithQR, String name, UserSignature userSignature) throws IOException, CertificateEncodingException, NoSuchAlgorithmException, OperatorCreationException, CMSException {
if (inputFile == null || !inputFile.exists()) {
throw new IOException("Document for signing does not exist");
}
setTsaUrl(tsaUrl);
// creating output document and prepare the IO streams.
// try (FileOutputStream fos = new FileOutputStream(signedFile);
// PDDocument doc = Loader.loadPDF(inputFile)) {
// creating output document and prepare the IO streams.
FileOutputStream fos = new FileOutputStream(signedFile);
// load document
PDDocument doc = PDDocument.load(inputFile);
int accessPermissions = SigUtils.getMDPPermission(doc);
LogSystem.info("Document permission " + accessPermissions);
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, SignatureField);
if (signature != null) {
rect = acroForm.getField(SignatureField).getWidgets().get(0).getRectangle();
}
}
if (signature == null) {
// create signature dictionary
signature = new PDSignature();
}
if (rect == null) {
rect = createSignatureRectangle(doc, humanRect);
}
// 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");
if(userSignature.getType().equals("sign"))
{
signature.setReason("Reason");
if (accessPermissions == 0) {
COSDictionary sigDict = signature.getCOSObject();
// DocMDP specific stuff
COSDictionary transformParameters = new COSDictionary();
transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
transformParameters.setInt(COSName.P, 0);
transformParameters.setNeedToBeUpdated(true);
COSDictionary referenceDict = new COSDictionary();
referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA256"));
referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
referenceDict.setNeedToBeUpdated(true);
COSArray referenceArray = new COSArray();
referenceArray.add(referenceDict);
sigDict.setItem(COSName.REFERENCE, referenceArray);
referenceArray.setNeedToBeUpdated(true);
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
COSDictionary permsDict = new COSDictionary();
catalogDict.setItem(COSName.PERMS, permsDict);
permsDict.setItem(COSName.DOCMDP, signature);
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
}
if(userSignature.getType().equals("seal"))
{
signature.setReason(userSignature.getQrText());
// try {
// if (doc.getVersion() >= 1.5f && accessPermissions == 0)
// {
// SigUtils.setMDPPermission(doc, signature, 2);
// }
// }catch(Exception e)
// {
// e.printStackTrace();
// }
if (accessPermissions == 0) {
// SigUtils.setMDPPermission(document, signature, 1);
COSDictionary sigDict = signature.getCOSObject();
// DocMDP specific stuff
COSDictionary transformParameters = new COSDictionary();
transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
transformParameters.setInt(COSName.P, 2);
transformParameters.setNeedToBeUpdated(true);
COSDictionary referenceDict = new COSDictionary();
referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA256"));
referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
referenceDict.setNeedToBeUpdated(true);
COSArray referenceArray = new COSArray();
referenceArray.add(referenceDict);
sigDict.setItem(COSName.REFERENCE, referenceArray);
referenceArray.setNeedToBeUpdated(true);
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
COSDictionary permsDict = new COSDictionary();
catalogDict.setItem(COSName.PERMS, permsDict);
permsDict.setItem(COSName.DOCMDP, signature);
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
}
// 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, isWithQR, name, userSignature.getDescOnly(), userSignature.getType(), userSignature.isVisible()));
signatureOptions.setPage(page);
doc.addSignature(signature, signatureInterface, signatureOptions);
doc.getDocumentCatalog().getAcroForm().getField("Signature1").setPartialName(SignatureField);
if (isExternalSigning()) {
this.tsaUrl=tsaUrl;
// ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
// // invoke external signature service
// byte[] cmsSignature = sign(externalSigning.getContent());
ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
// invoke external signature service
byte[] cmsSignature =IOUtils.toByteArray(externalSigning.getContent());
String sgn= signingProcess(cmsSignature);
// set signature bytes received from the service
externalSigning.setSignature(attachSignature(sgn));
} else {
// write incremental (only for signing purpose)
doc.saveIncremental(fos);
}
doc.close();
// } catch (CertificateEncodingException e) {
// e.printStackTrace();
// } catch (NoSuchAlgorithmException e) {
// e.printStackTrace();
// } catch (OperatorCreationException e) {
// e.printStackTrace();
// } catch (CMSException e) {
// e.printStackTrace();
// }
// 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);
}
设置 setMDPermission 以验证文档:
if (accessPermissions == 0) {
COSDictionary sigDict = signature.getCOSObject();
// DocMDP specific stuff
COSDictionary transformParameters = new COSDictionary();
transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
transformParameters.setInt(COSName.P, 1);
transformParameters.setNeedToBeUpdated(true);
COSDictionary referenceDict = new COSDictionary();
referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA256"));
referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
referenceDict.setNeedToBeUpdated(true);
COSArray referenceArray = new COSArray();
referenceArray.add(referenceDict);
sigDict.setItem(COSName.REFERENCE, referenceArray);
referenceArray.setNeedToBeUpdated(true);
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
COSDictionary permsDict = new COSDictionary();
catalogDict.setItem(COSName.PERMS, permsDict);
permsDict.setItem(COSName.DOCMDP, signature);
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
带有参数“sign”的签名和带有参数“seal”的认证
*更新,在 PDFBox 上第一个现有签名总是被认证,而不是第二个签名?最后一个标志可以认证并交换第一个吗?
有什么建议吗?
这个答案本质上是评论的更详细版本,本质上是发现你想做的事情是不可能的。
你问一个方法
Certify Document when Approval Signature exist
根据当前 PDF 规范:
ISO 32000-2:2020 subsection 12.8.1 "General" of 12.8 "Digital signatures"
A PDF document may contain the following standard types of signatures: [...] One or more approval signatures (also known as recipient signatures). These shall follow the certification signature if one is present.
因此,您无法将认证签名有效地添加到已经具有批准签名的 PDF。
(这并不意味着如果您在批准签名后添加认证签名,所有验证器都会自动检测到问题,更不用说正确显示原因了。许多验证器只执行严格来说必要的检查的子集。 ..)
More question if the certification signature come first then next is approval like 3 signature approval, can the last the certification setMDPPermission with 1 ? so at the end the document can't add more approval signature
setMDPPermission
在签名上添加了DocMDP变换,这样的变换使签名成为认证签名。因此,在签署已具有批准签名的文档时使用此方法将创建无效的 PDF 或完全失败。
您可以使用批准签名锁定 PDF 文档,但是,如果您将 Lock 词典添加到带有 P[=35= 的签名字段] 条目 1. 但是请注意,这是一个 ISO 32000-2 功能,最初是作为 Adobe 对 ISO 32000-1 的扩展功能引入的。并非所有 PDF 查看器都支持 ISO 32000-2,因此某些查看器可能不尊重此条目。
与java一起使用Apache PDFBox进行签名和认证,如果有签名则认证无效,如果有批准则使用JsignPDF可以认证,过程是签名后做认证(盖章)文件
使用 JsignPDF 签名
JsignPDF 认证后的文档:
PDFBox认证无效
PDFBox 的功能经过一些编辑后尝试:
public void signPDF(File inputFile, File signedFile, Rectangle2D humanRect, String tsaUrl, int page, String SignatureField, boolean isWithQR, String name, UserSignature userSignature) throws IOException, CertificateEncodingException, NoSuchAlgorithmException, OperatorCreationException, CMSException {
if (inputFile == null || !inputFile.exists()) {
throw new IOException("Document for signing does not exist");
}
setTsaUrl(tsaUrl);
// creating output document and prepare the IO streams.
// try (FileOutputStream fos = new FileOutputStream(signedFile);
// PDDocument doc = Loader.loadPDF(inputFile)) {
// creating output document and prepare the IO streams.
FileOutputStream fos = new FileOutputStream(signedFile);
// load document
PDDocument doc = PDDocument.load(inputFile);
int accessPermissions = SigUtils.getMDPPermission(doc);
LogSystem.info("Document permission " + accessPermissions);
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, SignatureField);
if (signature != null) {
rect = acroForm.getField(SignatureField).getWidgets().get(0).getRectangle();
}
}
if (signature == null) {
// create signature dictionary
signature = new PDSignature();
}
if (rect == null) {
rect = createSignatureRectangle(doc, humanRect);
}
// 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");
if(userSignature.getType().equals("sign"))
{
signature.setReason("Reason");
if (accessPermissions == 0) {
COSDictionary sigDict = signature.getCOSObject();
// DocMDP specific stuff
COSDictionary transformParameters = new COSDictionary();
transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
transformParameters.setInt(COSName.P, 0);
transformParameters.setNeedToBeUpdated(true);
COSDictionary referenceDict = new COSDictionary();
referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA256"));
referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
referenceDict.setNeedToBeUpdated(true);
COSArray referenceArray = new COSArray();
referenceArray.add(referenceDict);
sigDict.setItem(COSName.REFERENCE, referenceArray);
referenceArray.setNeedToBeUpdated(true);
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
COSDictionary permsDict = new COSDictionary();
catalogDict.setItem(COSName.PERMS, permsDict);
permsDict.setItem(COSName.DOCMDP, signature);
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
}
if(userSignature.getType().equals("seal"))
{
signature.setReason(userSignature.getQrText());
// try {
// if (doc.getVersion() >= 1.5f && accessPermissions == 0)
// {
// SigUtils.setMDPPermission(doc, signature, 2);
// }
// }catch(Exception e)
// {
// e.printStackTrace();
// }
if (accessPermissions == 0) {
// SigUtils.setMDPPermission(document, signature, 1);
COSDictionary sigDict = signature.getCOSObject();
// DocMDP specific stuff
COSDictionary transformParameters = new COSDictionary();
transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
transformParameters.setInt(COSName.P, 2);
transformParameters.setNeedToBeUpdated(true);
COSDictionary referenceDict = new COSDictionary();
referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA256"));
referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
referenceDict.setNeedToBeUpdated(true);
COSArray referenceArray = new COSArray();
referenceArray.add(referenceDict);
sigDict.setItem(COSName.REFERENCE, referenceArray);
referenceArray.setNeedToBeUpdated(true);
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
COSDictionary permsDict = new COSDictionary();
catalogDict.setItem(COSName.PERMS, permsDict);
permsDict.setItem(COSName.DOCMDP, signature);
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
}
// 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, isWithQR, name, userSignature.getDescOnly(), userSignature.getType(), userSignature.isVisible()));
signatureOptions.setPage(page);
doc.addSignature(signature, signatureInterface, signatureOptions);
doc.getDocumentCatalog().getAcroForm().getField("Signature1").setPartialName(SignatureField);
if (isExternalSigning()) {
this.tsaUrl=tsaUrl;
// ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
// // invoke external signature service
// byte[] cmsSignature = sign(externalSigning.getContent());
ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
// invoke external signature service
byte[] cmsSignature =IOUtils.toByteArray(externalSigning.getContent());
String sgn= signingProcess(cmsSignature);
// set signature bytes received from the service
externalSigning.setSignature(attachSignature(sgn));
} else {
// write incremental (only for signing purpose)
doc.saveIncremental(fos);
}
doc.close();
// } catch (CertificateEncodingException e) {
// e.printStackTrace();
// } catch (NoSuchAlgorithmException e) {
// e.printStackTrace();
// } catch (OperatorCreationException e) {
// e.printStackTrace();
// } catch (CMSException e) {
// e.printStackTrace();
// }
// 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);
}
设置 setMDPermission 以验证文档:
if (accessPermissions == 0) {
COSDictionary sigDict = signature.getCOSObject();
// DocMDP specific stuff
COSDictionary transformParameters = new COSDictionary();
transformParameters.setItem(COSName.TYPE, COSName.TRANSFORM_PARAMS);
transformParameters.setInt(COSName.P, 1);
transformParameters.setNeedToBeUpdated(true);
COSDictionary referenceDict = new COSDictionary();
referenceDict.setItem(COSName.TYPE, COSName.SIG_REF);
referenceDict.setItem(COSName.TRANSFORM_METHOD, COSName.DOCMDP);
referenceDict.setItem(COSName.DIGEST_METHOD, COSName.getPDFName("SHA256"));
referenceDict.setItem(COSName.TRANSFORM_PARAMS, transformParameters);
referenceDict.setNeedToBeUpdated(true);
COSArray referenceArray = new COSArray();
referenceArray.add(referenceDict);
sigDict.setItem(COSName.REFERENCE, referenceArray);
referenceArray.setNeedToBeUpdated(true);
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
COSDictionary permsDict = new COSDictionary();
catalogDict.setItem(COSName.PERMS, permsDict);
permsDict.setItem(COSName.DOCMDP, signature);
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
带有参数“sign”的签名和带有参数“seal”的认证
*更新,在 PDFBox 上第一个现有签名总是被认证,而不是第二个签名?最后一个标志可以认证并交换第一个吗?
有什么建议吗?
这个答案本质上是评论的更详细版本,本质上是发现你想做的事情是不可能的。
你问一个方法
Certify Document when Approval Signature exist
根据当前 PDF 规范:
ISO 32000-2:2020 subsection 12.8.1 "General" of 12.8 "Digital signatures" |
---|
A PDF document may contain the following standard types of signatures: [...] One or more approval signatures (also known as recipient signatures). These shall follow the certification signature if one is present. |
因此,您无法将认证签名有效地添加到已经具有批准签名的 PDF。
(这并不意味着如果您在批准签名后添加认证签名,所有验证器都会自动检测到问题,更不用说正确显示原因了。许多验证器只执行严格来说必要的检查的子集。 ..)
More question if the certification signature come first then next is approval like 3 signature approval, can the last the certification setMDPPermission with 1 ? so at the end the document can't add more approval signature
setMDPPermission
在签名上添加了DocMDP变换,这样的变换使签名成为认证签名。因此,在签署已具有批准签名的文档时使用此方法将创建无效的 PDF 或完全失败。
您可以使用批准签名锁定 PDF 文档,但是,如果您将 Lock 词典添加到带有 P[=35= 的签名字段] 条目 1. 但是请注意,这是一个 ISO 32000-2 功能,最初是作为 Adobe 对 ISO 32000-1 的扩展功能引入的。并非所有 PDF 查看器都支持 ISO 32000-2,因此某些查看器可能不尊重此条目。