注册
iOS

数字签名为什么可以防篡改

数字签名是什么


数字签名是一种数字技术,用于验证和保护数据的完整性


数字签名是通过一些加密算法将消息或文件与公钥(如果是非对称加密就有公钥不然就不用)绑定在一起,并生成唯一的签名。


数字签名的工作原理


数字签名的核心在于加密算法。最常用的是非对称加密算法,它将原文通过特定HASH函数得到的摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文提炼出一个摘要信息,与解密得到的摘要进行对比。


数字签名也可以使用哈希函数对文件或消息的散列值进行加密,确保消息不会被篡改。(也有人认为摘要算法不能逆向也就是解密所以不是加密算法,在此不做讨论)


数字签名可以与数字证书结合使用,以证明密钥的归属和真实性,从而保护数字签名过程不被破坏。


数字签名的应用


JWT


JWT通常由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature),以点号分隔。第一部分是头部,第二部分是载荷,第三部分是签名。以下是一个包含了用户ID、用户名和时间戳的JWT实例,格式为 Header.Payload.Signature

// 为方便展示,在'.'处作了换行处理,可以更好地看清楚结构
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

HeaderPayload都是经过 Base64URL 编码的,所以每个人都能通过解码得到原来的信息,固不应该在里面存一些敏感信息。


420221b8dffc596d0080c2f01c54722c.png


Signature就是我们要讨论的数字签名了!Signature 部分是对前两部分的签名,防止数据篡改。首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。Signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)


HMAC算法 是一种基于密钥的报文完整性的验证方法。HMAC算法利用哈希运算,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。其安全性是建立在Hash加密算法基础上的。


由于Signature是根据HeaderPayload以及服务器的secret来生成的,由于secret只有服务器知道,所以只要HeaderPayloadSignature其中一个被篡改了,那么后续验证的时候就不能通过。同时只有知道secret才能产生与HeaderPayload配对的Signature,所以也能确认该 Token 是否是该服务器所颁发的。


验证过程是用服务器的密钥通过同样的算法计算出一个新的 Signature 然后和旧的 Signature 进行比较,只要被篡改那么 Signature就会跟着改变,所以通过比较, Signature 一样的话则证明没有被篡改,否则则认为被篡改了。


CA 证书


其实就是这个 CA 证书的数字签名为什么可以防篡改,困扰了我好久,所以才去稍微深入了解了一下然后写下了这篇博客。就是为了这点醋,我才包的这顿饺子~


之前学习 https 的时候,看了各大论坛的帖子发现有挺多帖子对于 CA 证书是怎么做放篡改的讲的不太对或者讲的不太清晰,所以这个问题困扰了我挺久。以下是随便找的一些帖子(对事不对人):


例1:


6d0da3aab9aca93afae251c17324fa58.png


例2:


05115d2382d780c565bba1ed8887cd1c.png


例3:


c76d7fd6866d35eb7701cbb3a994ea27.png


(例1和例2是随便在掘金上面搜到的相关文章,例3是newbing的回答)


先回顾一下数字证书验证的大概过程:


CA 签发证书的过程:

  • 首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
  • 然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
  • 最后将 Certificate Signature 添加在文件证书上,形成数字证书;

客户端校验服务端的数字证书的过程:

  • 首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
  • 浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
  • 最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。

核心问题在于验证服务器发来的数字证书的数字签名时所用到的公钥是哪里来的。


假设有那么一个场景:


客户端A 和 服务器A 的通信过程中,私钥是Secret_RSA_A,公钥是Secret_PUB_A。服务器A 将自己的证书CA发给客户端A的过程中被 中间人B 给截获了,中间人B 用自己的公钥Secret_PUB_B 替换了 服务器A 发给 客户端A 的CA证书的公钥Secret_PUB_A,并且用和公钥Secret_PUB_B 配对的私钥Secret_RSA_B 对替换公钥后的CA证书的公钥、用途、颁发者、有效时间等信息生成的新HASH 进行加密,生成新的 Certificate Signature 并把原本证书上的 Certificate Signature 替换掉。但客户端A 对这并不知情。然后在后续客户端对该 CA证书验证的过程中,如果使用的是证书上的公钥,那么计算出来的 H1 和 H2 就会一样,也就是认为证书是可信的。(实际上加密使用的是CA私钥而不是服务器私钥所以中间人伪造不了一对新的公私钥,但是如果使用服务器发送过来的公钥去验证的话那么就有可能被伪造)


所以更加安全的做法应该是不使用传过来的证书上面的公钥(证书上的公钥是服务器持有者的公钥而不是CA公钥),而是使用预置在操作系统里面的公钥,因为证书加密是用CA私钥加密的而不是用服务器持有者的私钥进行加密的,传服务器持有者的公钥过来是为了和客户端协商然后生成后续对称加密通信需要用到的秘钥。这也是我之前看到的一些文章没有提到的(如上面的图1/2/3所示,没有针对原作者的意思),容易让人困惑。服务器发送过来的证书中的公钥是服务器的公钥而不是可以解密数字签名的公钥(数字签名的公钥也就是和CA证书配对的公钥)。 通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用操作系统内置的 CA 的公钥解密 Certificate Signature 内容。这行验证过程中存在一个证书信任链的问题。客户端收到服务器发送过来的CA证书后,浏览器开始查找操作系统中已内置的受信任的证书发布机构CA,与服务器发来的证书中的颁发者CA比对,用于校验证书是否为合法机构颁发,如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的。如果找到,那么浏览器就会从操作系统中取出 颁发者CA 的公钥,然后对服务器发来的证书里面的签名进行解密。


综上,数字签名只能验证数据的完整性(JWT 只有服务端可以验证他的身份,因为它有解密需要的密钥,而客户端是验证不了的),而验证身份需要的是数字证书。


最后


以上是本人在学习数字签名原理的过程中的一些感悟,由于个人的局限性,所以可能存在纰漏的情况,欢迎大家批评指正。


作者:龙崎爱吃糖
链接:https://juejin.cn/post/7219304050667470903
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册