在现代 Firefox/Chrome/Edge 浏览器中对 PDF 进行数字签名

Digitally Signing a PDF in Modern Firefox/Chrome/Edge Browsers

我一直在寻找这个问题的答案。

我有一个用 AngularJS 编写的 Web 应用程序,目前需要 IE11 和 Acrobat 插件才能对 PDF 表单进行数字签名。但是,该插件在 IE11 上是垃圾,并且不支持现代浏览器(我定义为 Firefox、Chrome 和 Edge。我公司将不支持 Safari。)

因为应用程序是 AngularJS(而不是 运行 在 Node 上),我需要一个 javascript 解决方案来签署 PDF。不仅如此,签名证书也保存在智能卡上,这意味着我需要一个可以通过某种 PKCS#11 接口访问证书的加密库。在Javascript。客户目前无法在 Acrobat 中打开外部表单。

我查看了多个库,但无法找出一个直接的答案:

None 这些软件包为我提供了足够的信息,让我知道是否要进一步研究它们。

有人可以提供更多信息或指导吗?

谢谢, 杰森

此答案旨在解决问题,而不是实际答案。

这是因为目前浏览器中没有API可以用来获取智能卡证书的私钥并使用它。据推测,这已经在 Web Crypto API 中进行了讨论,据我所知 - 共识是出于安全原因不应支持这一点(我强烈反对!)。

您和成百上千的其他开发人员(包括我们)一样,运气不佳。

第一个解决方法 涉及部署在服务器上的 .NET ClickOnce 桌面应用程序和来自服务器的 运行。该应用程序在 运行 时间参数中获取当前用户会话的安全上下文,以便在浏览器和 运行 浏览器旁边的应用程序之间共享该会话。从这个意义上说,运行独立运行这个应用程序(没有浏览器中的会话)会在与服务器通信期间导致授权问题。

该应用程序使用服务器的 API 来首先检索用户将要签名的文档。然后应用程序不受限制地使用本地证书存储(因为它是常规桌面应用程序),加密文档并将其发送回服务器。

优点: 可以从浏览器中调用 ClickOnce 应用程序。

缺点:这需要 .NET 运行客户端时间。

第二种解决方法 涉及一个 Java 桌面应用程序,该应用程序独立安装在客户端的计算机上。您为选定的OSes(比如Windows、Linux、MacOS)提供安装包,用户下载安装包并在他们的OS.

然后,当浏览器应该签署文档时,您提供一条指令,告诉用户 运行 应用程序在后台运行。该应用程序在 运行 时在本地主机和固定端口上公开一个 HTTP 侦听器,具有 两项服务

  • 接受要签名的文档数据的推送服务
  • 公开签名文档的拉取服务,当它可用时

如您所料,是浏览器发出请求,浏览器向 localhost:port 发出请求并将文档数据上传到推送服务。 Java 应用程序从 等待文档 切换到 正在签署文档 状态。用户应该使用该应用程序 - 从商店中选择一个证书(没有限制,因为是常规 Java 桌面应用程序)并签署文档。您的浏览器在后台 ping 应用程序的拉取服务,当数据准备就绪时,浏览器会下载它。然后,浏览器使用实际经过身份验证的会话将签名文档上传到实际服务器。

这里存在潜在的安全漏洞,因为任何本地应用程序或任何打开的网页都可以 ping 拉取服务并下载文档(您当然不希望这样做)。我们知道对此有两个修复。

首先,您可以在 Java 应用程序中使用另一项服务,该应用程序 returns 一次性身份验证令牌(例如 guid),该令牌将被读取一次,然后随附提供每次调用拉取服务作为身份验证令牌。如果任何其他恶意应用程序或网页在 之前 读取您的应用程序网页读取令牌,您的页面将从拉取服务中获取错误(因为一次性令牌显然已被盗并且没有可用的)。该网页可能会在此处发出通信错误信号,并警告用户潜在的安全问题。

修复漏洞的第二种方法涉及应用程序服务器提供的拉取服务调用的参数,并将其作为值放入页面的脚本中,这是一个由服务器证书签名的令牌。您的 Java 应用程序可以拥有服务器证书的 public 密钥,以便 Java 应用程序能够验证参数的签名。但是没有其他应用程序(也没有其他页面)能够伪造令牌(因为令牌的签名私钥只能在您的服务器上使用)并且没有简单的方法可以从页面主体中窃取有效令牌。

优点:Java 应用程序可能针对多个 OSes 缺点:这仍然需要在客户端Java 运行时间

这两种解决方法都在生产中进行了测试,并且都运行良好多年。我希望这能为您提供一个可能的方向,您的最终解决方案可能以此为基础。

@Json,你说“这不是我想听到的,但我会接受你的回答。” !!

我建议使用浏览器扩展对 pdf、文件或现代浏览器中的任何内容进行数字签名。浏览器扩展将在提供 JavaScript 的浏览器与本地连接到扩展主机(应用程序)运行 的浏览器和访问 CertStore 之间插入。

我们公司发布了一个这样的扩展,并且是免费的,它使用 .NET Framework 3.5,它在所有 Windows 客户端上普遍可用。它不使用 PKCS#11 简单地消除了提供 PKCS#11 驱动程序等的需要,并且一旦安装成功就透明地工作。它使用 Windows CertStore。

根据您的评论,您有更多客户端 运行 firefox,但我们正在开发 Edge 和 Firefox 扩展。

安装程序(主机应用程序 运行 在 windows 上的 chrome 浏览器后面)可以从 https://download.cnet.com/Signer-Digital-Chrome-Extension/3000-33362_4-78042540.html 下载 安装此主机并重新启动Chrome 将自动添加Signer.Digital Chrome 扩展程序

说明了此扩展的实际工作方式here

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.
              }
     );

如果成功,returns Base64 编码的 pkcs7 签名 - 使用任何合适的库将签名注入 pdf 如果失败,returns 以“SDHost 错误:”开头的错误消息:

希望对您有所帮助!

我从 2016 年开始寻找类似的东西。在巴西,我们有 Lacuna Software WEBPKI,但它一定很贵。

我昨天发现了这个:https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/pkcs11