# 数字签名
在本节中,您将了解签名和验证签名的常规步骤、相关的签名 API、数字签名交互的方式、以及我们提供的测试签名服务路由。
# 在 PDF 上签名和验证数字签名的步骤
在 PDF 上签名和验证数字签名,应执行以下过程:
签名文档
生成包含签名覆盖范围信息(签名域字典的 byteRange)的文档文件流。有关详细信息,请参阅 PDF Reference 1.7 或更高版本。
对签名覆盖范围的文件流计算相应的摘要。这可以通过调用
PDFUI.registerSignHandler(signerInfo)
或者PDFDoc.sign(signInfo,digestSignHandler)
接口来实现。使用证书对摘要进行签名来获取 signedData。这可以通过调用
PDFUI.registerSignHandler(signerInfo)
或者PDFDoc.sign(signInfo,digestSignHandler)
接口来实现。将 signedData 写入文件流。signedData 的位置在 byteRange 中指定。
验证签名
获取文档原始(未修改)文件流、签名覆盖范围信息(签名域字典的 byteRange)、签名数据和签名者。
对签名覆盖范围的文件流计算相应的摘要。这可以通过调用
PDFUI.setVerifyHandler(verifyFunction)
或者PDFDoc.verifySignature(signatureField, verifyHandler)
接口来实现。验证摘要和签名数据,并输出验证结果,其中包括文档是否被修改、签名者信息是否有效和时间戳是否过期等信息。这可以通过调用
PDFUI.setVerifyHandler(verifyFunction)
或者PDFDoc.verifySignature(signatureField, verifyHandler)
接口来实现。
# 数字签名相关 API
# PDFUI.registerSignHandler(signerInfo)
当前,Foxit PDF SDK for Web 支持两种类型的 signature filter:adbe.pkcs7.detached
和 adbe.pkcs7.sha1
。
adbe.pkcs7.detached
支持的对应的摘要算法有:'sha1'
, 'sha256'
, 和 'sha384'
。
adbe.pkcs7.sha1
支持的对应的摘要算法有:'sha1'
。
此方法用于注册签名者数据。下面是示例代码:
使用 adbe.pkcs7.detached
和 sha256
:
pdfui.registerSignHandler({
filter: "Adobe.PPKLite",
subfilter: "adbe.pkcs7.detached",
flag: 0x100,
distinguishName: "e=foxit@foxitsoftware.cn",
location: "FZ",
reason: "Test",
signer: "web sdk",
showTime: true,
sign: (setting, plainContent) => {
return requestData("post", "http://localhost:7777/digest_and_sign", "arraybuffer", {
subfilter: setting.subfilter,
md: "sha256", // "sha1", "sha256", "sha384"
plain: plainContent,
});
},
});
使用 adbe.pkcs7.sha1
和 sha1
:
pdfui.registerSignHandler({
filter: "Adobe.PPKLite",
subfilter: "adbe.pkcs7.sha1",
flag: 0x100,
distinguishName: "support@foxitsoftware.com",
location: "FZ",
reason: "Test",
signer: "web sdk",
sign: (signInfo, plainContent) => {
//sign handler which complete the signing action, return a Promise with signed data;
//function getDigest() and sign() should be completed by user.
let digest = getDigest(plainContent);//plainContent is a Blob data
let signData = sign(digest);
return Promise.resolve(signData);
}
});
# PDFUI.setVerifyHandler(verifyFunction)
此方法用于设置 Verification handler,在验证签名时将调用该 handler。Verification handler 返回称为 Signature_State
的验证结果状态。下面是示例代码:
pdfui.setVerifyHandler((signatureField, plainBuffer, signedData) => {
//function getDigest() and verify() should be completed by user.
let digest = getDigest(plainBuffer);
let verifiedStatus = verify(
signatureField.getFilter(),
signatureField.getSubfilter(),
signatureField.getSigner(),
digest,
signedData
);
return Promise.resolve(verifiedStatus);
});
# PDFDoc.sign(signInfo,digestSignHandler)
此方法用于对文档进行签名。需要消息摘要和签名函数。下面是示例代码:
/**
* @returns {Blob} - File stream of signed document.
*/
const signResult= await pdfdoc.sign(signInfo,(signInfo,plainContent) => {
//function getSignData() should be completed by developer.
return Promise.resolve(getSignData(signInfo,plainContent))//plainContent is a Blob data
});
# PDFDoc.verifySignature(signatureField, verifyHandler)
此方法用于验证签名。需要回调函数。下面是示例代码:
/**
* @returns {number} - Signature state.
*/
var result = await singedPDF.verifySignature(
pdfform.getField("Signature_0"),
function verify(signatureField, plainBuffer, signedData, hasDataOutOfScope) {
//function verifySignData() should be completed by developer.
let signInfo = {
byteRange: signatureField.getByteRange(),
signer: signatureField.getSigner(),
filter: signatureField.getFilter(),
subfilter: signatureField.getSubfilter(),
};
return Promise.resolve(verifySignData(signInfo, buffer));
}
);
# PDFSignature Class
PDFSignature.isSigned()
- 检查当前签名是否已签名。PDFSignature.getByteRange()
- 获取当前签名的文件流中指定范围的字节范围。PDFSignature.getFilter()
- 获取当前签名 filter。PDFSignature.getSubfilter()
- 获取当前签名的 subfilter。
# 数字签名功能交互
您可以通过使用 API 或 UI 的方式体验我们的签名工作流。该工作流基于 Node.js后端,可以访问发布包中的 ./server/signature-server-for-win
。
# 方法1 以编程方式在当前文档上放置签名
开启服务器,运行
https://webviewer-demo.foxitsoftware.com/
。在控制台中运行如下的代码。其将自动创建一个签名域,并在该签名域上放置一个数字签名。
签名后的文档将会被下载,并在 viewer 中重新打开。您可以点击签名域来进行验证。
//this code example assumes you are running the signature service on a local host and using the default port 7777.
var pdfviewer = await pdfui.getPDFViewer();
var pdfdoc = await pdfviewer.getCurrentPDFDoc();
var 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 }
);
});
//open the signed PDF
const singedPDF = await pdfviewer.openPDFByFile(signResult);
var pdfform = await singedPDF.loadPDFForm();
var verify = (signatureField, plainBuffer, signedData, hasDataOutOfScope) => {
return requestData("post", "http://127.0.0.1:7777/verify", "text", {
filter: signatureField.getFilter(),
subfilter: signatureField.getSubfilter(),
signer: signatureField.getSigner(),
plainContent: new Blob([plainBuffer]),
signedData: new Blob([signedData]),
});
};
var result = singedPDF.verifySignature(pdfform.getField("Signature_0"), verify);
# 方法2 以 UI 的方式来放置签名
通过我们在线的 viewer https://webviewer-demo.foxitsoftware.com/
来体验其是如何工作的。
准备工作
- 在您的浏览器中打开
https://webviewer-demo.foxitsoftware.com/
。
- 在您的浏览器中打开
添加一个签名并对其进行签名
单击 Form 选项卡中的 signature 按钮,切换到 addSignatureStateHandler。
单击左键,在页面上绘制一个矩形域。
点击手型工具或者按下
Esc
键,切换到 handStateHandler。在弹出框中设置签名信息,然后单击 OK 进行签名。签名后的文档将自动下载并重新打开。
验证签名
- 点击手型工具,然后点击已签名的签名域来进行验证。此时会弹出一个提示框,显示验证的结果。
备注:为了使此签名流程正常工作,我们在 complete_webViewer 的 index.html 文件中引用了如下的回调代码,并在后端运行签名服务。
//the variable `origin` refers to the service http address where your signature service is running.
//signature handlers
var requestData = (type, url, responseType, body) => {
return new Promise((res, rej) => {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open(type, url);
xmlHttp.responseType = responseType || "arraybuffer";
let formData = new FormData();
if (body) {
for (let key in body) {
if (body[key] instanceof Blob) {
formData.append(key, body[key], key);
} else {
formData.append(key, body[key]);
}
}
}
xmlHttp.onload = (e) => {
let status = xmlHttp.status;
if ((status >= 200 && status < 300) || status === 304) {
res(xmlHttp.response);
}
};
xmlHttp.send(body ? formData : null);
});
};
//set signature information and function. This function can be called to register different algorithm and information for signing
//the api `/digest_and_sign` is used to calculate the digest and return the signed data
pdfui.registerSignHandler({
filter: "Adobe.PPKLite",
subfilter: "adbe.pkcs7.sha1",
flag: 0x100,
distinguishName: "e=foxit@foxitsoftware.com",
location: "FZ",
reason: "Test",
signer: "web sdk",
showTime: true,
sign: (setting, plainContent) => {
return requestData("post", "origin", "arraybuffer", {
plain: plainContent,
});
}
});
//set signature verification function
//the api /verify is used to verify the state of signature
pdfui.setVerifyHandler((signatureField, plainBuffer, signedData) => {
return requestData("post", "origin", "text", {
filter: signatureField.getFilter(),
subfilter: signatureField.getSubfilter(),
signer: signatureField.getSigner(),
plainContent: new Blob([plainBuffer]),
signedData: new Blob([signedData]),
});
});
# 签名 HTTP 服务
如果您没有可用的后端签名服务,您可以使用以下我们提供作为测试的 HTTP 服务路由。
http://webviewer-demo.foxitsoftware.com/signature/digest_and_sign (opens new window)
http://webviewer-demo.foxitsoftware.com/signature/verify (opens new window)