几种签名和验证的使用场景和实现

555 阅读8分钟

概述

签名和验证,是一个信息安全和密码学概念。狭义的签名,是指为数字信息(所有类型,包括电子邮件、文档、代码、文本块等)生成和添加一个唯一的数字标识(通常是一段数字编码),目的是证明该信息的完整、真实和来源。

完整的签名验证的技术实现,通常是首先对信息进行摘要计算,然后使用数字加密技术,和签名方的私钥来对此摘要来进行加密操作;这一签名只能由此私钥对应的公钥,才能够进行正确的验证。我们可以看到,这一过程中,摘要算法可以保证信息的完整性,验证操作的成功可以保证这个摘要(也就是可以对应原始信息)只能由验证用的公钥所对应的私钥生成,从而保证原始信息确实来自于且只能来自于签名方(我们假设私钥是由签名方秘密保存和使用的),从而达成的信息的真实性和不可抵赖性(来源)。

另外,之所以通常对信息先进行摘要,而不是对原始信息进行直接签名操作,通常是工程上的考虑,直接签名的计算量和签名结果的内容长度,和签名的原始内容相关,直接对内容签名,会大幅度增加签名计算的时间和资源占用,而且签名的结果也不能控制。所以通常先使用摘要算法,获得一个固定长度的信息标识,然后对这个标识进行签名操作。由于摘要的长度是固定的,而且摘要计算通常非常高效,这样可以使整个签名过程和结果都是高效和可控的。

在实际的应用场景中,我们也可以简化这一操作,有时候只需要保证信息的完整性,也不需要第三方来检查其真实性,这样就引出了广义上的信息签名和更广泛的应用场景和实现。

自己签名自己验证(自签自验)

如果一个网络服务,只需要且只能由自己来验证自己签名的信息,可以使用如下的方式。 最简单的实现方式就是hmac算法。首先使用一个标准密钥,对一段信息进行HMAC计算,得到的结果就可以作为这段信息的签名。验证时,使用同样的方式来计算签名,并和以前的签名进行比较,就可以验证当前的签名有效性和信息的完整性了。其实现伪代码如下:

sign = hmac(content, key);

verify = hmac(content,key) == sign

自签自验证的一个典型的使用场景就是token的分发和验证。认证服务器完成认证后,会给当前登录的用户颁发一个访问令牌,为确保此令牌是由认证服务器颁发且只需要其来验证,就可以使用此方式。

双方签名和验证(对签对验)

对于一些点对点的通信和数据交换服务,信息交换是在两个特定节点之下进行的,它们都应该可以对信息的完整性进行检查,特别是只能在这一对通信主体之间来进行检查。这个场景下,签名验证的主体是一对,比如相对固定的客户端和服务端,就可以使用这种方式。 具体实现的方式,可以是通过密钥协商机制(如DH算法)首先协商出一对共享密钥,然后使用此密钥对传输的信息进行签名。其伪代码和流程如下:

askey = dh(avkey,bpkey); signa = hmac(content,askey);

bskey = dh(bvkey,apkey); signb = hmac(content,bskey);

verify = signa === signb;

一方签名和多方验证(一签多验)

这是标准的基于公钥方式的签名验证场景。签名主体首先生成用于签名的密钥对,并外部公开其公钥;在信息传输时,使用其私钥对信息的内容进行签名;其他任何可以获得公钥的主体,都可以对其进行验证,来确认此信息的完整、真实并且来源于签名方。

这种场景和使用有以下特点:

  • 签名的验证是开放的,签名方不确定信息的接收者和潜在的签名验证方
  • 除完整性外,验证操作可以保证信息来源和真实性,但前提条件是需要先确认公钥的来源(可以由证书验证或控制公钥分发过程来保证)
  • 任何人都可以进行验证操作
  • 通常由密码学库提供特定的签名和验证方法来实现,开发者不能自己操作过程和数据

此过程的伪代码如下:

sign = sign(content, vkey) verify = verify(content, sign, pkey)

验证过程实现的进一步探讨

从上面的几种签名验证实现方式我们可以发现,在验证阶段对签名检查,其实由两种思路:

简单的方式,就是再进行一次签名,然后对签名的内容进行比较,这一方式显然比较灵活,我们可以随意定义签名信息的格式和算法,比如如果觉得SHA256计算的结果太长,我们也不需要如此之高的安全性,我们可以将其再次缩减为一个较短的结果,牺牲一小部分安全性来换取签名信息的缩短。

复杂和完整的方式,利用了不对称加密的算法(后详),和数学方面的理论。是基于签名信息从另一个角度进行的计算,来检查信息的复合性,这一方式更为严谨科学,也会更安全。但对实现的技术要求比较高,也可以想见其运算效率比较低。

注:其实可能还有一种思路,就是利用非对称加密的加密解密功能,但反向操作。使用私钥对摘要进行加密,加密结果作为信息的签名。验证时,使用签名的公钥对签名进行解密,解密结果当然就是信息的摘要了,解密是否能够正确执行,以及摘要能够匹配,就可以作为验证的结果。 这一操作看起来可行,但好像正规的签名验证并不是这样实现的。

ECC签名验证原理

非对称加密由两大量实现方式,RSA和ECC(椭圆曲线)。 RSA的理论基础是大质数乘积和分解运算的不对称性,经过一段时间的发展,ECC计算也比较成熟了。我们通常认为ECC的技术相对比较先进,运算效率较高,相同安全等级所需要的密钥长度和长度增长也较小,应该时未来主要的发展方向。

下面的内容来自chatGPT,略有修改,我们并不是要来完全理解ECC签名验证的原理,而是帮助读者理解,ECC的验证,并不是简单的信息匹配或者私钥加密公钥解密这种实现方案。

ECC(Elliptic Curve Cryptography)是一种公钥加密算法,与RSA和DSA一样,可以用于数字签名。ECC签名和验证的原理如下:

  • 密钥生成:首先需要生成一个密钥对。其中私钥是一个随机数, 公钥是此随机数在椭圆曲线上对应点的坐标(x,y)。

  • 签名过程:假设要对消息m(已摘要)进行签名。签名者使用私钥对消息m进行加密生成一个密文c。具体过程为:

    a. 选择一个随机数k,计算kG(G为椭圆曲线上的基点,一般固定),其中G为公共参数。

    b. 计算r = xk mod n,其中n为椭圆曲线上点的个数。

    c. 计算s = (m + dr) / k mod n,其中m为消息的摘要,d为私钥。

    d. 签名结果为(r,s)。

  • 验证过程:接收者收到签名(r,s)和消息m后,使用公钥(x,y)对签名进行验证。具体过程为:

    a. 计算e = H(m)。

    b. 计算w = s^-1 mod n。

    c. 计算u1 = ew mod n,u2 = rw mod n。

    d. 计算点(x1,y1)= u1G + u2(x,y)。

    e. 如果r ≡ x1 mod n,则验证通过;否则验证失败。

其中,w为s的逆元,u1,u2,(x1,y1)为临时变量。

ECC签名和验证过程中的核心是椭圆曲线上的加法和倍乘运算,以及对哈希函数的使用。相对于传统的RSA和DSA签名算法,ECC具有更短的密钥长度和更高的安全性。

小结

以上就是笔者总结的一些常见的信息签名和验证的使用场景和实现的技术方案,帮助读者在实际的项目中,根据实际需求和条件限制,选择合适的实现方式,满足业务和安全的需求。