昨天的程序和例子,是来自于javax.net.ssl包中的核心类,这个包是JSSE的关于SSL通讯层次的扩展包。掌握这些核心类,对研究Tomcat的SSL功能是至关重要的。
首先来看一下这个包中整体的类图:

逐个分析一下上述的这些核心类:
1.SSLContext
SSL链接的上下文,说白了也就是通过这个SSLContext可以拿到整个包中其它的重要的部分。
a. SSLContext可以根据具体的protocol直接拿到其JSSE的SSLContext具体的实现

b.这个SSLContext可以创建SSLEngine,可以拿到客户端Socket,和服务器端Socket的工厂,上述的两种方式是SSL通讯方式,JSSE包中就支持的两种方式。

c. SSLContext一旦实例化之后,可以获得SSLParameter(SSL的参数),SSLSession(通过SSLContext的客户端和服务端的getClientSessionContext, getServerSessionContext,然后再通过SSLSessionContext获得Session),提供者(getProvider),协议(getProtocol)等多个属性。
可以总结出来,SSLContext就是这个包中灵魂,一切的源头的所在。
2.SSLServerSocketFactory/SSLSocketFactory
SSL通讯方式的第一种,也是最常用的一种,通过Socket进行通讯:

SSLServerSocketFactory是服务器端工厂,SSLSocketFactory是客户端工厂,二者都是创建Socket的。
3.SSLServerSocket/SSLSocket
由SSLServerSocketFactory和SSLSocketFactory创建出来的Socket,这两个Socket主要用于进行SSL的socket通讯。

可以这么理解,虽然SSLSocket的SSL交互方式比普通的TCP/IP的网络通信的Socket复杂一些,但是这些SSL交互过程都是隐藏在SSLSocket之下了,我们在编程的层级上不会去为前面的握手协议,数据传输协议而担忧。
4.SSLEngine
通过SSLEngine这种方式进行SSL通讯,这是java.net.ssl包的第二种SSL通讯方式(前面一种为通过SSLSocket进行通讯)。
SSLEngine是模拟出站和入站,首先可以看一下这张图:

上图中下面是netdata,也就是SSL的网络包,上面是appData,也就是应用中使用的数据流,
而SSLEngine的作用就是通过wrap和unwrap两个方法来将应用的数据流出站为SSL网络流,或者是SSL网络流入站为应用的数据流。
根据已初始化的 SSLContext 来调用 SSLContext.createSSLEngine() 即可创建 SSLEngine。在第一次调用 wrap()、 unwrap() 或 beginHandshake() 之前应该设置所有的配置参数。这些方法都能触发初始握手。
SSLEngine 有五种不同的阶段。
创建 - 已经创建和初始化 SSLEngine,但尚未被使用。在此阶段,应用程序可以设置任何特定于 SSLEngine 的设置(启用密码套件、SSLEngine 应该以客户端还是服务器模式握手等等)。一旦握手开始,任何新的设置(除了客户端/服务器模式,见下文)将在下一次握手时才被使用。
初始握手 - 初始握手是两个同位体交换通信参数,直到建立 SSLSession 为止的过程。在此阶段不能发送应用程序数据。
应用程序数据 - 一旦通信参数建立起来且握手完成,就可以通过 SSLEngine 传输应用程序数据。出站的应用程序报文被加密并进行完整性保护,入站的报文进行相反的过程。
重新握手 - 在应用程序数据阶段的任何时间,每一方都可以请求重新协商会话。新的握手数据可以混入到应用程序数据中。在开始重新握手阶段之前,应用程序可以重置 SSL/TLS 通信参数,例如已启用的密码套件列表和是否使用客户端验证,但是不能更改客户端/服务器模式。如前所述,一旦握手开始,任何新的 SSLEngine 配置设置都将在下一次握手时才被使用。
关闭 - 当不再需要连接时,应用程序应该关闭 SSLEngine,并且也应该在关闭底层传输机制之前发送/接收所有剩余的报文到同位体。一旦引擎关闭,它就是不可重用的:必须创建一个新的 SSLEngine。
5.SSLEngineResult
通过SSLEngine来进行出站和入站的操作,也就是unwrap和wrap的返回值,就是这个SSLEngineResult,我们来看看程序:

