如何用iText 8.0.3 Java库对PDF哈希进行PAdES合规签名?
使用iText 8.0.3实现PDF哈希签名及PAdES合规配置
核心思路
要实现“仅对PDF文档哈希签名、返回签名结果”并满足PAdES合规要求,需基于PdfPadesSigner的扩展能力,通过自定义外部签名逻辑分离文档哈希计算与签名生成步骤,同时配置对应签名属性与合规等级。
步骤1:自定义外部签名实现
实现IExternalSignature接口,在sign方法中获取待签字节数组(对应PDF文档的签名摘要),执行哈希签名逻辑(示例用BouncyCastle模拟,实际可替换为HSM、密钥库等签名服务),返回签名结果。
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.itextpdf.signatures.IExternalSignature; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; public class CustomHashSigner implements IExternalSignature { private final PrivateKey privateKey; private final String hashAlgorithm; private final String encryptionAlgorithm; static { Security.addProvider(new BouncyCastleProvider()); } public CustomHashSigner(PrivateKey privateKey, String hashAlgorithm, String encryptionAlgorithm) { this.privateKey = privateKey; this.hashAlgorithm = hashAlgorithm; this.encryptionAlgorithm = encryptionAlgorithm; } @Override public byte[] sign(byte[] message) throws Exception { // message为iText预处理后的待签字节数组,对应PDF文档的签名摘要 // 执行哈希签名逻辑,返回签名结果 Signature signature = Signature.getInstance(String.format("%swith%s", hashAlgorithm, encryptionAlgorithm), "BC"); signature.initSign(privateKey); signature.update(message); return signature.sign(); } @Override public String getHashAlgorithm() { return hashAlgorithm; } @Override public String getEncryptionAlgorithm() { return encryptionAlgorithm; } }
步骤2:配置PdfPadesSigner与签名属性
初始化PdfPadesSigner,设置PAdES合规等级,添加commitment-type-indication、signer-location等属性,配置时间戳服务(针对B-T/B-LT/B-LTA等级)。
import org.itextpdf.kernel.pdf.PdfReader; import org.itextpdf.kernel.pdf.PdfWriter; import org.itextpdf.signatures.PdfPadesSigner; import org.itextpdf.signatures.SignatureLevel; import org.itextpdf.signatures.SignerProperties; import org.itextpdf.signatures.TSAClientBouncyCastle; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; public class PadesHashSignatureDemo { public static void main(String[] args) throws Exception { // 1. 加载密钥库与私钥证书 KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream("your-keystore.p12"), "password".toCharArray()); String alias = ks.aliases().nextElement(); PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "password".toCharArray()); Certificate[] chain = ks.getCertificateChain(alias); // 2. 初始化PdfPadesSigner PdfReader reader = new PdfReader("input.pdf"); PdfWriter writer = new PdfWriter(new FileOutputStream("output.pdf")); PdfPadesSigner signer = new PdfPadesSigner(reader, writer, null); // 3. 配置签名属性 SignerProperties signerProperties = new SignerProperties(); // 设置签名位置 signerProperties.setLocation("Shanghai, CN"); // 添加承诺类型(示例为"批准"类型) signerProperties.addCommitmentType(SignerProperties.CommitmentTypeProperty.PROOF_OF_APPROVAL); // 添加内容提示 signerProperties.addContentHint("Signed PDF Document", "UTF-8"); // 设置MIME类型(默认application/pdf,可显式指定) signerProperties.setMimeType("application/pdf"); // 配置签名外观(可选,按需设置) signerProperties.setPageNumber(1) .setSignatureRectangle(new float[]{36, 748, 144, 780}) .setSignatureName("Signature"); // 4. 设置PAdES合规等级 // 可选值:PADES_B_B, PADES_B_T, PADES_B_LT, PADES_B_LTA signer.setSignatureLevel(SignatureLevel.PADES_B_LTA); // 5. 配置时间戳服务(针对B-T/B-LT/B-LTA等级) TSAClientBouncyCastle tsaClient = new TSAClientBouncyCastle( "http://your-tsa-server-url", null, null, 4096, "SHA-256" ); signer.setTsaClient(tsaClient); // 6. 绑定自定义签名实现并执行签名 CustomHashSigner customSigner = new CustomHashSigner(privateKey, "SHA-256", "RSA"); signer.signExternal(customSigner, chain); // 若需单独获取文档哈希的签名结果,可在CustomHashSigner的sign方法中存储或返回 } }
关键配置说明
- PAdES等级适配:
- B-B:基础签名,无需时间戳,仅需签名本身
- B-T:添加签名时间戳,需配置
tsaClient - B-LT:长期验证,依赖时间戳确保签名长期有效
- B-LTA:归档长期验证,需额外归档时间戳(iText自动处理,只需配置有效TSA)
- 签名属性添加:
commitment-type-indication:通过addCommitmentType指定,支持PROOF_OF_APPROVAL、PROOF_OF_CREATION等预定义类型content-hints:通过addContentHint添加描述与编码格式signer-location:通过setLocation设置签名者地理位置content-time-stamp:依赖TSA服务配置,自动添加到签名属性中
注意事项
- 实际场景中,
CustomHashSigner的sign方法需替换为真实签名逻辑(如调用硬件加密机API) - 确保依赖包完整:iText 8.0.3核心包、BouncyCastle相关包(bcprov-jdk15on、bcpkix-jdk15on)
- 时间戳服务需支持RFC 3161标准
内容的提问来源于stack exchange,提问作者Xavi Martí Lull
相关产品推荐
相关产品推荐

