Skip to content

OFD 签名与签章

本节介绍 OFD 文档的电子印章(OES)、数字证书签名、自定义签名及验签、脱敏与证书导出。整体链路为:打开文档 → 签署(输出新 OFD 文件)→ 重新打开并遍历签名 → 验证 / 处理。打开文件包请参阅 OFD 文件包

  1. 签署输出SealDocCertificateSignDocCustomSignatureDocSignWith* 方法通常直接写入 dest_file(或 WriterCallback),而非修改内存后调用 OFDPackage::Save
  2. 验签方式:OFD 模块 PDF 风格的 Progressive 渐进验签;验证为同步调用 Verify()VerifyEx() 等接口。

任务场景

  • 加载 OES 印章库,对文档单页、多页或骑缝位置加盖电子印章。
  • 使用 PFX 等证书进行不可见数字签名(SignWithNone)或带图片外观的可见签名(SignWithImage)。
  • 实现 SignatureCallback,以自定义摘要/签名算法完成签署与验签。
  • 遍历文档内签名/签章,验证完整性;可选在线验证(online = true)。
  • 对已签署签名进行脱敏(马赛克、雾化、红章转黑等)或导出公钥证书。

API 概览

功能C++ API(foxit::ofd核心参数 / 描述
枚举与读取签名OFDDoc::GetSignatureCountGetSignature按索引取得 ofdseal::Signature
印章库ofdseal::Oes传入 OES 库路径;Login(password)GetSealCount / GetSeal
印章实体ofdseal::Seal印章 ID、名称、GetImage() 等元数据
OES 签署ofdseal::SealDoc绑定 OFDDocSignWithPage / SignWithMultiPage
SignWithStraddle / SignWithMultiPosition
证书签署ofdseal::CertificateSignDoc(doc, cert_file, cert_password)SignWithNone / SignWithImage / SignWithStraddle
自定义签署ofdseal::CustomSignatureDoc
ofdseal::SignatureCallback
委托 Digest / Sign / VerifySignWithMultiPage / SignWithStraddle
布局ofdseal::ImagePosition
ofdseal::StraddlePosition
页索引、坐标;骑缝方向、比例、起始页、page_count
签署属性ofdseal::PublicProperties
ofdseal::SignatureProperties
锁定类型、保护注释/附件、提供者信息等
签名对象ofdseal::SignatureGetTypeGetVerifyMessageOperate
电子签章ofdseal::SealSignatureVerify / VerifyWithOes / VerifyWithoutOes / VerifyEx
ExportPublicCertificateFile
数字签名ofdseal::SignSignatureVerify()GetImage()ExportPublicCertificateFile
自定义验签ofdseal::CustomVerify::VerifySignature + SignatureCallback
脱敏处理ofdseal::OperateSignatureInfoe_RemoveInfoOnlye_ConvertToBlackImage
e_AtomizeImagee_MosaicImagee_RemoveAll
验签上下文ofdseal::VerifyExtendInfoapplication_nameuser_loginfile_name(UTF-8)

签名类型(Signature::SignatureType

枚举值含义
e_SignatureTypeDefault (0)默认
e_SignatureTypeSeal (1)电子签章
e_SignatureTypeSign (2)证书数字签名
e_SignatureTypeTimeStamp (3)时间戳签名

遍历文档中的签名

通过 OFDDoc::GetSignatureCount 取得数量,再按索引调用 GetSignature。根据 GetType() 区分签章与证书签名,并使用 SealSignature / SignSignature 强类型接口(与 Signature 共享同一句柄)。

c++
#include "ofd/fs_ofdpackage.h"
#include "ofd/fs_ofddoc.h"
#include "ofd/signature/fs_ofdsignature.h"

using namespace foxit;
using namespace foxit::ofd;

void TraverseSignatures(OFDDoc& doc) {
  if (doc.IsEmpty()) {
    return;
  }

  int count = doc.GetSignatureCount();
  for (int i = 0; i < count; ++i) {
    ofdseal::Signature signature = doc.GetSignature(i);
    if (signature.IsEmpty()) {
      continue;
    }

    ofdseal::Signature::SignatureType type = signature.GetType();
    WString provider = signature.GetProviderName();
    WString digest_method = signature.GetDigestMethod();
    (void)provider;
    (void)digest_method;

    if (type == ofdseal::Signature::e_SignatureTypeSign) {
      // 证书数字签名:转为 SignSignature 调用 Verify 等接口
      ofdseal::SignSignature sign_sig(signature.Handle());
      (void)sign_sig;
    } else if (type == ofdseal::Signature::e_SignatureTypeSeal) {
      // 电子签章:转为 SealSignature
      ofdseal::SealSignature seal_sig(signature.Handle());
      (void)seal_sig;
    }
  }
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.Signature;

public class OFDTraverseSignatureExample {
    public static void traverseSignatures(OFDDoc doc) throws Exception {
        if (doc.isEmpty()) {
            return;
        }

        int count = doc.getSignatureCount();
        for (int i = 0; i < count; i++) {
            Signature signature = doc.getSignature(i);
            if (signature.isEmpty()) {
                continue;
            }

            // 根据 getType() 区分签章与证书签名,再使用对应子类型接口
            int type = signature.getType();
            String provider = signature.getProviderName();
            String digestMethod = signature.getDigestMethod();
        }
    }
}

基于 OES 印章库签署

使用 Oes 加载印章库文件;若库受密码保护,须先 Login 成功再 GetSeal。通过 SealDoc 绑定已打开的 OFDDoc,调用 SignWithPage 等在指定位置盖章并输出到新文件。

c++
#include "ofd/fs_ofdpackage.h"
#include "ofd/fs_ofddoc.h"
#include "ofd/signature/fs_ofdoesseal.h"
#include "ofd/signature/fs_ofdsign.h"

using namespace foxit;
using namespace foxit::ofd;

bool SignWithOesSeal(OFDDoc& doc, const char* oes_lib_path,
                     const char* oes_password, const char* output_path) {
  if (doc.IsEmpty()) {
    return false;
  }

  // 加载 OES 印章库(路径替换为实际部署的印章库文件)
  ofdseal::Oes oes(oes_lib_path);
  if (oes.IsEmpty()) {
    return false;
  }

  // 受保护印章库须先登录
  if (!oes.Login(oes_password)) {
    return false;
  }

  if (oes.GetSealCount() <= 0) {
    return false;
  }

  ofdseal::Seal seal = oes.GetSeal(0);
  if (seal.IsEmpty()) {
    return false;
  }

  // SealDoc 构造时绑定 OFDDoc,建议局部作用域使用
  ofdseal::SealDoc seal_doc(doc);
  if (seal_doc.IsEmpty()) {
    return false;
  }

  // 在第 0 页 (50, 50) 处盖章,宽 100、高 50
  ofdseal::ImagePosition position(0, 50.0f, 50.0f, true, 100.0f, 50.0f);
  ofdseal::PublicProperties properties;

  return seal_doc.SignWithPage(seal, position, output_path, properties);
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.ImagePosition;
import com.foxit.sdk.ofd.signature.Oes;
import com.foxit.sdk.ofd.signature.PublicProperties;
import com.foxit.sdk.ofd.signature.Seal;
import com.foxit.sdk.ofd.signature.SealDoc;

public class OFDOesSignExample {
    public static boolean signWithOesSeal(OFDDoc doc, String oesLibPath,
            String oesPassword, String outputPath) throws Exception {
        if (doc.isEmpty()) {
            return false;
        }

        // 加载 OES 印章库(路径替换为实际部署的印章库文件)
        Oes oes = new Oes(oesLibPath);
        if (oes.isEmpty()) {
            return false;
        }

        // 受保护印章库须先登录
        if (!oes.login(oesPassword)) {
            return false;
        }

        if (oes.getSealCount() <= 0) {
            return false;
        }

        Seal seal = oes.getSeal(0);
        if (seal.isEmpty()) {
            return false;
        }

        SealDoc sealDoc = new SealDoc(doc);
        if (sealDoc.isEmpty()) {
            return false;
        }

        // 在第 0 页 (50, 50) 处盖章
        ImagePosition position = new ImagePosition(0, 50.0f, 50.0f, true, 100.0f, 50.0f);
        PublicProperties properties = new PublicProperties();

        return sealDoc.signWithPage(seal, position, outputPath, properties);
    }
}

骑缝章与多页签署

OES 骑缝章使用 SealDoc::SignWithStraddle(seal, straddle_position, dest_file)证书骑缝章使用 CertificateSignDoc::SignWithStraddle(image_path, straddle_position, …)StraddlePosition 指定骑缝方向、高度比例、起始页与跨页数量,须保证文档页数足够。

c++
#include "ofd/signature/fs_ofdsign.h"

using namespace foxit;
using namespace foxit::ofd;

bool SignStraddleWithCertificate(OFDDoc& doc, const char* cert_file,
                                 const char* cert_password,
                                 const char* image_path,
                                 const char* output_path) {
  if (doc.IsEmpty()) {
    return false;
  }

  ofdseal::CertificateSignDoc signer(doc, cert_file, cert_password);
  if (signer.IsEmpty()) {
    return false;
  }

  // 右侧骑缝,3/5 高度,从第 0 页起跨 2 页
  ofdseal::StraddlePosition straddle(
      ofdseal::StraddlePosition::e_StraddlePositionTypeRight,
      ofdseal::StraddlePosition::e_StraddleProportionThreeFifth,
      35.0f, 0, 2);

  ofdseal::SignatureProperties sig_props("company", "provider", "1.0");
  ofdseal::PublicProperties pub_props(
      true,
      ofdseal::PublicProperties::e_SealSignLockTypSignatureXmlAndOFDXml,
      true, false, "sig-dir");

  return signer.SignWithStraddle(image_path, straddle, output_path,
                                 sig_props, pub_props);
}

bool SignStraddleWithOes(OFDDoc& doc, ofdseal::Seal& seal,
                         const char* output_path) {
  if (doc.IsEmpty() || seal.IsEmpty()) {
    return false;
  }

  ofdseal::SealDoc seal_doc(doc);
  ofdseal::StraddlePosition straddle(
      ofdseal::StraddlePosition::e_StraddlePositionTypeRight,
      ofdseal::StraddlePosition::e_StraddleProportionThreeFifth,
      35.0f, 0, 2);

  return seal_doc.SignWithStraddle(seal, straddle, output_path);
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.CertificateSignDoc;
import com.foxit.sdk.ofd.signature.PublicProperties;
import com.foxit.sdk.ofd.signature.Seal;
import com.foxit.sdk.ofd.signature.SealDoc;
import com.foxit.sdk.ofd.signature.SignatureProperties;
import com.foxit.sdk.ofd.signature.StraddlePosition;

public class OFDStraddleSignExample {
    public static boolean signStraddleWithCertificate(OFDDoc doc, String certFile,
            String certPassword, String imagePath, String outputPath) throws Exception {
        if (doc.isEmpty()) {
            return false;
        }

        CertificateSignDoc signer = new CertificateSignDoc(doc, certFile, certPassword);
        if (signer.isEmpty()) {
            return false;
        }

        // 右侧骑缝,3/5 高度,从第 0 页起跨 2 页
        StraddlePosition straddle = new StraddlePosition(
                StraddlePosition.e_StraddlePositionTypeRight,
                StraddlePosition.e_StraddleProportionThreeFifth,
                35.0f, 0, 2);

        SignatureProperties sigProps = new SignatureProperties("company", "provider", "1.0");
        PublicProperties pubProps = new PublicProperties();

        return signer.signWithStraddle(imagePath, straddle, outputPath, sigProps, pubProps);
    }

    public static boolean signStraddleWithOes(OFDDoc doc, Seal seal,
            String outputPath) throws Exception {
        if (doc.isEmpty() || seal.isEmpty()) {
            return false;
        }

        SealDoc sealDoc = new SealDoc(doc);
        StraddlePosition straddle = new StraddlePosition(
                StraddlePosition.e_StraddlePositionTypeRight,
                StraddlePosition.e_StraddleProportionThreeFifth,
                35.0f, 0, 2);

        return sealDoc.signWithStraddle(seal, straddle, outputPath);
    }
}

多页相同位置盖章可使用 SignWithMultiPage(页面索引数组或 "0-10,35,50-" 形式的 page_scope 字符串);多位置盖章使用 SignWithMultiPosition 配合 ImagePositionArray

基于数字证书签署

CertificateSignDoc 构造时传入 OFDDoc、证书文件路径(支持 String / WString)及证书密码。SignWithNone 为不可见签名;SignWithImage 在指定 ImagePosition 附加图片外观。

c++
#include "ofd/fs_ofdpackage.h"
#include "ofd/fs_ofddoc.h"
#include "ofd/signature/fs_ofdsign.h"
#include "ofd/signature/fs_ofdsignature.h"

using namespace foxit;
using namespace foxit::ofd;

bool SignWithCertificate(OFDDoc& doc, const wchar_t* cert_file,
                         const char* cert_password,
                         const char* image_path, const char* output_path,
                         bool with_image) {
  if (doc.IsEmpty()) {
    return false;
  }

  ofdseal::CertificateSignDoc signer(doc, WString(cert_file), cert_password);
  if (signer.IsEmpty()) {
    return false;
  }

  ofdseal::SignatureProperties sig_props("sig-company", "sig-provider", "1.0");
  ofdseal::PublicProperties pub_props(
      true, ofdseal::PublicProperties::e_SealSignLockTypeNone,
      false, false, "sig-dir");

  if (with_image) {
    // 可见签名:指定页、坐标与图片尺寸
    ofdseal::ImagePosition position(0, 50.0f, 50.0f, true, 100.0f, 50.0f);
    return signer.SignWithImage(image_path, position, output_path,
                                sig_props, pub_props);
  }

  // 不可见数字签名
  return signer.SignWithNone(output_path, sig_props, pub_props);
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.CertificateSignDoc;
import com.foxit.sdk.ofd.signature.ImagePosition;
import com.foxit.sdk.ofd.signature.PublicProperties;
import com.foxit.sdk.ofd.signature.SignatureProperties;

public class OFDCertificateSignExample {
    public static boolean signWithCertificate(OFDDoc doc, String certFile,
            String certPassword, String imagePath, String outputPath,
            boolean withImage) throws Exception {
        if (doc.isEmpty()) {
            return false;
        }

        CertificateSignDoc signer = new CertificateSignDoc(doc, certFile, certPassword);
        if (signer.isEmpty()) {
            return false;
        }

        SignatureProperties sigProps = new SignatureProperties(
                "sig-company", "sig-provider", "1.0");
        PublicProperties pubProps = new PublicProperties();

        if (withImage) {
            // 可见签名:指定页、坐标与图片尺寸
            ImagePosition position = new ImagePosition(
                    0, 50.0f, 50.0f, true, 100.0f, 50.0f);
            return signer.signWithImage(imagePath, position, outputPath,
                    sigProps, pubProps);
        }

        // 不可见数字签名
        return signer.signWithNone(outputPath, sigProps, pubProps);
    }
}

自定义签名与验签

实现 SignatureCallbackDigestSignVerifyGetSignMethod 等),通过 CustomSignatureDoc 完成签署;验签使用 CustomVerify::Verify

c++
#include "ofd/signature/fs_ofdcustomsignature.h"

using namespace foxit;
using namespace foxit::ofd;

class MySignatureCallback : public ofdseal::SignatureCallback {
 public:
  String GetProviderName() override { return "my-provider"; }
  String GetCompany() override { return "my-company"; }
  String GetVersion() override { return "1.0"; }
  String GetSignatureTime() override { return "2026-06-01T10:00:00"; }

  String Digest(const void* data, int length) override {
    // 实现摘要算法
    return "";
  }

  String GetDigestMethod() override { return "SHA256"; }

  String Sign(const void* data, int length) override {
    // 实现签名
    return "";
  }

  String GetSignMethod() override { return "RSA"; }

  String GetSeal() override { return ""; }

  bool Verify(const void* data, int length, const void* sign_value,
              int sign_value_length) override {
    // 实现验签
    return false;
  }
};

bool CustomSignDocument(OFDDoc& doc, MySignatureCallback* callback,
                        const char* output_path) {
  if (doc.IsEmpty() || callback == nullptr) {
    return false;
  }

  // 回调须在签署全流程内保持有效
  ofdseal::CustomSignatureDoc custom_doc(doc, callback);
  if (custom_doc.IsEmpty()) {
    return false;
  }

  ofdseal::ImagePosition position(0, 50.0f, 50.0f, true, 80.0f, 40.0f);
  ofdseal::PublicProperties properties;

  // page_scope "0-" 表示全部页面
  return custom_doc.SignWithMultiPage("0-", position, output_path, properties);
}

bool CustomVerifySignature(ofdseal::Signature& signature,
                           MySignatureCallback* callback) {
  if (signature.IsEmpty() || callback == nullptr) {
    return false;
  }

  return ofdseal::CustomVerify::Verify(signature, callback);
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.CustomSignatureDoc;
import com.foxit.sdk.ofd.signature.CustomVerify;
import com.foxit.sdk.ofd.signature.ImagePosition;
import com.foxit.sdk.ofd.signature.PublicProperties;
import com.foxit.sdk.ofd.signature.Signature;
import com.foxit.sdk.ofd.signature.SignatureCallback;

public class OFDCustomSignExample {
    public static boolean customSignDocument(OFDDoc doc,
            SignatureCallback callback, String outputPath) throws Exception {
        if (doc.isEmpty() || callback == null) {
            return false;
        }

        // 回调须在签署全流程内保持有效
        CustomSignatureDoc customDoc = new CustomSignatureDoc(doc, callback);
        if (customDoc.isEmpty()) {
            return false;
        }

        ImagePosition position = new ImagePosition(0, 50.0f, 50.0f, true, 80.0f, 40.0f);
        PublicProperties properties = new PublicProperties();

        // pageScope "0-" 表示全部页面
        return customDoc.signWithMultiPage("0-", position, outputPath, properties);
    }

    public static boolean customVerifySignature(Signature signature,
            SignatureCallback callback) throws Exception {
        if (signature.isEmpty() || callback == null) {
            return false;
        }

        return CustomVerify.verify(signature, callback);
    }
}

验证签名

SignSignature::Verify() 用于证书签名;SealSignature 提供 Verify(online)VerifyWithOesVerifyWithoutOesVerifyEx(extend_info, online)。验证后可通过 GetVerifyMessage() 读取详细信息。

c++
#include "ofd/signature/fs_ofdsignature.h"

using namespace foxit;
using namespace foxit::ofd;

bool VerifyDocumentSignatures(OFDDoc& doc) {
  if (doc.IsEmpty()) {
    return false;
  }

  bool all_ok = true;
  for (int i = 0; i < doc.GetSignatureCount(); ++i) {
    ofdseal::Signature signature = doc.GetSignature(i);
    if (signature.IsEmpty()) {
      continue;
    }

    ofdseal::Signature::SignatureType type = signature.GetType();
    bool verified = false;

    if (type == ofdseal::Signature::e_SignatureTypeSign) {
      ofdseal::SignSignature sign_sig(signature.Handle());
      // 证书签名离线验证
      verified = sign_sig.Verify();
    } else if (type == ofdseal::Signature::e_SignatureTypeSeal) {
      ofdseal::SealSignature seal_sig(signature.Handle());
      // 电子签章:online=false 为离线验证
      verified = seal_sig.Verify(false);

      // 可选:传入业务上下文参与验章
      ofdseal::VerifyExtendInfo extend_info("MyApp", "user01", "document.ofd");
      verified = seal_sig.VerifyEx(extend_info, false);
    }

    WString message = signature.GetVerifyMessage();
    (void)message;

    if (!verified) {
      all_ok = false;
    }
  }
  return all_ok;
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.SealSignature;
import com.foxit.sdk.ofd.signature.SignSignature;
import com.foxit.sdk.ofd.signature.Signature;
import com.foxit.sdk.ofd.signature.VerifyExtendInfo;

public class OFDVerifySignatureExample {
    public static boolean verifyDocumentSignatures(OFDDoc doc) throws Exception {
        if (doc.isEmpty()) {
            return false;
        }

        boolean allOk = true;
        for (int i = 0; i < doc.getSignatureCount(); i++) {
            Signature signature = doc.getSignature(i);
            if (signature.isEmpty()) {
                continue;
            }

            int type = signature.getType();
            boolean verified = false;

            if (signature instanceof SignSignature) {
                // 证书签名验证
                verified = ((SignSignature) signature).verify();
            } else if (signature instanceof SealSignature) {
                SealSignature sealSig = (SealSignature) signature;
                // 电子签章:online=false 为离线验证
                verified = sealSig.verify(false);

                // 可选:传入业务上下文参与验章
                VerifyExtendInfo extendInfo = new VerifyExtendInfo(
                        "MyApp", "user01", "document.ofd");
                verified = sealSig.verifyEx(extendInfo, false);
            }

            String message = signature.getVerifyMessage();
            if (!verified) {
                allOk = false;
            }
        }
        return allOk;
    }
}

签名脱敏与处理

对已签署签名调用 Signature::Operate,传入 OperateSignatureInfo 指定脱敏模式。部分模式会删除签名信息;操作后须 OFDPackage::SaveAs 写回(若在原包上修改)。

c++
#include "ofd/signature/fs_ofdsignature.h"

using namespace foxit;
using namespace foxit::ofd;

bool DesensitizeSignature(OFDDoc& doc, int signature_index) {
  if (doc.IsEmpty() || signature_index < 0 ||
      signature_index >= doc.GetSignatureCount()) {
    return false;
  }

  ofdseal::Signature signature = doc.GetSignature(signature_index);
  if (signature.IsEmpty()) {
    return false;
  }

  // 马赛克脱敏:mosaic_vector_length 控制马赛克粒度
  ofdseal::OperateSignatureInfo info(
      ofdseal::OperateSignatureInfo::e_MosaicImage, 5, 5, 9);

  return signature.Operate(info);
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.OperateSignatureInfo;
import com.foxit.sdk.ofd.signature.Signature;

public class OFDOperateSignatureExample {
    public static boolean desensitizeSignature(OFDDoc doc,
            int signatureIndex) throws Exception {
        if (doc.isEmpty() || signatureIndex < 0
                || signatureIndex >= doc.getSignatureCount()) {
            return false;
        }

        Signature signature = doc.getSignature(signatureIndex);
        if (signature.isEmpty()) {
            return false;
        }

        // 马赛克脱敏
        OperateSignatureInfo info = new OperateSignatureInfo(
                OperateSignatureInfo.e_MosaicImage, 5, 5, 9);

        return signature.operate(info);
    }
}

OperateSignatureInfo 操作类型

枚举值含义
e_RemoveInfoOnly仅删除签名/签章信息,保留图片
e_ConvertToBlackImage删除信息并将彩色图转灰度(红章转黑)
e_AtomizeImage删除信息并雾化图片
e_MosaicImage删除信息并马赛克图片
e_RemoveAll完全删除签名/签章及图片

导出公钥证书

SignSignatureSealSignature 均支持 ExportPublicCertificateFile,将签名中的公钥证书导出为文件。

c++
#include "ofd/signature/fs_ofdsignature.h"

using namespace foxit;
using namespace foxit::ofd;

bool ExportSignatureCertificate(OFDDoc& doc, int index,
                                const char* cert_output_path) {
  if (doc.IsEmpty() || index < 0 || index >= doc.GetSignatureCount()) {
    return false;
  }

  ofdseal::Signature signature = doc.GetSignature(index);
  if (signature.GetType() == ofdseal::Signature::e_SignatureTypeSign) {
    ofdseal::SignSignature sign_sig(signature.Handle());
    return sign_sig.ExportPublicCertificateFile(cert_output_path);
  }
  if (signature.GetType() == ofdseal::Signature::e_SignatureTypeSeal) {
    ofdseal::SealSignature seal_sig(signature.Handle());
    return seal_sig.ExportPublicCertificateFile(cert_output_path);
  }
  return false;
}
java
import com.foxit.sdk.ofd.OFDDoc;
import com.foxit.sdk.ofd.signature.SealSignature;
import com.foxit.sdk.ofd.signature.SignSignature;
import com.foxit.sdk.ofd.signature.Signature;

public class OFDExportCertExample {
    public static boolean exportSignatureCertificate(OFDDoc doc, int index,
            String certOutputPath) throws Exception {
        if (doc.isEmpty() || index < 0 || index >= doc.getSignatureCount()) {
            return false;
        }

        Signature signature = doc.getSignature(index);
        if (signature instanceof SignSignature) {
            return ((SignSignature) signature).exportPublicCertificateFile(certOutputPath);
        }
        if (signature instanceof SealSignature) {
            return ((SealSignature) signature).exportPublicCertificateFile(certOutputPath);
        }
        return false;
    }
}

注意事项

  • 文档入口:读取签名统一通过 OFDDoc::GetSignatureCount / GetSignature PDF 模块的 Progressive 渐进验签。
  • 签署与保存SignWith* 直接输出到目标路径;在原文件上继续编辑时,重新打开输出文件或调用 OFDPackage::SaveAs
  • OES 登录:受保护印章库须 Oes::Login 成功后再 GetSeal;失败时可读 GetLastErrorMessage()
  • 骑缝章页数StraddlePosition::page_countstart_page_index 之和不得超过文档页数。
  • 对象生命周期SealDoc / CertificateSignDoc / CustomSignatureDoc 构造时绑定 OFDDocSignatureCallback* 须在签署/验签期间保持有效。
  • 验证含义Verify() 返回 true 表示完整性/数学验证通过;证书吊销、过期等须结合 online 参数与 GetVerifyMessage() 解读。
  • 许可证与模块:OES、证书签名、国密算法依赖相应产品模块与许可证;文档加密或禁止签章权限可能影响签署,请参阅 安全与权限
  • 文档级权限:文档级 OFDPermission安全与权限