Java核心技术卷II(第8版) – 读书笔记 – 第9章

1、安全机制是Java不可分割的一部分,主要从以下方面:

(1)语言设计特性(越界、类型、无指针等)
(2)访问控制(文件访问、网络访问)
(3)代码签名(用加密算法表明作者身份,代码是否被修改过)

2、类加载器将在加载时检查类是否完整,它与“安全管理器”协同工作。

3、Java编译器将.java文件编译成字节码.class文件。后者必须由解析器解释后才能执行。每个Java程序至少有三个类加载器:

引导类加载器:是JVM虚拟机的一部分,通常用C实现,他们没有ClassLoader,如String类。

扩展类加载器:位于jre/lib/ext中的jar包。这样即使没有设置ClassPath,也可以找到对应的类。

系统类加载器:设置在classpath中的类,也可以jvm –classpath中加入路径。

4、加载的时候是有顺序的,当使用系统加载器时,首先要使用扩展加载类,例如java.util.ArrayList,会首先要求扩展类加载器,失败才会到系统类加载器。

有时候我们需要的类已经打包在jar中,可以用URLClassLoader:

5、由于上述的顺序,如果在扩展加载类中含某类A,比如MySQL的驱动放在ext下了,我们Class.forname的时候,就会无视我们提供在classpath下的jar包,所以我们可以强制更改类加载器:

6、可以编写自己的类加载器:继承自ClassLoader,并覆盖方法findClass(String name)。这是有意义的:我们可以自己将.class进行加密,然后在ClassLoader中解密,这种思路可以用在付费软件上!

书上用了简单的凯撒密码加密class文件、在loadClass之前解密.class文件:

7、要编写自己的类加载器,只需要继承ClassLoader,并覆盖findClass(String className)方法。

比如我们要在加载之前实现解密,那么需要:
(1)读取本地class文件,并解密,得到bytes[](cbs)。
(2)调用defineClass()方法,尝试构造Class类:
Class<?> cls = defineClass(name, cbs, 0, cbs.length);

8、当类加载器将新加载的字节码传递给虚拟机之前,它们将被校验(verify)。

校验将负责检查那些明显错误或者具有破坏性的指令,例如:
(1)未初始化就使用的变量
(2)调用与引用类型不匹配
(3)访问私有数据
(4)堆栈溢出

9、际上,上述校验所做的工作,和编译器没有本质区别,但为什么还要这么做呢?

实际上,用Java自带编译器生成的.class文件肯定可以通过上述校验。但是在互联网这个开放环境内,你得class很可能被别人恶意修改,甚至植入后门。
如果你实在不想校验可以如下:

10、一旦通过了校验(verify),就会启动第二道安全机制:安全管理器。它负责控制某个操作是否允许执行,主要包括但不限于:

(1)创建一个新的类加载器
(2)退出虚拟机
(3)使用反射访问另一个类成员
(4)访问本地文件
(5)打开socket连接
(6)启动打印作业
(7)访问系统剪贴板
(8)访问AWT事件队列
(9)打开一个顶层窗口

11、默认的Java程序是不安装(额外)安全管理器的。配置自己指定的安全管理器(以及策略)时要非常小心,因为他可能影响程序的正常运行。安全管理器不是凌驾于系统权限之上的。它是在系统权限和程序员执行的程序之间,加了一道屏障。

12、允许在/tmp下读写文件:

此外,也可以配置策略文件(policy):

13、当安全管理器(SecurityManager)类需要检查某个权限时,它要查看当前位于调用堆栈上的所有方法的类,然后遍历上述所有保护类,让它们判断是否允许通行。如果所有都同意,检查通过,否则,抛出SecurityException异常。

14、策略文件可以放在两个位置下:

(1)JRE主目录的java.policy下
(2)~/.java.policy,windows不知道。。

如果程序要指定自己的策略文件,可以:

或者

上述情况是追加,即把xx.policy追加到其他策略上,我们还可以覆盖默认策略,用两个等号==即可:

15、一个策略文件包含若干的grant项,一个项的格式如下:

codesource可以是:

或者

permission部分是:

className需要写全,如: java.io.SocketPermission

目标target,具体可以见JDK文档或者书,以Socket的为例

16、Socket由主机和端口范围组成

主机形式有:
hostname或者ip address 具体某个机器
localhost/空 本机
*.domainsuffex 域名下所有机器
* 所有主机

端口形式:
:n 单一端口
:n- 大于n的端口
:-n 小于n的端口
:n1-n2 位于n1到n2之间端口

权限目标:accept, connect, listen, resolve等

一个例子:
permission java.net.SocketPermission “*.hostname.com:8000-8999”, “connect”;

17、权限定制,我们当然也可以自己定义权限类,需要:

(1)拓展自Permission类。
并提供带有两个String参数的构造函数:
(1)第一个参数是target,第二个是action
(2)implies(Permission other)决定是否依赖于其他权限

18、Java认证和授权服务(JAAS)是1.4及以上版本提供的服务,主要用于确定程序使用者的身份,并授权给用户权限。

JAAS是可插拔的设计API,可以与上面提到的SecurityManager结合使用。

19、Java内置了消息摘要算法:MD5和SHA-1

如果要复用,重头算,可以alg.reset()

20、Java内置了一些加密算法,可以用Cipher:

Cipher cipher = Cipher.getInstance(algoName); // algoName可以是AES/DES/CBC/PKCS5Padding

mode常用的有Cipher.DECRYPT_MODE和Cipher.ENCRYPT_MODE

然后update加密

一般这些padding都是需要按照块来操作的:
cipher.getBlockSize()获取每一块的大小,

21、像AES这种算法,如果Random随机生成密钥,会非常危险,我们可以用KeyGenerator完成。

一个完整的生成AES密钥、AES加密的例子如下:

22、上面这种还会比较复杂,特别是需要Padding的时候很麻烦。

JDK提供了CipherInputStream和CipherOutputStream,用于对数据进行加密,它会透明地调用update()和final(),自动处理块的问题,非常方便。
下面是一个例子,由于流可以和其他各种包装起来用,瞬间就很简单了:

23、Java也内置了非对称密码(公钥) 算法。但是一般来说,我们不会用RSA加密大段文本,而是用RSA的公钥加密对称密钥(如一个AES密钥),然后用户持有私钥,先解密出AES密码,再解密正文。

如下是用RSA包裹AES密钥的方法

24、下面是从RSA加密中恢复AES密钥:

解密类似,只不过用的是unwrap()方法:

本章结束。

Leave a Reply

Your email address will not be published.