这样啊 任务来了。 使用浏览器,邀请用户使用电子签名(以下称为EP)对PDF进行签名。 用户必须具有包含证书,公钥和私钥的令牌。 接下来,在服务器上,您需要在PDF文档中插入签名。 之后,您需要检查签名的有效性。 我们使用ASP.NET,因此使用C#作为后端。
麻烦的是,您需要使用CAdES-X Long Type 1格式的签名,以及俄语的GOST R 34.10-2001,GOST R 34.10-2012等。 另外,可以有多个签名,即用户可以轮流签名文件。 但是,以前的签名必须保持有效。
在解决问题的过程中,他们决定为我们增加麻烦,并减少传输给客户的数据量。 仅传输文档的哈希,而不传输文档本身。
在源代码中,我将忽略对该主题意义不大的时刻,而仅将其留给密码学使用。 我将只为那些JS引擎支持Promise和函数生成器的普通浏览器提供JS代码。 我认为谁需要为IE自己写东西(我不得不“不想”)。
您需要什么:
- 用户必须收到密钥对和证书。
- 用户必须从Crypto PRO安装插件。 没有这个,我们将无法使用JS工具与加密服务提供商合作。
备注:
- 对于测试,我有测试Crypto PRO CA颁发的证书以及我们的一名员工收到的普通令牌(在撰写本文时,大约1500r并具有Crypto PRO的年度许可证和两个证书:但是“新”和“旧” GOST)
- 他们说该插件可以与ViPNet一起使用,但我尚未对其进行测试。
现在我们假设在我们的服务器上有一个可供签名的PDF。
将来自Crypto PRO的脚本添加到页面:
<script src="/Scripts/cadesplugin_api.js" type="text/javascript"></script>
然后我们需要等到cadesplugin对象形成为止
window.cadespluginLoaded = false; cadesplugin.then(function () { window.cadespluginLoaded = true; });
我们问哈希服务器。 以前,为此,我们仍然需要知道用户将签署哪个证书以及算法。 一点说明:我将用于客户端加密的所有功能和“变量”组合到了一个CryptographyObject中。
填充CryptographyObject对象的certificate字段的方法:
fillCertificates: function (failCallback) { cadesplugin.async_spawn(function*() { try { let oStore = yield cadesplugin.CreateObjectAsync("CAPICOM.Store"); oStore.Open(cadesplugin.CAPICOM_CURRENT_USER_STORE, cadesplugin.CAPICOM_MY_STORE, cadesplugin.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED); let certs = yield oStore.Certificates; certs = yield certs.Find(cadesplugin.CAPICOM_CERTIFICATE_FIND_TIME_VALID); let certsCount = yield certs.Count; for (let i = 1; i <= certsCount; i++) { let cert = yield certs.Item(i); CryptographyObject.certificates.push(cert); } oStore.Close(); } catch (exc) { failCallback(exc); } }); }
注释:尝试打开证书存储。 此时,用户的系统将警告您该站点正在尝试使用证书,密码学和其他不可思议的不可理解的废话。 用户将需要单击“是”
接下来,我们获取及时有效的证书(未过期)并将其放入certificates数组中。 由于cadesplugin的异步特性(对于IE,一切都不同;),必须完成此操作。
哈希方法:
getHash: function (certIndex, successCallback, failCallback, - ) { try { cadesplugin.async_spawn(function*() { let cert = CryptographyObject.certificates[certIndex]; let certPublicKey = yield cert.PublicKey(); let certAlgorithm = yield certPublicKey.Algorithm; let algorithmValue = yield certAlgorithm.Value; let hashAlgorithm;
注释:请注意cadesplugin.async_spawn,将生成器函数传递给该函数,在该函数上依次调用next(),从而导致转换为yield。
因此,我们从C#获得了async-await的某种模拟。 一切看起来都是同步的,但是异步地工作。
现在,当请求哈希时,在服务器上会发生什么。
首先,您需要安装iTextSharp nuget软件包(在撰写本文时,当前版本应为5.5.13)。
其次,需要CryptoPro.Sharpei,这将由Crypto PRO .NET SDK承担
现在你可以得到哈希
在客户端,从服务器获取哈希后,我们对其进行签名
注释:将收到的签名发送到服务器(请参见上文)
好了,最后,在服务器端的文档中插入签名
注释:SimpleExternalSignatureContainer是实现IExternalSignatureContainer接口的最简单的类。
实际上,仅通过PDF签名即可。 验证将在本文的后续部分中描述。 我希望她会...
对收到Oid签名算法的评论进行了更正。 谢谢啦