使用纯 JavaScript 签署 PDF

IT技术 javascript pdf sign signing webcrypto-api
2021-01-25 23:05:18

随着 WebCrypto API 的发展并得到 Chrome 和 Firefox 的支持,我想用它来对 PDF 文档进行数字签名。周围的文献并不多,但我找到了一些示例 [1] 和一个名为 PKI.js [2] 的库。在示例中,描述了签名过程,但最终返回了签名。我希望我的 Base64 PDF 文件以已签名的 Base64 字符串再次返回,但遗憾的是,事实并非如此。据我所知,PKI.js 也不提供对我的 Base64 PDF 进行签名的方法。

有没有办法只使用 JavaScript 和 WebCrypto API 签署 PDF?私钥可以输入<textarea>到浏览器的证书设置中,或者更好地存储在浏览器的证书设置中。

Base64 PDF(来自 REST API)→ 使用 JS 和证书签名→ 签名的 Base64 PDF(发送到 REST)

4个回答

这在技术上是可行的,事实上这是我们在制作 PKIjs 时想到的场景之一(这就是为什么有这个示例) - https://pkijs.org/examples/PDFexample.html

也就是说,进行签名需要使用 PDF 结构本身,这需要自定义解析器或对现有解析器(例如 pdfjs)的修改。

长话短说,在浏览器中签署 PDF 需要很多工作,但这是我们正在努力的事情。

你可以用它签署任何东西。这个例子招牌任意文件- pkijs.org/examples/CMSSigned_complex_example.html这个例子显示导入PKCS#12秒- pkijs.org/examples/PKCS12SimpleExample.html看到unmitigatedrisk.com/?p=543了一些限制和实现注意事项。
2021-03-11 23:05:18
我想对纯文本进行签名,那么这个库可以吗?我没有找到可以读取 p12 证书+私钥并生成签名的示例。
2021-03-27 23:05:18

披露:我为 CISPL 工作。

截至目前,WebCrypto API 不提供对 (Windows) 或任何其他密钥库或本地加密 USB/智能卡设备的访问。

同样在大多数签名场景中,为了保护服务器边界内的 pdf 文件,不建议将完整的 pdf 文件发送到浏览器或签名 API 服务器。

因此,它的良好做法是创建用于签名的 PDF 哈希值,将哈希值发送到浏览器并通过浏览器扩展使用 javascript 来访问在本地系统上运行的某些应用程序以访问本地密钥库(或 USB/智能卡)并生成签名并发回( PKCS7 或 CMS 容器(在 PDF 签名的情况下)到服务器,其中签名可以注入回 PDF,从中创建哈希用于签名并发送到浏览器或签名 api 服务器。

对于基于浏览器的签名场景,我公司提供了一种免费的浏览器扩展 Signer.Digital 和服务器所需的 .NET 库。本地系统(在 Windows 上运行在 Chrome 浏览器后面的主机)可以从cNET 下载站点下载 安装此主机并重新启动 Chrome 将自动添加Signer.Digital Chrome 扩展和/或Signer.Digital Firefox 扩展

此处说明了此扩展的实际工作以及完整的代码演练和工作示例 VS 2015 项目源代码的下载链接。

从扩展调用方法的Javascript:

 //Calculate Sign for the Hash by Calling function from Extension SignerDigital
 SignerDigital.signPdfHash(hash, $("#CertThumbPrint").val(), "SHA-256")      //or "SHA256"
  .then(
         function (signDataResp) {
           //Send signDataResp to Server
     },
         function (errmsg) {
             //Send errmsg to server or display the result in browser.
           }
  );

如果成功,则返回 Base64 编码的 pkcs7 签名 - 使用合适的库或 Signer.Digital 提供的库将签名注入 pdf

如果失败,则返回以“SDHost 错误”开头的错误信息:

来自浏览器的数字签名

来自浏览器的数字签名

  1. 服务器向浏览器发送要签名的数据/文档/内容的哈希值。
  2. 浏览器使用 Signer.Digital 浏览器扩展 Javascript API 来调用来自 Signer.Digital 浏览器扩展主机的操作。
  3. 在 Windows 上,浏览器扩展主机使用 Microsoft 证书存储和底层 CSP 来获得哈希签名。
  4. 在 Linux 上,浏览器扩展主机使用 Crypto Device 的 PKCS#11 .SO 库来获得哈希签名。
  5. 原始签名(哈希签名)或签名容器由 Signer.Digital Browser EXtension Host 返回到浏览器。
  6. 如果是加密设备,即。USB 令牌或智能卡,用户的私钥永远不会从设备中出来,但要签名的哈希值会发送到设备以使其签名。
  7. Web 应用程序(浏览器中的 Javascript)将签名发送回服务器,并且可以在 PDF 文档、XML 或 Json 中或根据需要修改相同的签名。
“我说的是在服务器上生成 pdf。” - 即便如此,如果我不相信该服务器向我发送正确的哈希值,我(作为用户)希望能够通过下载 pdf、使用受信任的软件在本地签名,然后上传再次签署pdf。不过,我必须承认,任意浏览器扩展在这里并不真正算作受信任的软件,所以在这里,用例是否只向我传输散列或整个文档确实无关紧要......
2021-03-14 23:05:18
一些例子是在我公司的 Web 应用程序(比如 CRM 或基于 Web 的会计软件)上签署我刚刚准备好的发票或采购订单,我想最终签名。另一个例子是 eReturn 我刚刚在我的公司 web 应用程序或 eReturn 服务提供商的 web UI 上预览,然后我不喜欢在签名之前查看返回的 XML 或 Json.. 只是想帮助受信任的案例......你想要我用“普遍做法”代替“良好做法”?!:)
2021-03-21 23:05:18
@mki,我说的是在服务器上生成的 pdf。如果用户在他自己的计算机上有 PDF,那么有很多工具可用,包括最常用的 Acrobat Reader 来签署 PDF 文档......但问题是关于使用 JavaScript 签名,这意味着文档在服务器上,签名在浏览器上。
2021-03-27 23:05:18
不需要改变任何东西。如果用户有理由信任有问题的服务器(例如,由适当的 CC 认证支持),那么该解决方案确实有其魅力。
2021-03-31 23:05:18
“它的良好做法......” - 好的做法是什么,取决于您更信任哪个应用程序。您的回答假定服务器应用程序是值得信赖的,可以信任它为用户确实想要签名的 pdf 提供哈希值。对于第一次处理某个服务器应用程序的用户来说,这个假设可能不正确,他碰巧在他的计算机上有一个他信任的签名应用程序。
2021-04-07 23:05:18

PDFSign.js,一个可以在浏览器中签署 PDF 文件的库。虽然它使用伪造作为签名。如果PKI.js支持分离的pkcs7签名,那么替换forge应该很容易。

您可以使用 openpgp.js 签署任何文件(包括 pdf)

https://openpgpjs.org/openpgpjs/doc/#create-and-verify-detached-signatures

(向下滚动到“创建并验证分离的签名”)

将文件作为 Uint8Array 读取并使用您的私钥对其进行签名。

每当人们想要签署并强调他们想要签署 pdf 的事实时,他们通常意味着他们想要使用集成的 pdf 签名而不是使用单独的、分离的签名文件进行签名。这意味着,对于可互操作的签名,PKCS#1/PKCS#7 格式基于 X.509 证书,而不是 PGP 格式。
2021-03-21 23:05:18