【发布时间】:2016-09-02 15:42:24
【问题描述】:
对于一个项目,我必须在工作流程中由多人在另外创建的页面上对 PDF 进行数字签名。为了实现这一点,我们使用 iText 7 库和以下代码,基于 Bruno Lowagie 的示例:
public static void main(String[] args) throws IOException, GeneralSecurityException, XMPException {
String path = "F:/Java/keystores/testPdfSign";
char[] pass = "test".toCharArray();
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(path), pass);
String alias = "";
Enumeration<String> aliases = ks.aliases();
while (alias.equals("tester")==false && aliases.hasMoreElements())
{
alias = aliases.nextElement();
}
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
PDFSign app = new PDFSign();
app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA1, "SunJSSE", PdfSigner.CryptoStandard.CMS, "Test", "Test", null, null, null, 0);
}
public void sign(String src, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
String reason, String location,
Collection<ICrlClient> crlList,
IOcspClient ocspClient,
ITSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, XMPException {
// Creating the reader and the signer
PdfDocument document = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST+"_temp"));
if (initial == true)
{
document.addNewPage();
}
int pageCount = document.getNumberOfPages();
document.close();
PdfSigner signer = new PdfSigner(new PdfReader(DEST+"_temp"), new FileOutputStream(DEST), true);
// Creating the appearance
if (initial == true)
{
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
}
PdfSignatureAppearance appearance = signer.getSignatureAppearance()
.setReason(reason)
.setLocation(location)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(10, 400, 100, 100);
appearance
.setPageRect(rect)
.setPageNumber(pageCount);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
signer.setFieldName(signer.getNewSigFieldName());
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ProviderDigest digest = new ProviderDigest(provider);
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
这会导致 PDF 的新签名版本中的签名无效,因为 Adobe Acrobat Reader 表示签名后已对其进行了编辑。令人惊讶的是,当我在 Foxit Reader 中打开文件时,它说它没有被修改并且是有效的。
另外我尝试的是省略添加新页面的第一步,只在原始文档的最后一页签名,然后签名在 Adobe Reader 中有效,但我的情况没有解决方案,作为额外的页面是必须的。
我尝试的另一件事是没有将certificateLevel设置为CERTIFIED_FORM_FILLING_AND_ANNOTATIONS,而是将其保留为默认NOT_CERTIFIED,这样我在新页面上也有一个有效的签名,但这也不是一个解决方案,因为它赢了以后不要让我添加任何其他签名。
有人知道 Adobe Reader 将签名评为无效的原因和/或有解决此问题的方法吗?
提前致谢
大卫
【问题讨论】:
-
您能出示您的原始 pdf 文件吗?你的描述听起来好像已经签名了。
-
我尝试了不同的 pdf,都没有签名,所以这不应该是问题
-
我只是试图重现您的问题,但结果是
java.security.NoSuchAlgorithmException: no such algorithm: SHA1 for provider SunJSSE。确实,您对提供程序SunJSSE的使用几乎没有意义。 -
我将您的代码更改为使用“BC”而不是“SunJSSE”,并预先注册了 BouncyCastle 提供程序。现在运行它会产生一个正确的签名,“文档自认证以来没有被修改过”。
-
首先感谢您的帮助!不幸的是,将提供程序更改为 BouncyCastle 对我来说会导致同样的问题,你能让我看看你的代码吗?你也能解释一下为什么使用
SunJSSE没有意义吗?
标签: java pdf adobe signature itext7