使用iText 5.5.13实现外部数字签名时遇NO FIELD错误求助
iText 5.5.13 外部数字签名 signDeferred 报 NO FIELD 错误解决
问题描述
使用Java结合iText 5.5.13实现外部数字签名时,执行MakeSignature.signDeferred()方法触发NO FIELD错误,签名流程分为4步:
- 准备待签名PDF
- 计算哈希值
- 对哈希值进行签名
- 将签名结果合并到源PDF
异常信息:
Exception in thread "main" com.itextpdf.text.DocumentException: No field
at com.itextpdf.text.pdf.security.MakeSignature.signDeferred(MakeSignature.java:291)
at test.SignerFinal.main(SignerFinal.java:219)
相关代码:
//step 1 - preparing pdf to sign int contentEstimated2 = 8192; String src = "src.pdf"; String dest = "dest.pdf"; InputStream is = new FileInputStream(src); PdfReader reader = new PdfReader(is); reader.setAppendable(true); ByteArrayOutputStream baos = new ByteArrayOutputStream (); FileOutputStream os = new FileOutputStream(dest); PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0'); PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); String MotSigner = "GIF.jpg"; //IMAGE Image image = Image.getInstance(MotSigner); appearance.setImage(image); appearance.setLayer2Text(""); Rectangle rect = new Rectangle(250, 795, 300, 840); appearance.setVisibleSignature(rect, 1, "sig1"); ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED); MakeSignature.signExternalContainer(appearance, external, size); // step 2 - calculating the hash is = new ByteArrayInputStream(baos.toByteArray()); BouncyCastleDigest digest = new BouncyCastleDigest(); byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest("SHA-256")); //step 3 - signing the hash //sending hash to signer server. getting SignedBytes as result byte[] SignHashedPDF = Base64.decode(SignedBytes); //step 4 - combine hash to the source pdf reader = new PdfReader(src); FileOutputStream os1 = new FileOutputStream(dest); ExternalSignatureContainer external1 = new MyExternalSignatureContainer(SignHashedPDF); MakeSignature.signDeferred(reader, "sig1", os1, external1); os.close(); } class MyExternalSignatureContainer implements ExternalSignatureContainer { protected byte[] sig; public MyExternalSignatureContainer(byte[] sig) { this.sig = sig; } public byte[] sign(InputStream is) { return sig; } public void modifySigningDictionary(PdfDictionary signDic) { } }
问题原因
第4步调用signDeferred时,传入的是原始的src.pdf,但签名域sig1是在第1步中添加到baos对应的临时PDF中的,原始PDF中根本不存在这个签名域,因此触发"No field"错误。
修复方案
核心修改点
- 第1步执行完
signExternalContainer后,将baos中带有空白签名域的临时PDF保存到文件或内存; - 第4步调用
signDeferred时,读取带有空白签名域的临时PDF,而非原始的src.pdf。
修改后的关键代码片段
// step 1 修改:保存带有空白签名域的临时PDF File tempFile = new File("temp.pdf"); try (FileOutputStream tempOs = new FileOutputStream(tempFile)) { baos.writeTo(tempOs); } // step 4 修改:读取临时文件而非原始src.pdf try (PdfReader reader = new PdfReader(new FileInputStream(tempFile)); FileOutputStream os1 = new FileOutputStream(dest)) { ExternalSignatureContainer external1 = new MyExternalSignatureContainer(SignHashedPDF); MakeSignature.signDeferred(reader, "sig1", os1, external1); } // 可选:删除临时文件 tempFile.delete();
其他细节修正
- 第1步中
size变量未定义,替换为已声明的contentEstimated2; - 补全
MyExternalSignatureContainer构造方法的大括号; - 使用try-with-resources语法自动关闭流,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Erez
相关产品推荐
相关产品推荐

