Skip to content

数字签名概览

本节介绍在 PDF 上进行数字签名与验证的一般步骤、相关接口、如何通过内置流程体验签名功能,以及我们提供的仅供测试的签名 HTTP 服务地址。

福昕 PDF SDK (Web) 11.0.0 起

  • PDFUI.registerSignHandlerPDFUI.setVerifyHandler 已在 11.0 中弃用
  • 请改用 SignatureWorkflowService(内置签名 UI 注册签名者与外观信息)以及 SignatureService.setVerifyHandler(全局验签回调)。若需覆盖 UI 签名流程或深度定制,请参阅 签名流程 API 迁移指南签名流程签名服务自定义签名 UI

数字签名与验证步骤

对 PDF 进行签名与验证时,可按下列流程理解:

  • 签署文档
  1. 生成包含签名 byteRange 的文件流(详见 PDF 规范)。
  2. byteRange 覆盖范围计算摘要,并通过 PKI 或签名服务得到 signedData
    • 程序化签名时,在调用 PDFDoc.sign(signInfo, digestSignHandler) 时实现摘要/签名回调;
    • 使用内置签名 UI(11.0+)时,通过 SignatureWorkflowServiceaddSignerInfoaddSignatureAPInfo 注册签名逻辑与外观。
  3. signedData 写入流中 byteRange 指定的位置。SDK 负责大部分 PDF 结构处理;开发者提供签名实现及可选的外观配置。
  • 验证签名
  1. 取得原始文件字节、签名的 byteRangesignedData 及签名者等元数据。
  2. 在由您主动发起验签的场景,使用 PDFDoc.verifySignature(signatureField, verifyHandler),并传入显式的 verifyHandler
  3. 内置 UIJavaScript 动作触发的验签,应通过 pdfViewer.getSignatureService().setVerifyHandler(...) 配置全局处理器(详见 签名服务)。

提示: PDFDoc.verifySignature 不会自动使用该全局处理器,除非您在回调中自行衔接。

相关数字签名接口

程序化签名与验证:PDFDoc.signPDFDoc.verifySignature

在不依赖 PDFUI 辅助方法的前提下,这是从业务代码中完成签名与验证的主要方式。

PDFDoc.sign(signInfo, digestSignHandler)

对文档进行签名。需提供摘要/签名处理函数,返回签名数据(通常由后端完成)。

js
/**
 * @returns {Blob} - 已签名文档的文件流。
 */
const signResult = await pdfdoc.sign(signInfo, (signInfo, plainContent) => {
  return Promise.resolve(getSignData(signInfo, plainContent)); // plainContent 为 Blob
});

PDFDoc.verifySignature(signatureField, verifyHandler)

验证签名。回调会收到签名字段、明文内容、签名数据,以及是否存在超出签名范围的内容等参数。

js
/**
 * @returns {number} - 签名状态。
 */
const result = await signedPDF.verifySignature(
  pdfform.getField("Signature_0"),
  async (signatureField, plainBuffer, signedData, hasDataOutOfScope) => {
    const signInfo = {
      byteRange: await signatureField.getByteRange(),
      signer: await signatureField.getSigner(),
      filter: await signatureField.getFilter(),
      subfilter: await signatureField.getSubfilter(),
    };
    return verifySignData(signInfo, plainBuffer, signedData, hasDataOutOfScope);
  }
);

内置 UI:SignatureWorkflowService(11.0+)

通过 pdfui.getSignatureWorkflowService() 注册供默认签名对话框使用的签名者(含 sign 回调)与签名外观条目。

常见场景下,福昕 PDF SDK (Web) 支持两种签名 subfilteradbe.pkcs7.detachedadbe.pkcs7.sha1

  • adbe.pkcs7.detached 下,摘要算法常用 'sha1''sha256''sha384'(具体以后端能力为准)。
  • adbe.pkcs7.sha1 下,摘要算法为 'sha1'

示例:adbe.pkcs7.detached + SHA-256

js
const workflow = pdfui.getSignatureWorkflowService();
workflow.addSignerInfo({
  filter: "Adobe.PPKLite",
  subfilter: "adbe.pkcs7.detached",
  signer: "web sdk",
  sign: async (setting, plainContent) => {
    return requestData(
      "post",
      "http://localhost:7777/digest_and_sign",
      "arraybuffer",
      {
        subfilter: setting.subfilter,
        md: "sha256", // "sha1" | "sha256" | "sha384"
        plain: plainContent,
      }
    );
  },
});
workflow.addSignatureAPInfo({
  title: "Standard detached SHA256",
  distinguishName: "e=foxit@foxitsoftware.cn",
  location: "FZ",
  reason: "Test",
  flag: 0x100,
  showTime: true,
});

示例:adbe.pkcs7.sha1

js
const workflow = pdfui.getSignatureWorkflowService();
workflow.addSignerInfo({
  filter: "Adobe.PPKLite",
  subfilter: "adbe.pkcs7.sha1",
  signer: "web sdk",
  sign: async (signInfo, plainContent) => {
    const digest = getDigest(plainContent);
    const signData = sign(digest);
    return signData;
  },
});
workflow.addSignatureAPInfo({
  title: "Standard SHA1",
  distinguishName: "support@foxitsoftware.com",
  location: "FZ",
  reason: "Test",
  flag: 0x100,
  showTime: true,
});

