今天接手了一个Spring Boot的后端项目,里面主要是一些给移动端用的接口,整个项目的框架、包括前后端的交互格式之类的东西都已经由老大定义好了,我直接进行开发就行。

不像以前普通的网页端接口,前端直接把数据传到后端,后端业务处理完毕后再把数据返回,这次这个项目的所有接口都是经过加密处理的,好像既有AES对称加密算法、又有RSA非对称加密算法在里面,反正早上一开始老大给我讲项目架构的时候我是没听太懂。

到了中午吃饭的时候,我趁机缠着老大给我讲解了一下他接口加密的设计思想。




java中前后端项目某个数据进行签名和验签 前端接口签名_时间戳


接口为什么要加密?

我问老大:“我们APP需要先登录才能使用,后端接口也有做鉴权,接口加密不是多此一举吗?

老大摇了摇头说,APP调用的接口都是暴露在外的,别人一抓包就能拿到接口地址,再直接请求接口地址就能绕过我们的APP拿到数据,登录、鉴权只是最基本的措施,接口加密之后,这样就算别人抓包,看到的请求参数和返回数据都是加密后的密文,我们项目中使用AES对称加密算法来加密数据。

我似懂非懂的点点头,见我点头,老大反过来问我:“那你说说使用RSA非对称加密算法的目的是什么?

我只得摆出一副黑人问号脸……

老大白了我一眼说,使用RSA算法目的一是拿公钥加密AES的密钥,二是拿私钥对数据进行签名,可以起到防篡改、防抵赖的作用。

防篡改?防抵赖?听完我继续黑人问号脸……

老大说给你举个例子就明白了,孙杨知道吧?最近挺火那个,新闻上说他抗拒药检的原因是对药检人员的资质存在疑问,说白了就是他不相信药检工作人员的身份,那么这个双方的身份信任问题,其实就跟我们前后端接口通信需要首先确认对方身份一样,可以通过RSA算法来解决。

  1. 首先我们为世界反兴奋剂机构官方生成一对RSA密钥对,私钥由机构秘密持有,公钥将公布给孙杨在内的所有运动员,然后机构官方使用私钥为药检工作人员的资质证书进行签名(相当于盖上官方机构的章)
  2. 当药检工作人员需要对孙杨进行药检时,首先出示已经签名过的资质证书,此时孙杨可以拿机构官方公布的公钥对签名进行校验,如果签名校验通过则可以证明资质证书确实是由官方签发的,药检人员身份没有问题;如果签名校验失败则证明资质证书是伪造的,药检人员身份存在问题

我正要继续再问点什么,老大看着碗里的饭吞了吞口水对我说:“我饭都凉了,要不你先自己看看代码?”

我:……

好在经过我一下午努力,总算弄明白了整个接口加密通信的流程,下面我们一起来了解一下吧。

AES对称加密算法示意图

首先简单介绍一下AES对称加密,我画了一张AES加密算法的草图(大家凑合看吧):


java中前后端项目某个数据进行签名和验签 前端接口签名_加密算法_02


发送方使用密钥将明文进行AES加密,将密文和密钥传给接收方,接收方使用同样的密钥对密文进行AES解密,得到明文。

RSA非对称加密算法示意图

接着我再简单介绍一下RSA非对称加密,我又画了一张RSA加密算法的草图:


java中前后端项目某个数据进行签名和验签 前端接口签名_客户端_03


首先接收方生成一对RSA密钥(公钥、私钥),公钥公开给发送方,私钥自己保留;发送方使用公钥将明文加密得到密文,将密文发送给接收方,接收方使用私钥将密文解密得到明文。同样的,接收方也可以使用私钥对数据进行签名后回传给发送方,发送方可以使用公钥来验证签名,从而判断数据是否来自接收方。

AES+RSA配合实现接口加密,完整流程图

首先看一下从客户端请求开始,到服务端解密的一整套流程:


java中前后端项目某个数据进行签名和验签 前端接口签名_客户端_04


看起来还不错吧?下面我们一起来看看怎么实现的吧。正式开始前,首先介绍一下前期的准备工作:

  • 服务端生成RSA密钥对(公钥、私钥),公钥下发给所有客户端、私钥自己持有
  • 客户端生成RSA密钥对(公钥、私钥),公钥提交到服务端保存、私钥自己持有

客户端发送请求加密流程

首先介绍一下客户端发送一次请求完整的流程:


java中前后端项目某个数据进行签名和验签 前端接口签名_时间戳_05


  1. 随机生成一个固定长度的用于AES加密的密钥
  2. 使用服务端的RSA公钥对AES密钥明文进行加密生成AES密钥密文
  3. 使用AES密钥明文对参数明文进行AES加密生成参数密文
  4. 生成请求时间戳
  5. 生成固定长度的随机字符串
  6. 将参数密文、AES密钥密文、时间戳、随机字符串进行MD5计算,得到md5值
  7. 使用RSA私钥对md5值签名,得到签名值
  8. 最后将参数密文、AES密钥密文、时间戳、随机字符串、签名值一起发送到服务端

服务端解密流程:

再来看一下服务端接收到数据后的揭秘流程:


java中前后端项目某个数据进行签名和验签 前端接口签名_时间戳_06


  1. 比较请求时间戳与当前时间戳差值是否在系统设置的超时时间内,超过则认为请求过期
  2. 从缓存查找随机字符串是否已存在,如果已存在则认为是一次重复请求,不存在则将该随机字符串放入缓存
  3. 将参数密文、AES密钥密文、时间戳、随机字符串进行MD5计算,得到md5值
  4. 使用客户端RSA公钥验证签名是否来自合法授权的客户端,防止非法客户端篡改数据
  5. 使用自己的RSA私钥解密AES密钥密文,得到AES密钥明文
  6. 使用ES密钥明文解密参数密文得到参数明文
  7. 使用aesKey解密encryptParams得到明文参数
  8. 进行正常的业务处理流程
  9. 返回数据加密流程与客户端一致,直接使用客户端的AES密钥即可。

彩蛋

有眼尖的小伙伴有可能已经发现了,上面的例子中还夹杂了一些时间戳、随机字符串概念在里面,这些参数都有什么用呢?这些悬念就留到下一篇里揭晓吧。下一篇将会是实战篇,我会基于Spring Boot项目,通过自定义注解的方式来实现接口的自动加解密。

我是一名正儿八经的程序员,喜欢我的文章欢迎 转发 及 关注,我也会经常与大家分享工作当中的代码那些事儿。