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

PDFBox设置签名锁后仍可添加有效签名如何解决

PDFBox认证签名锁定后新增签名原签名仍有效问题

问题描述

可参考我此前提出的关于签名锁定后禁止新增其他审批/签名的相关问题。

我目前已成功实现文档认证加锁功能,但测试发现锁定后仍可对文档执行签名操作、新增其他签名或审批内容,且操作后原有签名仍显示为有效状态,不符合预期:

完成签名锁定后,若有其他主体对文档进行签名,原有签名应变为无效状态。

Adobe阅读器签名状态截图

当前文档版本流转路径为:
rev 1 -> rev 2 -> 认证 -> 锁定 -> 新增rev 4(新增后签名仍为有效状态)

现有实现代码

// 创建空签名字段
PDDocument docEx=PDDocument.load(inputFile);
PDAcroForm acroFormField = docEx.getDocumentCatalog().getAcroForm();
acroFormField.getCOSObject().setNeedToBeUpdated(true);
COSObject fields = acroFormField.getCOSObject().getCOSObject(COSName.FIELDS);
if (fields != null)
    fields.setNeedToBeUpdated(true);

acroFormField.setSignaturesExist(true);
acroFormField.setAppendOnly(true);
acroFormField.getCOSObject().setDirect(true);

PDPage pageField = docEx.getPage(page);
// 创建空签名字段,默认名称为"Signature1"
PDSignatureField signatureField = new PDSignatureField(acroFormField);
PDAnnotationWidget widget = signatureField.getWidgets().get(0);
PDRectangle rectField = new PDRectangle((float)humanRect.getX(), (float)humanRect.getY(), (float)humanRect.getWidth(), (float)humanRect.getHeight());
widget.setRectangle(rectField);
widget.getCOSObject().setNeedToBeUpdated(true);
widget.setPage(pageField);
pageField.getAnnotations().add(widget);
pageField.getCOSObject().setNeedToBeUpdated(true);
acroFormField.getFields().add(signatureField);
setLock(signatureField, acroFormField);
docEx.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
docEx.saveIncremental(fos);


fos.close();
// 关闭文档,替换为已创建空签名字段的文件
PDDocument doc = PDDocument.load(signedFile);

FileOutputStream fosSeal = new FileOutputStream(signedFile);

PDSignature signatureLock = new PDSignature();
PDSignatureField signatureFieldLoad = (PDSignatureField) doc.getDocumentCatalog().getAcroForm().getField("Signature1");
LogSystem.info("signatureFieldLoad " + signatureFieldLoad.getValueAsString());
LogSystem.info("signaturelock " + signatureLock);
signatureFieldLoad.setValue(signatureLock);

COSBase lock = signatureFieldLoad.getCOSObject().getDictionaryObject(COS_NAME_LOCK);
if (lock instanceof COSDictionary)
{
    COSDictionary lockDict = new COSDictionary();
    lockDict.setItem(COS_NAME_ACTION, COS_NAME_ALL);
    lockDict.setItem(COSName.TYPE, COS_NAME_SIG_FIELD_LOCK);

    COSDictionary transformParams = new COSDictionary(lockDict);
    transformParams.setItem(COSName.TYPE, COSName.getPDFName("TransformParams"));
    transformParams.setItem(COSName.V, COSName.getPDFName("1.2"));
    transformParams.setInt(COSName.P, 1);

    transformParams.setDirect(true);
    transformParams.setNeedToBeUpdated(true);

    COSDictionary sigRef = new COSDictionary();
    sigRef.setItem(COSName.TYPE, COSName.getPDFName("SigRef"));
    sigRef.setItem(COSName.getPDFName("TransformParams"), transformParams);
    sigRef.setItem(COSName.getPDFName("TransformMethod"), COSName.getPDFName("FieldMDP"));
    sigRef.setItem(COSName.getPDFName("Data"), doc.getDocumentCatalog());
    sigRef.setDirect(true);
    COSArray referenceArray = new COSArray();
    referenceArray.add(sigRef);
    signatureLock.getCOSObject().setItem(COSName.getPDFName("Reference"), referenceArray);
    LogSystem.info("LOCK DICTIONARY");

    final Predicate<PDField> shallBeLocked;
    final COSArray fieldsLock = lockDict.getCOSArray(COSName.FIELDS);
    final List<String> fieldNames = fieldsLock == null ? Collections.emptyList() :
            fieldsLock.toList().stream().filter(c -> (c instanceof COSString)).map(s -> ((COSString)s).getString()).collect(Collectors.toList());
    final COSName action = lockDict.getCOSName(COSName.getPDFName("Action"));
    if (action.equals(COSName.getPDFName("Include"))) {
        shallBeLocked = f -> fieldNames.contains(f.getFullyQualifiedName());
    } else if (action.equals(COSName.getPDFName("Exclude"))) {
        shallBeLocked = f -> !fieldNames.contains(f.getFullyQualifiedName());
    } else if (action.equals(COSName.getPDFName("All"))) {
        shallBeLocked = f -> true;
    } else { // 未知操作,不锁定任何字段
        shallBeLocked = f -> false;
    }
    lockFields(doc.getDocumentCatalog().getAcroForm().getFields(), shallBeLocked);
    setMDPPermission(doc, signatureLock, 2);

    // 默认过滤器
    signatureLock.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);

    // 基础签名和PAdES第2部分签名的子过滤器
    signatureLock.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

    signatureLock.setName(name);

    // 签名日期,有效签名必填项
    signatureLock.setSignDate(getDate());

    // 若使用外部签名,不设置SignatureInterface实例
    SignatureInterface signatureInterface = isExternalSigning() ? null : this;


    doc.addSignature(signatureLock, signatureInterface, signatureOptions);

    doc.getDocumentCatalog().getAcroForm().getField("Signature1").setPartialName(SignatureField);

    if (isExternalSigning()) {
        this.tsaUrl=tsaUrl;

        ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fosSeal);

        // 调用外部签名服务
        byte[] cmsSignature =IOUtils.toByteArray(externalSigning.getContent());
        String sgn=null;
        try {
            sgn = signingProcess(cmsSignature);
        }catch(Exception e)
        {
            LogSystem.error(e.toString());
            e.printStackTrace();
            doc.close();
            Telegram tele = new Telegram();
            tele.Send(e.toString());
            return false;
        }

        // 写入服务返回的签名字节
        if (sgn != null)
        {
            externalSigning.setSignature(attachSignature(sgn));
        }
    }

咨询问题

  • 该问题应如何排查修正?
  • 如何正确配置才能实现禁止文档修改、签名锁定后新增签名则原签名失效的效果?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:39:42