首页 > 解决方案 > 在现代 Firefox/Chrome/Edge 浏览器中对 PDF 进行数字签名

问题描述

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

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

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

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

这些软件包都没有提供足够的信息让我知道是否要进一步研究它们。

那里的任何人都可以为我提供更多信息或方向吗?

谢谢,杰森

标签: javascriptpkcs#11

解决方案


该答案旨在解决方法,而不是实际答案。

这是因为目前浏览器中没有 API可以用来获取智能卡证书的私钥并使用它。这应该在 Web Crypto API 中讨论过,据我所知 - 出于安全原因,共识是不应该支持(我强烈不同意!)。

作为成百上千的其他开发人员(包括我们在内),您不走运。

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

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

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

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

第二种解决方法涉及独立安装在客户端计算机上的 Java 桌面应用程序。您为选定的操作系统(例如 Windows、Linux、MacOS)提供安装包,用户下载安装包并将应用程序安装到他们的操作系统中。

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

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

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

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

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

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

优点:Java 应用程序可能针对多个操作系统缺点:这仍然需要客户端的 Java 运行时

这两种解决方法都在生产中进行了测试,并且都可以运行多年。我希望这给你一个可能的方向,你的最终解决方案可以基于。


推荐阅读