使用 WebCrypto API 的电子签名

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(245)   2023-12-04 15:34:57

有时我们需要让用户以电子方式签署某些内容。人们通常将其理解为以某种方式将您的手写签名放在屏幕上。根据司法管辖区的不同,这可能没问题,或者仅存储图像可能还不够。例如,在欧洲,Regulation 910/2014 定义了什么是电子签名。正如可以从法律文本中预期的那样,定义相当模糊:

“电子签名”是指电子形式的数据,附加到其他电子形式的数据或在逻辑上与其他电子形式的数据相关联,并由签名人用来签名;

是的,多读几遍,多说几遍“wat”,让我们讨论一下这是什么意思。它基本上可以意味着任何东西。仅将绘制的签名图像(例如使用 html 画布)附加到数据上在技术上是可以接受的,并且仍然可以计算在内。

但是当我们涉及到更具体的电子签名类型——高级和合格的电子签名时,情况会好一些:

高级电子签名应满足以下要求: (a) 它与签名人的链接是唯一的; (b) 它能够识别签字人; (c) 它是使用电子签名创建数据创建的,签名人可以高度自信地在他的单独控制下使用这些数据; (d) 它与用其签名的数据相关联,使得数据的任何后续变化都可以检测到。

从技术意义上讲,这看起来像是一个合适的“数字签名”——例如使用私钥签名,使用公钥验证签名。 “合格”签名需要由基本上是受信任的证书颁发机构的合格提供商颁发。用于放置合格签名的密钥必须在安全设备(智能卡和 HSM)上发布,这样只有所有者才能访问私钥。

但高级签名和合格签名之间的法律区别并不完全清楚——该条例明确规定非合格签名也具有法律价值。在浏览器中使用合格签名(使用智能卡)是一种可怕的用户体验——在大多数情况下,它会通过 Java Applet,它基本上只能在 Internet Explorer 和当今特殊版本的 Firefox 上运行。备选方案包括处理签名的桌面软件和本地服务 JWS 应用程序,但智能卡目前是一个大问题和题外话。

那么,我们如何让用户“放置”电子签名呢?我有一个想法,这可以完全使用 WebCrypto API 来完成,现在浏览器或多或少地支持它。思路如下:

  • 让用户输入密码以进行登录
  • 从密码中导出密钥(例如使用 PBKDF2)
  • 使用派生密钥签署用户提交的表单内容
  • 将签名与其余表单数据一起存储
  • 可选地,存储派生密钥以用于验证目的

这是一个实现该流程的 javascript 要点

许多片段取自非常有用的 webcrypto examples repo。 hex2buf、buf2hex 和 str2ab 函数是实用程序(可惜在 js 中不是标准的)。

代码的作用很简单,尽管有点冗长。所有操作都使用 promises 和“then”链接起来,老实说,写起来和读起来都很乏味(但我想这是不可避免的):

  • 密码作为原始密钥加载(转换为数组缓冲区后)
  • 使用 PBKDF2 派生密钥(迭代 100 次)
  • 秘钥用于对用户填写的内容进行HMAC“签名”
  • 签名和密钥已存储(在此示例中的 UI 中)
  • 然后可以使用数据、签名和密钥来验证签名

你可以在这里测试它:

存储签名应该足以满足“电子签名”的定义。事实上,它是一个只有用户知道的秘密密码,甚至可能意味着这是一个“高级电子签名”。存储派生的密钥是有问题的——如果你存储它,就意味着你可以代表用户“伪造”签名。但不存储它意味着您无法验证签名——只有用户可以。根据用例,您可以选择一个或另一个。

现在,我不得不承认我尝试从密码(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

相关阅读

Java HashSet 教程展示了如何使用 Java HashSet 集合。 Java哈希集 HashSet 是一个不包含重复元素的集合。此类为基本操作(添加、删除、包含和大小)提供恒定时间性...
SpringApplicationBuilder 教程展示了如何使用 SpringApplicationBuilder 创建一个简单的 Spring Boot 应用程序。 春天 是用于创建企业应...
通道是继 buffers 之后 java.nio 的第二个主要新增内容,我们在之前的教程中已经详细了解了这一点。通道提供与 I/O 服务的直接连接。 通道是一种在字节缓冲区和通道另一端的实体(通...
课程大纲 Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了一个分布式的、支持多租户的全文搜索引擎,带有 HTTP Web 界面和无模式的 JSON 文档。 Elasti...
解析器是强大的工具,使用 ANTLR 可以编写可用于多种不同语言的各种解析器。 在这个完整的教程中,我们将: 解释基础:什么是解析器,它可以用来做什么 查看如何设置 ANTLR 以便在 Java...
Java 是用于开发各种桌面应用程序、Web 应用程序和移动应用程序的最流行的编程语言之一。以下文章将帮助您快速熟悉 Java 语言,并迈向 API 和云开发等更复杂的概念。 1. Java语言...
Java中的继承是指子类继承或获取父类的所有非私有属性和行为的能力。继承是面向对象编程的四大支柱之一,用于提高层次结构中类之间的代码可重用性。 在本教程中,我们将了解 Java 支持的继承类型,...
Java Message Service 是一种支持正式通信的 API,称为 网络上计算机之间的消息传递。 JMS 为支持 Java 程序的标准消息协议和消息服务提供了一个通用接口。 JMS 提...
之前,我介绍了spring 3 + hibernate 集成 示例和struts 2 hello world 示例。在本教程中,我将讨论在将 spring 框架与 struts 与 hibern...
Java 项目中的一项常见任务是将日期格式化或解析为字符串,反之亦然。解析日期意味着你有一个代表日期的字符串,例如“2017-08-3”,你想把它转换成一个代表 Java 中日期的对象,例如Ja...