上述的这几个状态是什么意思?
这个定义在SSLEngineResult.Status中的枚举对象中:

因为SSLEngine从前面出站和入站的操作可以看出,是与字节和缓冲区相关的,因此这里肯定有对于缓冲区的扩容等进行的操作,或者是源字节不够。
6.SSLParameter
SSL相关的参数,说白了这个就是SSL配置的一些参数,例如:
a.支持的加密解密算法列表

b.支持的协议数组

c.客户端是否需要验证

等等,这些属性基本上和Tomcat的server.xml中的连接器的SSL配置类似。
由这些属性构成了一个SSLParameter:

这个SSLParameter是由SSLContext上下文实现来决定的,可以看到下面的两个方法:


都是从SSLContext上下文实现中返回默认的SSLParameter支持属性的。
7.SSLSessionContext
从SSLContext的上下文实现,可以返回SSLSessionContext,这个SSLSessionContext叫做会话的上下文。
也就是每次一个客户端和SSL服务器端产生一个SSLSession,这些Session都与这个SSLSessionContext形成关联。
每一个SSLSession都有一个SSLSessionID,在SSLSessionContext会有一个缓存:

遍历上面的枚举对象,对于每一个SessionId,SSLSessionContext可以通过这个SessionId拿到SSLSession:

其次,对于SSLSessionContext可以设置和拿到SSLSession的每个对象的缓存大小和超时限制,

上述的两个属性,在SSLSessionContext中是整体设置的,在每一个SSLSession中不可以进行修改。
8.SSLSession
对于每一个SSL的会话,都与一个SSLSession相对应,你写过Servlet,可以将这个SSLSession和HttpSession对应起来来理解,二者实际上是差不多的。
每一个SSLSession都有一个ID:

其次,你可以和HttpSession一样,将HttpSession当做一个Map来进行存储一些值,并且在会话中可以进行获取:

当然,你也可以拿到一串的绑定到此SSLSession中的名字列表:

可以让SSLSession失效:

这个SSLSession可以获得与当前会话关联的SSL各种属性和内容:

如上面的证书,证书链,端口,主机名,SSL协议,缓冲区大小,权限等等。
9.java.security.KeyStore
Keystore类是java中的秘钥库对应的keystore文件的模拟,这个类不在这个javax.net.ssl包中,但是这里一定需要进行介绍:

可以通过Keystore来获得对应JKS(或者是P12类型)的实例。
然后通过load方法,来与真正的keystore文件进行关联。
10.KeyManagerFactory/TrustManagerFactory
这两个秘钥管理器的工厂,前面看过Tomcat的配置属性就可以了解,一个是服务器端秘钥管理,一个是存放客户端秘钥的管理。
这两个秘钥管理器工厂的初始化,是需要使用Keystore来进行初始化的:

首先,无论是哪一个工厂,对于SUN的JDK,都是“SUNX509”,这个在Tomcat的配置中也可以配置,与对应的JDK有关。
其次,无论是服务端的keystore还是存放客户端的trustStore,都是与Keystore类关联,因为毕竟都是秘钥库类型。
最后,将这这两个工厂进行初始化,最终是通过KeyManagerFactory或者TrustManagerFactory获得KeyManager和TrustManager:

11.KeyManager/TrustManager
前面一直提到的keyManager或者TrustManager,这两个秘钥管理器的作用是在SSL的交互的过程中用来验证证书中的秘钥内容的工具,也就是说,当SSL握手协议的时候,第一步会与本地的Keystore文件中的证书进行匹配,看是否客户端传递的证书是Keystore文件中的一个条目,如果是的话,最终得出验证结论的,证书同构。
以Keymanager为例:

因为目前的证书格式,大多数都是X.509格式,所以在javax.net.ssl包中也提供了X509实例化的两个秘钥管理器

可以分析X509KeyManager中的内容,提供了一系列的方法,在SSL的交互过程中,调用这些方法,获得对应的秘钥或者公钥,或者验证客户端的信息:

12.HttpsURLConnection
这个类是javax.net.ssl包中提供的https的一个方便的客户端:

最重要的方法就是其构造函数,传递一个URL:

然后,就可以获得各种交互的内容了:

13.SSLPermission
SSL的权限信息:

用于安全。




