公钥加密私钥解密是密送,保证消息即使公开也只有私钥持有者能读懂。
私钥加密公钥解密是签名,保证消息来源是私钥持有者。
数字签名只能验证数据的完整性,数据本身是否加密不属于数字签名的控制范围
CS
C客户端,S服务器端
在客户端软件发布前,客户端保存一个公钥,服务器保存一个私钥;客服端and服务器都保存一个固态key : solid_key
步骤1:
客户端随机生成一个对称密钥,称为动态key : dynamic_key,使用公钥加密内容(dynamic_key+账户+密码+其他需要的参数)。发送给服务器
步骤2:
服务器收到后使用私钥解密,并验证用户和密码是否正确,正确的话保存此dynamic_key在用户的信息中(如果原先已经有了dynamic_key就替换掉旧的); 返回数据:包括客户端登录成功的信息,返回一个Token(令牌,以后见牌如见人)和其他需要返回的参数(传输过程中不携带uid,用token标识用户唯一身份,更安全)。
步骤3:
使用本地保存的dynamic_key解密得到token和其他参数。 安全传输准备工作做完了。
步骤4:
每次传输数据对于加密的内容一律使用dynamic_key进行加密,按照协商好的签名规则进行签名signature(需要dynamic_key参与到签名规则中,以保障安全),并附以明文参数token、明文参数signature、密文参数params这三部分,然后把此内容发送给服务器。
signature = sha1(rawData + dynamic_key),服务器利用用户对应的 dynamic_key 使用相同的算法计算出签名 signature2 ,比对 signature 与 signature2 即可校验数据的完整性。 (貌似在数据全部加密的情况下,不需要签名的)
步骤5:
根据token找到保存的dynamic_key,然后进行解密(dynamic_key仅仅用来解密,对于解密的内容无法验证),然后根据签名规则验证签名;查看token是否匹配,如果匹配的话那么此次请求就是有效的,否则无效,需要重新登录;如果验证签名失败,直接返回错误。
更新公钥通知服务
服务器端首先需要用原来的私钥签名,客服端有原来的公钥验证签名,如果签名通过,客服端方可更新公钥;签名不通过,需要重新下载最新客户端安装包(包含新的公钥);
如果被网络截获,还需要知道固态key,才能够修改客户端本地公钥
优点:
安全性,除非入侵服务器拿到私钥,或者破解久经考验的非对称加解密算法,没有其他办法大规模攻破,相比其他单纯使用对称加密和非对称加密方法效果都要好。
缺点:
用户第一次握手验证时使用非对称加密的内容,服务器非对称解密消耗资源比较多,如果攻击者发送大量此类信息,容易导致服务器DDoS攻击。个人觉得暂时无法从根本上避免这个问题的发生,只能够加上一些措施增加其难度,从而过滤掉一部分攻击者,具体措施有很多种,比如自己软件写个加密算法,服务器会根据解密算法进行计算从而验证是否有效,再比如android的话把这些算法写在jni层,增加反编译破解的难度。
其他
下表解决“根据token获取对应用户uid”在redis中如果做持久存储的话,token失效无法被有效清除,导致占用内存的风险;
CREATE TABLE `lee_user_token` (
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户UID',
`token` char(36) NOT NULL COMMENT 'uuid 生产的36位字符串',
PRIMARY KEY (`uid`),
UNIQUE KEY `i_token` (`token`,`uid`) USING BTREE
) ENGINE=MEMORY DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='这里只是存储 token 与 uid 的映射关系,不做登录过期有效性判断';
参考信息:
- http://segmentfault.com/q/1010000000156701
- http://baike.baidu.com/view/356572.htm
- http://baike.baidu.com/view/1982056.htm#2
- http://www.dewen.org/q/2005
- https://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html