夜已深,注意休息哦!

编程开发

前后端分离项目接口数据加密的秘钥交换逻辑(RSA、AES)

2020年03月25日 12:01:54 · 本文共 1,524 字阅读时间约 5分钟 · 12,683 次浏览
前后端分离项目接口数据加密的秘钥交换逻辑(RSA、AES)

在前后端分离的项目中,往往需要传输一些敏感的信息,例如密码、金额等,签名验签算法只能保证数据不被篡改,但是却无法对数据进行保密,如果用户输入的密码明文传输,就会被网络中的节点截获,虽然大部分网络运营商并不会去截取网络传输的内容,但是不能排除用户连接的WiFi网络是不是钓鱼网络,所以在传输敏感信息的时候需要加密,本文就讨论如何安全的让客户端和服务器交换秘钥。

加密算法的选择,RSA还是AES

涉及到加解密就涉及到加解密算法的选择,比较常见的是非对称加密RSA和对称加密AES,它们各有什么特点和选择呢?我们逐个说明

RSA非对称加密

RSA算法会生成两个秘钥,一个公钥,一个私钥,使用公钥加密的数据必须使用私钥解密,这样的好处是只有服务器有私钥,其他人无法解密。但是他有个非常致命的弱点,那就是加密内容的长度不能大于秘钥长度,以RSA 1024为例,PKCS#1建议的padding占用了11个字节,这样,128字节(1024bits)-减去11字节是117字节,也就是说我们使用RSA 1024最多只能加密117字节的内容,如果超过这个内容就会报错,可往往我们要传输的内容非常庞大,我们也不能无限制的增加秘钥长度。而且在移动设备上性能有限,并不适合大量加解密运算。所以我们还需AES。

AES对称加密

因为上述的RSA受到秘钥长度的加密限制,我们还需要AES加密,AES的优点是无论明文有多大它都可以加解密。但是缺点是它不区分私钥和公钥,密钥只有一个,任何知道秘钥的人都可以加解密信息。

RSA和AES结合使用取长补短

RSA和AES都有各自的优缺点,如果我们把他们结合使用就可以取长补短,我们可以使用RSA来保护AES的秘钥,这样可以保证只有双方知道AES的秘钥,并且AES可以加密任何长度的信息,这样就解决了它们各自的缺点。


秘钥交换逻辑

一、客户端向服务器端申请RSA公钥;服务器生成一个RSA秘钥对,将公钥发送给客户端。我们起名为「Server公钥」

二、客户端收到服务器给出的「Server公钥」,客户端生成自己的RSA秘钥对,我们起名为「Client公钥」,客户端使用「Server公钥」去加密自己的「Client公钥」,发送给服务器

三、服务器收到客户端的请求,使用「Server私钥」解密得到「Client公钥」

四、服务器生成一个「AES秘钥」,使用「Client公钥」加密「AES秘钥」发送给客户端

五、客户端使用「Client私钥」解密得到「AES秘钥」

后续双方的交互使用「AES秘钥」进行加解密,秘钥交换流程完成。

秘钥交换逻辑

实例代码:

@Test
public void reaTest() {
    //服务器端的
    Map<Integer, String> serverKeyMap = RSAUtils.genKeyPair(2048);
    //客户端的
    Map<Integer, String> clientKeyMap = RSAUtils.genKeyPair(1024);
    String clientPubKey = clientKeyMap.get(0);
    try {
        //客户端使用服务器公钥加密自己的公钥
        String encrypt = RSAUtils.encrypt(clientPubKey, serverKeyMap.get(0));
        //服务器端使用自己的私钥解密拿到客户端公钥
        Assert.assertEquals(clientPubKey, RSAUtils.decrypt(encrypt, serverKeyMap.get(1)));
        String AESkey = "aeskey123456";
        //使用客户端的公钥加密AES的秘钥
        String encryptAES = RSAUtils.encrypt(AESkey, clientPubKey);
        //客户端用自己的私钥解密拿到AES的秘钥
        Assert.assertEquals(AESkey, RSAUtils.decrypt(encryptAES, clientKeyMap.get(1)));
    } catch (Exception ex) {
        ex.printStackTrace();
        Assert.assertNull(ex);
    }
}
商业用途请联系作者获得授权。
版权声明:本文为博主「任霏」原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.renfei.net/posts/1003346
评论与留言

以下内容均由网友提交发布,版权与真实性无法查证,请自行辨别。

好的谢谢解答

请问在上面的这个流程中,如果在第一步客户端向浏览器请求公钥因为是走明文的,如果这时候服务端返回的公钥被中间人替换掉了,中间人把自己的公钥给到前端,这样后续的请求是不是就就不再安全了?这样的话这个流程是不是可以认为不算是安全的呢?

renfei 站点官方 回复

关于中间人攻击,就需要引入第三方来解决信任问题,https 的 ssl 证书就可以,比如我请求www.renfei.net,你说你(中间人)是www.renfei.net,那你就出具 ssl 证书证明你是,如果证书对不上,那我就不信任你,不会继续交换秘钥了。申请 ssl 证书的时候会审核域名所有权,不是随便就能申请的,所以假冒的服务器端(中间人)是无法出具证书证明自己是www.renfei.net的。

