数字签名
在本节中,您将了解签名和验证签名的常规步骤、相关的签名 API、数字签名交互的方式、以及我们提供的测试签名服务路由。
在 PDF 上签名和验证数字签名的步骤
在 PDF 上签名和验证数字签名,应执行以下过程:
- 签名文档
- 生成包含签名覆盖范围信息(签名域字典的byteRange)的文档文件流。有关详细信息,请参阅 PDF Reference 1.7或更高版本。
- 对签名覆盖范围的文件流计算相应的摘要。这可以通过调用registerSignHandler(signerInfo)或者PDFDoc.sign(signInfo,digestSignHandler)接口来实现。
- 使用证书对摘要进行签名来获取signedData。这可以通过调用registerSignHandler(signerInfo)或者PDFDoc.sign(signInfo,digestSignHandler)接口来实现。
- 将signedData写入文件流。signedData的位置在byteRange中指定。
- 验证签名
- 获取文档原始(未修改)文件流、签名覆盖范围信息(签名域字典的byteRange)、签名数据和签名者。
- 对签名覆盖范围的文件流计算相应的摘要。这可以通过调用setVerifyHandler(verifyFunction)或者PDFDoc.verifySignature(signatureField, verifyHandler) 接口来实现。
- 验证摘要和签名数据,并输出验证结果,其中包括文档是否被修改、签名者信息是否有效和时间戳是否过期等信息。这可以通过调用setVerifyHandler(verifyFunction) 或者PDFDoc.verifySignature(signatureField, verifyHandler) 接口来实现。
数字签名相关 API
PDFUI.registerSignHandler(signerInfo)
此方法用于注册签名者数据。下面是示例代码:
pdfui.registerSignHandler({ filter: "Adobe.PPKLite", subfilter: "adbe.pkcs7.sha1", flag: 0x100, distinguishName: "support@foxitsoftware.com", location: "FZ", reason: "Test", signer: "web sdk", sign: (signInfo, buffer) => { //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(buffer); 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,(buffer) => { //function getSignData() should be completed by developer. return Promise.resolve(getSignData(signInfo,buffer)) });
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
- isSigned() – 检查当前签名是否已签名。
- getByteRange() – 获取当前签名的文件流中指定范围的字节范围。
- getFilter() – 获取当前签名filter。
- getSubfilter() – Get the current signature subfilter.
数字签名功能交互
您可以通过使用 API 或 UI 的方式体验我们的签名工作流。该工作流基于Node.js后端,可以访问发布包中的 ./server/pkcs7。
方法1以编程方式在当前文档上放置签名
- Run https://webviewer-demo.foxitsoftware.com/with starting a service.
- 开启服务器,运行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, (buffer) => { return requestData( "post", "http://127.0.0.1:7777/digest_and_sign", "arraybuffer", { plain: new Blob([buffer]) } ); }); //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=zhiquan_ye@foxitsoftware.cn", location: "FZ", reason: "Test", signer: "web sdk", showTime: true, sign: (setting, buffer) => { return requestData("post", "origin", "arraybuffer", { plain: new Blob([buffer]), }); }, }); //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
http://webviewer-demo.foxitsoftware.com/signature/verify
https://webviewer-demo.foxitsoftware.com/signature/digest_and_sign
https://webviewer-demo.foxitsoftware.com/signature/verify
中国服务器:
http://webviewer-demo.foxitsoftware.cn/signature/digest_and_sign