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

使用iText Java对加密PDF签名时如何保留权限?

问题分析与解决方案

你遇到的问题是因为iText在对加密PDF签名时,即使启用preserveEncryption(),也会自动添加允许修改注释、填充表单等权限,导致原有的严格权限(仅允许打印)被放宽。要保留原始权限,需要手动读取原PDF的加密配置,并在签名过程中显式设置这些参数,而不是依赖自动保留机制。

具体修改步骤

  1. 补充方法参数:sign()方法需要传入原始加密时使用的用户密码,因为重新设置加密需要该密码。
  2. 读取原PDF加密信息:提前获取原PDF的权限、PDF版本、加密算法等配置。
  3. 手动设置加密参数:在创建PdfSigner后,显式配置加密参数,完全复用原有的权限限制。

修改后的完整代码

package net.opho.olympiad.util;
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.kernel.crypto.DigestAlgorithms;
import com.itextpdf.kernel.mac.MacProperties;
import com.itextpdf.kernel.mac.MacProperties.MacDigestAlgorithm;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.signatures.*;
import lombok.extern.slf4j.Slf4j;
import net.opho.olympiad.entity.Signature;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;

@Slf4j
public class PdfUtil {
    public static byte[] encryptPdf(String pdfPath, String userPassword, String ownerPassword) throws IOException {
        Security.addProvider(BouncyCastleFactoryCreator.getFactory().getProvider());
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        WriterProperties writerProperties = new WriterProperties();
        writerProperties.setPdfVersion(PdfVersion.PDF_2_0).setStandardEncryption(
                userPassword.getBytes(),
                ownerPassword.getBytes(),
                EncryptionConstants.ALLOW_PRINTING,
                EncryptionConstants.ENCRYPTION_AES_256,
                new MacProperties(MacDigestAlgorithm.SHA_256));
        PdfDocument pdfDocument = new PdfDocument(new PdfReader(pdfPath), new PdfWriter(byteArrayOutputStream, writerProperties));
        pdfDocument.close();
        byte[] output = byteArrayOutputStream.toByteArray();
        log.info(pdfPath + " encrypted, byte length: " + output.length);
        return output;
    }

    public static byte[] sign(byte[] pdfFileContent, String userPassword, String ownerPassword, Signature signature, String username) throws IOException, GeneralSecurityException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 读取原PDF的加密配置信息
        PdfDocument tempDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(pdfFileContent), new ReaderProperties().setPassword(ownerPassword.getBytes())));
        PdfEncryptionDict encryptionDict = tempDoc.getEncryptionDict();
        int originalPermissions = encryptionDict.getPermissions();
        PdfVersion pdfVersion = tempDoc.getPdfVersion();
        int encryptionAlgorithm = encryptionDict.getEncryptionAlgorithm();
        tempDoc.close();

        // 初始化签名读取器和签名器
        PdfReader reader = new PdfReader(new ByteArrayInputStream(pdfFileContent), new ReaderProperties().setPassword(ownerPassword.getBytes()));
        StampingProperties stampingProperties = new StampingProperties();
        PdfSigner signer = new PdfSigner(reader, byteArrayOutputStream, stampingProperties);

        // 显式设置加密参数,复用原权限
        WriterProperties writerProperties = new WriterProperties();
        writerProperties.setPdfVersion(pdfVersion)
                .setStandardEncryption(
                        userPassword.getBytes(),
                        ownerPassword.getBytes(),
                        originalPermissions,
                        encryptionAlgorithm,
                        new MacProperties(MacDigestAlgorithm.SHA_256));
        signer.setWriterProperties(writerProperties);

        // 配置签名属性
        SignerProperties signerProperties = new SignerProperties();
        signerProperties.setReason("Document for OPhO user " + username).setLocation("OPhO Official Website");
        signer.setSignerProperties(signerProperties);

        // 加载密钥库并执行签名
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(signature.getKeyStorage()), signature.getKeyStoragePassword().toCharArray());
        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, signature.getKeyStoragePassword().toCharArray());
        IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, provider.getName());
        IExternalDigest digest = new BouncyCastleDigest();

        signer.signDetached(digest, pks, ks.getCertificateChain(alias), null, null, null, 0, PdfSigner.CryptoStandard.CMS);

        byte[] output = byteArrayOutputStream.toByteArray();
        log.info("Signed, byte length: " + output.length);
        return output;
    }
}

关键说明

  • 移除了stampingProperties.preserveEncryption(),改为手动读取并复用原PDF的加密参数,确保权限完全一致。
  • 添加了userPassword参数到sign()方法,调用该方法时需要传入加密PDF时使用的用户密码。
  • 通过PdfEncryptionDict获取原PDF的权限、加密算法等核心配置,保证签名后的PDF权限与原始文件完全一致。

内容的提问来源于stack exchange,提问作者user27326776

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:42:03