提示:

  • 当前列表中,每次 addSignerInfosigner 字符串与每次 addSignatureAPInfotitle 必须唯一。
  • 关于 overrideSigningWorkflowoverrideVerifyWorkflow 及签名人显示策略,见 签名流程

全局验签:SignatureService.setVerifyHandler(11.0 +)

针对走查看器默认验签路径的场景(例如用户点击验签或部分脚本触发的验签),通过 PDFViewer 取得服务并注册回调。

js
const pdfViewer = await pdfui.getPDFViewer();
const signatureService = pdfViewer.getSignatureService();
signatureService.setVerifyHandler(
  async (signatureField, plainContent, signedData, hasDataOutOfScope) => {
    const filter = await signatureField.getFilter();
    const subfilter = await signatureField.getSubfilter();
    const signer = await signatureField.getSigner();
    const digest = getDigest(plainContent);
    return verify(filter, subfilter, signer, digest, signedData, hasDataOutOfScope);
  }
);

提示:

PDFSignature

  • PDFSignature.isSigned() — 当前签名字段是否已签署。
  • PDFSignature.getByteRange() — 签名覆盖的文件字节范围。
  • PDFSignature.getFilter() / getSubfilter() — 签名过滤器与子过滤器。

视版本与调用上下文,部分 getter 可能为异步;建议在异步回调中使用 await 获取。

已弃用:PDFUI.registerSignHandler / PDFUI.setVerifyHandler

11.0.0 起,上述方法仅为兼容保留,内部分别委托至 SignatureWorkflowServiceSignatureService新项目请勿再使用。

js
// 已弃用 — 等价于 SignatureWorkflowService + addSignerInfo / addSignatureAPInfo
pdfui.registerSignHandler({ /* ... */ });

// 已弃用 — 等价于 pdfViewer.getSignatureService().setVerifyHandler(...)
pdfui.setVerifyHandler(/* ... */);

体验数字签名功能

可通过 API内置 UI 体验签名流程。安装包内提供基于 Node.js 的示例后端,路径为 ./server/signature-server-for-win

方法一:在当前文档上以代码放置签名

  1. 打开 https://webviewer-demo.foxitsoftware.com/ 并启动签名服务。
  2. 在浏览器控制台执行下列示例,将自动创建签名字段并完成签署。
  3. 打开签署结果文档,点击签名字段进行验证(需已按方法二或全局验签方式配置验签逻辑)。
js
// 假定本地签名服务监听 7777 端口。
const pdfviewer = await pdfui.getPDFViewer();
const pdfdoc = await pdfviewer.getCurrentPDFDoc();
const signInfo = {
  filter: "Adobe.PPKLite",
  subfilter: "adbe.pkcs7.sha1",
  rect: { left: 10, bottom: 10, right: 300, top: 300 },
  pageIndex: 0,
  flag: 511,
  signer: "signer",
  reason: "reason",
  email: "email",
  distinguishName: "distinguishName",
  location: "loc",
  text: "text",
};
const signResult = await pdfdoc.sign(signInfo, (signInfo, plainContent) => {
  return requestData(
    "post",
    "http://127.0.0.1:7777/digest_and_sign",
    "arraybuffer",
    { plain: plainContent }
  );
});
const signedPDF = await pdfviewer.openPDFByFile(signResult);
const pdfform = signedPDF.getPDFForm();
const verify = async (signatureField, plainBuffer, signedData, hasDataOutOfScope) => {
  return requestData("post", "http://127.0.0.1:7777/verify", "text", {
    filter: await signatureField.getFilter(),
    subfilter: await signatureField.getSubfilter(),
    signer: await signatureField.getSigner(),
    plainContent: new Blob([plainBuffer]),
    signedData: new Blob([signedData]),
  });
};
await signedPDF.verifySignature(pdfform.getField("Signature_0"), verify);

方法二:通过内置 UI 放置签名

使用 https://webviewer-demo.foxitsoftware.com/

  • 准备:在浏览器中打开在线查看器。
  • 添加并签署:在「表单」选项卡使用签名工具,在页面上拖出矩形区域,切换到手型工具(或按 Esc),在弹出的对话框中完成设置。
  • 验证:使用手型工具点击已签署的签名字段。

若要将内置 UI 与自有后端对接,请使用 SignatureWorkflowServiceSignatureService 注册。下列为典型写法(requestDataindex.html 中类似;将 origin 替换为实际服务基地址):

js
const pdfViewer = await pdfui.getPDFViewer();
const signatureService = pdfViewer.getSignatureService();
signatureService.setVerifyHandler(async (signatureField, plainBuffer, signedData) => {
  return requestData("post", "origin", "text", {
    filter: await signatureField.getFilter(),
    subfilter: await signatureField.getSubfilter(),
    signer: await signatureField.getSigner(),
    plainContent: new Blob([plainBuffer]),
    signedData: new Blob([signedData]),
  });
});

const workflow = pdfui.getSignatureWorkflowService();
workflow.addSignerInfo({
  filter: "Adobe.PPKLite",
  subfilter: "adbe.pkcs7.sha1",
  signer: "web sdk",
  sign: async (setting, plainContent) => {
    return requestData("post", "origin", "arraybuffer", {
      plain: plainContent,
    });
  },
});
workflow.addSignatureAPInfo({
  title: "Demo appearance",
  distinguishName: "e=foxit@foxitsoftware.com",
  location: "FZ",
  reason: "Test",
  flag: 0x100,
  showTime: true,
});

签名相关 HTTP 测试服务

若无自有后端,可使用下列仅供测试的地址: