对于网络安全的知识比较匮乏,最近在读《深入JAVA虚拟机》一书,讲到了JAVA虚拟机的安全控制问题,想起本科的时候做过一个网络硬盘项目,其中纠结讨论的就是一个验证客户端和服务端以防止假客户端或假服务端欺骗的问题,一并总结了放在这里。
一、C/S结构网络应用验证
如果是广域网的C/S应用,比如QQ之类的,在进行通信之前,需要有一个彼此验证身份的过程。我们假设用户通过http协议发送的信息是可能被抓包软件捕获的,这样就不能明文传输用户密码等信息,怎样建立一个链接方式假冒的客户端或假冒的服务端骗取用户信息呢?当初我们使用了如下的协议方式:
使用语言可以将上述过程描述如下:
1.客户端接受用户输入的用户登录用的Client ID和密码,然后将Client ID发送给服务器端。
2.服务器端收到后从数据库中查出对应的Client ID,验证其存在,并用数据库中对应的密码(用户设置的密码经过MD5加密的字符串)采用AES加密算法对一个随机产生的通信加密密码 和ID进行加密后生成字符串,连同没有加密的ID一同发回给客户端。
3.客户端确认ID是自己的,使用用户输入的密码,采用MD5加密方法生成解密字符串,对传输过来的加密字段进行解密后获得下一步传输所用的加密密钥,并验证加密后的ID是否正确,即服务端是否有误。进而使用解密后的密码对ID再次进行加密,传输给服务器端。
4. 服务器端接收使用上次发送时产生的随机密码加密的用户ID,如果解密正确后,则确认客户端有效,正常的通信才能够真正开始。
这里利用了MD5加密的时候理论上无法逆向生成明文的特性,在第三步验证服务器端的真实性,即默认服务器端才可以保存有用户设置的密码,第四步服务器端会通过对于用户ID的解密验证客户端不是假冒的,即即使一个假冒客户端知道有效的client ID,没有正确的密码也无法解密下次通讯需要的密码。这样,无需在两端传输密码信息,避免假冒客户端和假冒服务器的问题,基本保证系统的安全性。
二、JVM中的代码签名和认证机制
在JAVA语言设计的时候就考虑到要对JAVA编译后的二进制文件进行网络传输,因此基于公钥/私钥机制设计了代码签名和认证的机制,用于保证二进制文件在网络传输过程中不被替换或者修改。对一个jar文件进行数字签名的过程如下图所示:
1.首先,要对未加密的class文件和数据文件进行一个单向散列的过程,形成一个散列值。这个散列值的位数越多,就越能保证安全,因为由不同文件产生相同散列的可能性就越小。
2.第二步是使用发布这个jar包的人的私钥对上一步所得到的散列值进行签名,即加密。对于散列值进行加密比对数据文件进行加密速度要快,而只要当冒充者拥有你的私钥时,才能同时替换这原未加密文件和签名之后的散列值。
3.第三步是将上面的两部分组合到jar文件中去,就可以用于传输了。
在接收到该jar文件的使用者进行验证的过程如下图所示:
1. 取出未加密的文件进行散列计算,得出散列值。
2. 取出签名后的散列值,使用作者公布的公钥进行解密(任何使用私钥加密的东西都可以用公钥解密),获得计算后的散列值。
3. 比较两个散列值是否正确,如果正确就说明签名验证通过,jar包没有被修改,否则验证失败,jar包不可信,不予以执行。
通过这样的简单散列和加密的过程,就可以保证jar包的可信度。这里面仍然有很多问题,比如公钥获取是否可信?如果公钥本来就是假的,或者jar包的生产者本来就是个意欲进行破坏的人,这就无法保证安全性了。至于公钥的问题可以使用中立的安全机构进行同样方式的签名予以保证,这就是所谓的“证书”(塞班系统的软件签名也是这个样子的吧……)。而密码学解决不了所有的问题,只能从形式上证明所设计或使用的协议正确性。
JAVA虚拟机的安全机制还有很多方面,比如使用类装载器进行防治替换等等,以后有机会再进行总结和分析。