apk数字签名实践

"之深入探索"

Posted by is Q on May 11, 2019

apk数字签名实践之深入探索

前言

这段时间有一个对web app进行签名验证的一个需求,为了确保所下载的apk包来自于我们所信任的服务器,防止请求被劫持或者替换,由于暂时还是使用http协议在上https之前有一个过渡期,所以不得不考虑传输安全,毕竟http就是在整个网络上”裸奔”。

数字签名

做这个之前我们首先得了解数字签名到底是什么,在做这个验签之前我也一直是处于听说过的状态中,所以做这个任务之前花了一点时间去了解一下。简单说数字签名只做一件事情,就是证明这个内容一定来自于某人,为什么呢?因为使用私钥进行签名,因此只有他自己能生成自己的签名(私钥被盗不算)。

说这个数字签名之前我们首先简单提一下密码学的知识,加密方法大致分为两类:对称加密(加密和解密都使用一套密钥)、非对称加密(有两套密钥,一套私钥、一套公钥,私钥只有自己知道,公钥可以给其他人;一般发送方用公钥加密发送消息,而只有自己能够用私钥进行解密,以此达到安全传输消息的目的;而签名一般使用私钥,用公钥进行验证签名)。但是我在实现我的过程中发现了一个问题,网上大部分讲解数字签名的文章好像和真正的签名算法有所出入,最后在前组长以及查看《密码编码学与网络安全》数字签名这一章才让我有点清晰。

网上的资料关于数字签名的流程主要为:服务器将内容进行hash得到内容的摘要,然后使用私钥加上摘要完成签名,然后将内容以及签名一起发给接收方,接收方收到之后使用服务器的公钥进行解密得到摘要一,然后再使用和服务器一样的hash算法对内容进行hash得到摘要二,然后对比两个摘要是否相等,如果相等就说明签名通过,不相等就验签失败。

实际运用并不顺利

带着认识开始做签名任务,发现在和后台对接的时候有点小差别,我理解的是要解密对比摘要,很后台说的验签有点出入(内容hash+公钥+签名代入签名验证算法)得出结果?最后查了一下资料发现数字签名的一般模型为:

数字签名基本模型

根据这个模型来看,第一部分理解都是没有问题的,但是问题就是处在验签过程,图上是直接使用使用内容hash+签名代入签名验证算法得出结果,和后台理解的完美贴合。看到这里我就更加疑惑了,难道网上的资料都有问题?而且维基给出来的数字签名原理和我以前理解的一样,如下图:

维基数字签名原理

第一部分和上图的过程一摸一样,唯一的区别就是验签过程。出于找到原因的目的继续在《密码编码学与网络安全》这本书中寻找原因,最终找到了影子:

两种签名方法

RSA方法:hash函数的输入是要签名的消息,输出是定长的hash码(前H),用发送发的私钥(PRa)将该Hash码加密形成签名,然后发送消息及其签名。接收方收到消息后计算hash码(后H),并且使用发送方的公钥对签名解密,如果计算出的Hash码与解密出结果相同,则认为签名是有效的。因为只有发送方拥有私钥,所以只有发送方能够产生有效的私钥。 DSA方法:DSA采用hash函数,得到hash码,它以hash码和为此次签名产生的随机数(k)作为签名函数的输入,签名函数依赖于发送发的私钥(PRa)和一组参数,这一组参数为通信伙伴共有,可以认为这组参数构成全局公钥(PUG)。接受方,对接收到的消息产生hash,这个hash码和签名一起作为验证函数的输入,验证函数依赖于全局公钥和发送方公钥(PUa)。若验证函数的输出等于r成分则签名有效。

最后我的理解是:数字签名的模型是上图13.1这样的,但是在实现方式上是可以调整,因此网上的大部分资料应该都是相对于某一种签名方式来说的,而不是对签名过程进行分析。

apk签名实践

这里我只说前端方向,在这个任务中我采用的就是一般模型的基本流程,先生成一套RSA公私钥,前端直接将公钥硬编码。后端读取apk(buffer读取)并将内容进行hash(前后端协商使用sha256),依赖发送方的私钥将hash码代入签名函数,得到签名;把apk和签名一起发给前端,前端首先下载apk,然后读取apk文件(前端读取可以文本形式、dataurl形式、ArrayBuffer形式),前后端读取形式不同验签一定失败,由于后台使用的是buffer读取,因此前端也只能采用buffer读取。⚠️(crypto-js中sha256不支持原生的ArrayBuffer,最后选择js-sha256,签名验证库:jsencrypt,除了文档不太友好之外都挺不错,自带一个测试demo)。文件读取方式统不统一就决定了此次签名能不能成功。

以上都是个人理解。