如何将PFX转换为PNG Base64用于PDF数字签名?
问题原因
你混淆了PFX密钥库文件和可见签名外观图片的用途:
- PFX是包含私钥和证书链的密钥库,用于完成PDF的数字签名加密与验证,本身不是图像格式;
signatureImageInBase64是用于显示在PDF上的可见签名外观(比如手写签名的PNG图片),必须是合法的图像文件转Base64,不能用PFX文件替代。
正确实现步骤与代码
以下是使用Apache PDFBox完成带可见签名的PDF数字签名的完整Java代码:
依赖配置(Maven)
确保引入PDFBox签名相关依赖及BouncyCastle加密库:
<dependencies> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.32</version> </dependency> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox-signature</artifactId> <version>2.0.32</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> </dependencies>
签名代码
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.*; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSignature; import java.io.*; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; import java.util.Base64; public class PDFDigitalSigner { public static void main(String[] args) throws Exception { // 配置参数 String inputPdf = "待签名文件.pdf"; String outputPdf = "已签名文件.pdf"; String pfxFile = "你的证书.pfx"; String pfxPassword = "PFX密码"; String signatureImage = "签名外观图片.png"; // 合法的图像文件 // 1. 加载PFX密钥库,获取私钥与证书链 KeyStore keyStore = KeyStore.getInstance("PKCS12"); try (FileInputStream fis = new FileInputStream(pfxFile)) { keyStore.load(fis, pfxPassword.toCharArray()); } String alias = keyStore.aliases().nextElement(); PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, pfxPassword.toCharArray()); Certificate[] certChain = keyStore.getCertificateChain(alias); // 2. 将签名外观图片转为Base64 String imageBase64; try (FileInputStream imageIn = new FileInputStream(signatureImage); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int len; while ((len = imageIn.read(buffer)) != -1) { baos.write(buffer, 0, len); } imageBase64 = Base64.getEncoder().encodeToString(baos.toByteArray()); } // 3. 初始化PDF文档与签名对象 try (PDDocument document = PDDocument.load(new File(inputPdf))) { PDSignature signature = new PDSignature(); signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); signature.setName("签名人"); signature.setLocation("签名地点"); signature.setReason("文档确认"); signature.setSignDate(new java.util.Date()); // 4. 设置可见签名外观 PDVisibleSignature visibleSig = new PDVisibleSignature(); visibleSig.setPage(0); // 签名在第1页(索引从0开始) visibleSig.setXAxis(150); // 签名框左下角X坐标 visibleSig.setYAxis(50); // 签名框左下角Y坐标 visibleSig.setWidth(220); // 签名框宽度 visibleSig.setHeight(100); // 签名框高度 PDVisibleSigProperties sigProps = new PDVisibleSigProperties(); sigProps.setSignature(signature); sigProps.setPage(visibleSig.getPage()); sigProps.setVisualSignEnabled(true); sigProps.setPdVisibleSignature(visibleSig); sigProps.setImage(PDVisibleSigProperties.createImageFromByteArray(Base64.getDecoder().decode(imageBase64))); sigProps.setZoom(0.8f); // 图片缩放比例 sigProps.buildSignature(); // 5. 执行签名 ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(new FileOutputStream(outputPdf)); byte[] content = externalSigning.getContent(); // 生成CMS签名数据 org.bouncycastle.cms.CMSSignedDataGenerator gen = new org.bouncycastle.cms.CMSSignedDataGenerator(); org.bouncycastle.cert.jcajce.JcaCertStore certStore = new org.bouncycastle.cert.jcajce.JcaCertStore(java.util.Arrays.asList(certChain)); gen.addSignerInfoGenerator( new org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder( new org.bouncycastle.jce.provider.BouncyCastleProvider() ).build( new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder("SHA256withRSA").build(privateKey), (java.security.cert.X509Certificate) certChain[0] ) ); gen.addCertificates(certStore); org.bouncycastle.cms.CMSSignedData signedData = gen.generate(new org.bouncycastle.cms.CMSProcessableByteArray(content), false); externalSigning.setSignature(signedData.getEncoded()); } } }
关键注意事项
- PFX文件仅用于提供签名所需的私钥和证书,不能作为可见签名的图片使用;
- 可见签名图片必须是PNG/JPG等合法图像格式,可自行制作手写签名扫描件或公司签章图片;
- 签名坐标需根据PDF页面实际尺寸调整,避免超出页面边界;
- 确保PFX文件密码正确,且证书未过期、包含有效的私钥。
内容的提问来源于stack exchange,提问作者Jake
相关产品推荐
相关产品推荐

