Java安全模型很重要的一点就是它能支持认证。所谓认证就是利用数字签名对.class文件进行担保,保证.class文件不是恶意的。
首先Java需要利用keytool命令生成签名所需的公钥/私钥对,然后就可以进行签名了。上文说“对.class文件进行担保”,实际上Java是通过对.jar文件签名类进行担保的:
- 生成jar文件。
- 把组成这个jar文件内容的字节流作为输入,输入到一个hash函数中,生成一个固定的散列值。
- 对这个散列值进行签名。
- 把签名后的散列值加入到jar文件尾部。注意,jar文件本身不需要加密。
- 最后,接受jar文件的人也要利用jar文件计算散列值,这样可以利用公钥验证签名的正确性了。
这个流程如图1-1所示:
图1-1 签名流程
那么这里Java是如何保证.class文件的正确性呢?
首先说明一下hash函数的作用:保证class文件没有被修改过,因为最后是要比较散列值与解密后的散列值是否相等。如果class文件在传输过程中被黑客替换了,那么散列值就不可能与解密后的散列值相等,最后的验证也就不会通过。
如果黑客能够把恶意class文件作为输入,利用hash函数产生一个与原class文件相同的散列值,那么这样就不会出现上面不等的情况。但是hash函数是很难(十分十分十分十分十分难)从不同的输入中产生相同的散列值,所以这样做也不可行。
此外,黑客还可以盗取私钥来伪造签名。这就不是Java能管的事了,只能祈求用户或者组织有很强的防护机制保证私钥不丢失。
最后再说一下关于签名的问题。私钥和公钥是一一对应的关系,一个好的签名方案中私钥签名的信息只能通过对应的公钥验证,不能利用别的公钥验证。现在假设Alice的私钥/公钥对为a/A。首先Alice公开公钥A,接着Alice用私钥a对jar文件签名,然后想要让Bob接收这个jar文件。为了安全Bob要用公钥A对jar文件进行验证,如果验证通过则接收。如图1-2所示:
图1-2 正常流程
有一个黑客叫做Eve,他自己也有私钥/公钥对e/E。在某些情况下他能利用某些手段替换公钥A变为自己的公钥E(比如公钥A需要通过网站下载,那么Eve可以很容易的替换公钥A)。此时Bob并不知道,还以为公钥A是公钥A,其实公钥A已经变成公钥E了。然后Eve发送用私钥e签过名的恶意jar文件给Bob,Bob验证通过,接收了这个文件,运行,结果银行存款都被偷走了(或者其他什么恶意行为)。如图1-3所示。
图1-3 Eve替换公钥
如何防止这种行为?可以让一个可信第三方机构,对Alice的公钥进行签名,得到一个证书(实际上也是一个签名),Alice把证书和公钥发给Bob,Bob利用证书的公钥进行验证,验证通过,则证明这确实是Alice的公钥。