数字签名概览
本节介绍在 PDF 上进行数字签名与验证的一般步骤、相关接口、如何通过内置流程体验签名功能,以及我们提供的仅供测试的签名 HTTP 服务地址。
福昕 PDF SDK (Web) 11.0.0 起
PDFUI.registerSignHandler与PDFUI.setVerifyHandler已在 11.0 中弃用。- 请改用
SignatureWorkflowService(内置签名 UI 注册签名者与外观信息)以及SignatureService.setVerifyHandler(全局验签回调)。若需覆盖 UI 签名流程或深度定制,请参阅 签名流程 API 迁移指南、签名流程、签名服务 与 自定义签名 UI。
数字签名与验证步骤
对 PDF 进行签名与验证时,可按下列流程理解:
- 签署文档
- 生成包含签名
byteRange的文件流(详见 PDF 规范)。 - 对
byteRange覆盖范围计算摘要,并通过 PKI 或签名服务得到signedData。- 程序化签名时,在调用
PDFDoc.sign(signInfo, digestSignHandler)时实现摘要/签名回调; - 使用内置签名 UI(11.0+)时,通过
SignatureWorkflowService的addSignerInfo、addSignatureAPInfo注册签名逻辑与外观。
- 程序化签名时,在调用
- 将
signedData写入流中byteRange指定的位置。SDK 负责大部分 PDF 结构处理;开发者提供签名实现及可选的外观配置。
- 验证签名
- 取得原始文件字节、签名的
byteRange、signedData及签名者等元数据。 - 在由您主动发起验签的场景,使用
PDFDoc.verifySignature(signatureField, verifyHandler),并传入显式的verifyHandler。 - 对内置 UI或 JavaScript 动作触发的验签,应通过
pdfViewer.getSignatureService().setVerifyHandler(...)配置全局处理器(详见 签名服务)。
提示:
PDFDoc.verifySignature不会自动使用该全局处理器,除非您在回调中自行衔接。
相关数字签名接口
程序化签名与验证:PDFDoc.sign 与 PDFDoc.verifySignature
在不依赖 PDFUI 辅助方法的前提下,这是从业务代码中完成签名与验证的主要方式。
PDFDoc.sign(signInfo, digestSignHandler)
对文档进行签名。需提供摘要/签名处理函数,返回签名数据(通常由后端完成)。
/**
* @returns {Blob} - 已签名文档的文件流。
*/
const signResult = await pdfdoc.sign(signInfo, (signInfo, plainContent) => {
return Promise.resolve(getSignData(signInfo, plainContent)); // plainContent 为 Blob
});
PDFDoc.verifySignature(signatureField, verifyHandler)
验证签名。回调会收到签名字段、明文内容、签名数据,以及是否存在超出签名范围的内容等参数。
/**
* @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) 支持两种签名 subfilter:adbe.pkcs7.detached 与 adbe.pkcs7.sha1。
adbe.pkcs7.detached下,摘要算法常用'sha1'、'sha256'、'sha384'(具体以后端能力为准)。adbe.pkcs7.sha1下,摘要算法为'sha1'。
示例:adbe.pkcs7.detached + SHA-256
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
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,
});
提示:
- 当前列表中,每次
addSignerInfo的 signer 字符串与每次addSignatureAPInfo的 title 必须唯一。- 关于
overrideSigningWorkflow、overrideVerifyWorkflow及签名人显示策略,见 签名流程。
全局验签:SignatureService.setVerifyHandler(11.0 +)
针对走查看器默认验签路径的场景(例如用户点击验签或部分脚本触发的验签),通过 PDFViewer 取得服务并注册回调。
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);
}
);
提示:
- 返回值应为数值型签名状态,参见
Signature_State。- 与
PDFSignature.verify的协作及适用范围详见 签名服务。
PDFSignature 类
PDFSignature.isSigned()— 当前签名字段是否已签署。PDFSignature.getByteRange()— 签名覆盖的文件字节范围。PDFSignature.getFilter()/getSubfilter()— 签名过滤器与子过滤器。
视版本与调用上下文,部分 getter 可能为异步;建议在异步回调中使用 await 获取。
已弃用:PDFUI.registerSignHandler / PDFUI.setVerifyHandler
自 11.0.0 起,上述方法仅为兼容保留,内部分别委托至 SignatureWorkflowService 与 SignatureService。新项目请勿再使用。
// 已弃用 — 等价于 SignatureWorkflowService + addSignerInfo / addSignatureAPInfo
pdfui.registerSignHandler({ /* ... */ });
// 已弃用 — 等价于 pdfViewer.getSignatureService().setVerifyHandler(...)
pdfui.setVerifyHandler(/* ... */);
体验数字签名功能
可通过 API 或 内置 UI 体验签名流程。安装包内提供基于 Node.js 的示例后端,路径为 ./server/signature-server-for-win。
方法一:在当前文档上以代码放置签名
- 打开
https://webviewer-demo.foxitsoftware.com/并启动签名服务。 - 在浏览器控制台执行下列示例,将自动创建签名字段并完成签署。
- 打开签署结果文档,点击签名字段进行验证(需已按方法二或全局验签方式配置验签逻辑)。
// 假定本地签名服务监听 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 与自有后端对接,请使用 SignatureWorkflowService 与 SignatureService 注册。下列为典型写法(requestData 与 index.html 中类似;将 origin 替换为实际服务基地址):
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 测试服务
若无自有后端,可使用下列仅供测试的地址: