使用iText Java对加密PDF签名时如何保留权限?
问题分析与解决方案
你遇到的问题是因为iText在对加密PDF签名时,即使启用preserveEncryption(),也会自动添加允许修改注释、填充表单等权限,导致原有的严格权限(仅允许打印)被放宽。要保留原始权限,需要手动读取原PDF的加密配置,并在签名过程中显式设置这些参数,而不是依赖自动保留机制。
具体修改步骤
- 补充方法参数:
sign()方法需要传入原始加密时使用的用户密码,因为重新设置加密需要该密码。 - 读取原PDF加密信息:提前获取原PDF的权限、PDF版本、加密算法等配置。
- 手动设置加密参数:在创建
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
相关产品推荐
相关产品推荐

