有时我们需要让用户以电子方式签署某些内容。人们通常将其理解为以某种方式将您的手写签名放在屏幕上。根据司法管辖区的不同,这可能没问题,或者仅存储图像可能还不够。例如,在欧洲,Regulation 910/2014 定义了什么是电子签名。正如可以从法律文本中预期的那样,定义相当模糊:
“电子签名”是指电子形式的数据,附加到其他电子形式的数据或在逻辑上与其他电子形式的数据相关联,并由签名人用来签名;
是的,多读几遍,多说几遍“wat”,让我们讨论一下这是什么意思。它基本上可以意味着任何东西。仅将绘制的签名图像(例如使用 html 画布)附加到数据上在技术上是可以接受的,并且仍然可以计算在内。
但是当我们涉及到更具体的电子签名类型——高级和合格的电子签名时,情况会好一些:
高级电子签名应满足以下要求: (a) 它与签名人的链接是唯一的; (b) 它能够识别签字人; (c) 它是使用电子签名创建数据创建的,签名人可以高度自信地在他的单独控制下使用这些数据; (d) 它与用其签名的数据相关联,使得数据的任何后续变化都可以检测到。
从技术意义上讲,这看起来像是一个合适的“数字签名”——例如使用私钥签名,使用公钥验证签名。 “合格”签名需要由基本上是受信任的证书颁发机构的合格提供商颁发。用于放置合格签名的密钥必须在安全设备(智能卡和 HSM)上发布,这样只有所有者才能访问私钥。
但高级签名和合格签名之间的法律区别并不完全清楚——该条例明确规定非合格签名也具有法律价值。在浏览器中使用合格签名(使用智能卡)是一种可怕的用户体验——在大多数情况下,它会通过 Java Applet,它基本上只能在 Internet Explorer 和当今特殊版本的 Firefox 上运行。备选方案包括处理签名的桌面软件和本地服务 JWS 应用程序,但智能卡目前是一个大问题和题外话。
那么,我们如何让用户“放置”电子签名呢?我有一个想法,这可以完全使用 WebCrypto API 来完成,现在浏览器或多或少地支持它。思路如下:
这是一个实现该流程的 javascript 要点。
许多片段取自非常有用的 webcrypto examples repo。 hex2buf、buf2hex 和 str2ab 函数是实用程序(可惜在 js 中不是标准的)。
代码的作用很简单,尽管有点冗长。所有操作都使用 promises 和“then”链接起来,老实说,写起来和读起来都很乏味(但我想这是不可避免的):
你可以在这里测试它:
存储签名应该足以满足“电子签名”的定义。事实上,它是一个只有用户知道的秘密密码,甚至可能意味着这是一个“高级电子签名”。存储派生的密钥是有问题的——如果你存储它,就意味着你可以代表用户“伪造”签名。但不存储它意味着您无法验证签名——只有用户可以。根据用例,您可以选择一个或另一个。
现在,我不得不承认我尝试从密码(RSA 和 ECDSA)中导出一个非对称密钥对。 WebCrypto API 不允许开箱即用。所以我尝试使用 deriveBits() 来“生成”密钥,例如为 RSA 设置“n”和“d”值,为 ECDSA 设置 x、y 和 d 值(可以在此处找到,经过一些搜索)。但我失败了——你不能只指定任何值作为 importKey 参数,而且约束没有在任何地方记录,除了低级算法细节,这有点超出了我的实验范围。
目标是如果我们只从密码中导出私钥,我们可以很容易地从私钥中导出公钥(但反之亦然)——然后我们存储公钥以进行验证,而私钥仍然是真正的私钥,这样我们就无法伪造签名。
我必须在这里添加免责声明,我意识到这不是很安全。首先,在许多情况下,从密码派生密钥是有问题的。但是,在这种情况下(放置签名),没关系。
作为旁注——使用 WebCrypto API 是乏味的。也许是因为还没有人实际使用过它,所以谷歌搜索错误基本上会给你 Chromium 的源代码,除此之外什么都没有。感觉像是未知领域(尽管文档和示例足以帮助您入门)。
这样做电子签名有没有用,我不知道。我为一个实际有意义的用例(党员声明签名)实现了它。它是否比画布上的手绘签名更好——我认为是(除非你从图像中得出密钥,在这种情况下,手写签名更好,因为熵更高)。
标签2: Java教程地址:https://www.cundage.com/article/jcg-electronic-signature-using-webcrypto-api.html