上面说的这个流程里面貌似没有引入第三方验证哦,然后你说的这个流程不是就https已经实现的流程吗?

renfei 站点官方 回复

感谢您的补充。是这样的,这篇文章我最初本来是解决等保测评中密码明文传输的问题,所以没写https,后来没想到引来大家的热评,在文章开头我又补充了关于使用https解决中间人的建议https://www.renfei.net/posts/1003373。在 https 基础上再套自己的加密是因为,如果设备在自己手里也是可以抓包解密修改的,我们有个使用场景是使用我们的APP打卡,类似钉钉,如果不自己加密的话,是可以被破解任意伪造打卡数据的,当然不一定按照我的文章,可能是APP内置证书的,所有软件都有破解的办法,我们这样做是增加破解的难度和成本,让破解的人放弃破解。

请问客户端和服务端之间如果全程都是https的话,那还还有必要对信息进行加密吗?

renfei 站点官方 回复

我觉得还是有必要的,首先明确一点https也是可以被抓包解密的,前提是拿到设备。分享一次我的事迹,我曾经抓包支付宝APP,想改数据包里的内容,虽然是https,依然能抓取并拿到里面的内容,但是支付宝在传输的时候还是加密了,也就是说 https 里面还是加密的内容,这就造成我无法修改数据包,除非我反编译支付宝的APP,拿到加密算法,再伪造APP请求。

请问在上面的过程中,aes秘钥只生成一次,还是一个用户一次?

任霏 站点官方 回复

您好,aes是按照客户端请求生成的,跟用户没关系,也就是说一个客户端就生成一个,只要有秘钥交换请求的流程,就会生成一个。如果一个客户端上面登陆了多个用户,那么是可以共用一个aes秘钥的,这个只是用来端到端加密交互的,并不用于身份识别,所以跟用户没关系。

打算

明白了,写的好!

您好,对于秘钥交换逻辑有个问题,为何我前端还要在生成一次RSA秘钥给到后端公钥,然后由后端生成AES对称秘钥给前端呢,如果是前端生成AES对象秘钥然后通过后端的公钥加密给到后端,这样会有什么风险吗? 一、客户端向服务器端申请RSA公钥;服务器生成一个RSA秘钥对,将公钥发送给客户端。我们起名为「Server公钥」 二、客户端收到服务器给出的「Server公钥」,客户端生成一个「AES秘钥」,客户端使用「Server公钥」去加密自己的「AES秘钥」,发送给服务器 三、服务器客户端使用「Server私钥」解密客户端发送的「AES秘钥」进行存储

任霏 站点官方 回复

您的逻辑上是没问题的,我是根据行业习惯来的,不信任客户端产生的东西,或者说接口传进来的我都不信任,如果由客户端生成AES的话,后端还需要判断这个秘钥的强度,增加了后端的工作量,不如直接不信任客户端,都由后端决定用什么密码,什么时候下发新的密码

麻烦问一下,前后端分离的情况下,ras的私钥怎么进行存储呢,因为是每个用户会生成每个人自己的私钥,那么在服务器端如何进行存储呢

任霏 站点官方 回复

您是说服务器端生成的RSA私钥吗,服务器在生成RSA秘钥对的时候同时产生一个KeyID,保存在数据库中,向客户端发送「公钥」的时候会告知客户端秘钥的KeyID;客户端下次发送加密后的内容时需要带上KeyID,这样服务器就知道客户端目前使用的是哪套秘钥对了,同时你也可以在服务器端存储的时候给秘钥对设置有效期,比如7天以后这套秘钥对作废。

感谢您的回复,目前我采取的就是这种方式,不过会不会存在截获到KeyID,导致可以进行模拟数据提交和攻击了

任霏 站点官方 回复

KeyID并不是敏感信息,被截获以后也干不了什么。使用KeyID的场景是:客户端自己生成了一套RSA秘钥对,使用服务器的「公钥」加密客户端的「公钥」然后上报给服务器。也就是上报客户端的「公钥」时候使用的。骇客截获到KeyID和密文以后能做什么呢?首先密文需要服务器的「私钥」才能解密所以没用;KeyID只是标识我在使用哪套服务器秘钥对而已,即使模拟提交,骇客和服务器成功交换了秘钥,可是后面的业务逻辑还是要用户密码的啊。如果您说的是骇客作为中间人转发客户端的请求,那用HTTPS就可以解决,因为SSL证书是要验证域名的

十分感谢!

客户端与服务端的RSA公私钥每次访问都要动态生成吗?

任霏 站点官方 回复

只执行一次,客户端就会把秘钥保存下来。调用加密函数->加密函数检查本地AES秘钥->存在就直接使用,不存在就跑这个秘钥交换流程。每次都执行的话性能就太差了

五、客户端使用「Client私钥」解密得到「AES秘钥」这一步,「Client私钥」不会泄露吗?

任霏 站点官方 回复

首先,「Client私钥」是客户端自己生成的,保存在客户端,不会向网络发送,服务端也不知道「Client私钥」是什么,如果存储到客户端都算泄露,那物理设备已经被攻陷,我作为软件服务提供商也没有办法啊。其次,「Client私钥」是为每一个客户端生成的,每一个设备的秘钥都不同,就算被黑客拿到,也只能解密这一个客户端的内容而已。

微信搜一搜:任霏博客