You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 20:28:10