游戏协议加密及身份验证
时间如白驹过隙,一眨眼就过去了,本打算在 2 月份发这篇的,结果一直误以为 2 月有 31 日
这一块之前一直感兴趣,终于在 2 月份争取到机会去做这一块的工作。前辈给我留下了一个鉴权的框架:
- 客户端连接服务器
- 服务器将随机数发给客户端
- 客户端用客户端私钥加密,发回到服务器
- 服务器端用客户端公钥解开密文,比对解开的随机数是否与原来的一致
- 如果一致,则客户端发送随机数到服务器,服务器用服务器端私钥加密,将密文发送到客户端;同时,产生新的随机数,用于稍后对会话进行 rc4 加密,并将这个随机数以客户端公钥加密,发送到客户端。
- 客户端用服务器端公钥解开密钥,比对随机数和自己发的是否相同,相同的话则用客户端私钥解开下一个包,取出用于 rc4 加密的 key 密文
- 至此,双方身份验证完成,会话先进行压缩,再通过 rc4 算法进行加密
在这里,私钥充当了一个身份的象征,只要拥有私钥,就认为对方是授信客户端。可能有朋友会问,如果客户端被破解怎么办?我觉得安全专家道哥有个观点很好,安全的本质是信任的问题。设计任何安全方案,最终都必须要有一个东西是「假设可以信任的」,只是看这个「可信任」的东西被攻击成功的概率大小。如果不这么做,则做不出任何的安全方案。所以,考虑协议安全的时候,我们只能选择相信客户端密钥被安全的保管着。
回头再看看这个方案,这里用到了私钥进行加密。熟悉 ssh 的同学应该会联想到其挑战过程,ssh 服务器是用客户端公钥对随机数进行加密,再发送到客户端的。公钥加密和私钥加密的区别是,在明文相同的情况下,公钥加密出来的密文每次都不一样,而私钥加密出来的密文是每次都相同的。示例代码可以参见: https://github.com/spin6lock/rsa_encrypt_and_decrypt_in_c 密文和明文总是一一映射,容易被碰撞攻击,进而绕过加密过程。因此,私钥加密多用于身份验证,比如电子邮件的数字签名,首先用 md5 对邮件明文进行信息摘要,然后用私钥进行加密,公钥是公开的,保证任何看到这封邮件的人都可以进行解密,获取 md5 信息进行验证,确保邮件没有被纂改。
于是,我花了一周左右的时间修改了验证流程。新的验证流程如下:
- 客户端连接服务器
- 服务器用客户端公钥加密随机数,发给客户端
- 客户端用客户端私钥解密,发回服务器
- 服务器对比随机数,不一样的话就断开连接
- 客户端用服务器端公钥加密随机数,发送到服务器
- 服务器用私钥解密,发送回客户端
- 客户端验证通过
- 服务器用客户端公钥加密 rc4 会话所用密钥,发送到客户端
- 客户端用公钥解开,接下来在 rc4 加密下上传
接下来,想针对手机网络模块的工作特点进行协议优化。由于手机受限于电量,网络芯片工作时会有启动,半速,全速,半速,设备待机这个速度曲线,如果能够在应用层对包进行打包发送,能够大大提高手机的待机时间,参见这篇 开发指南 。协议压缩放在下一篇吧 ~