Java平台安全机制与JSSE详解
1. Java GSS - API安全上下文
在Java中,若两个应用程序要使用GSS - API安全地交换消息,它们必须先建立一个联合安全上下文。
org.ietf.jgss.GSSContext
接口封装了GSS - API安全上下文,并提供可用的安全服务。两个应用程序通过创建和使用
GSSContext
对象来建立和维护构成安全上下文的共享信息。
1.1 上下文建立前的准备
若使用默认的
GSSManager
实例来实例化
GSSContext
,则可保证使用Kerberos V5 GSS - API机制进行上下文建立。该机制由Oid “1.2.840.113554.1.2.2” 标识,在RFC 1964中定义。
在上下文建立阶段开始前,上下文发起者可以请求建立的上下文具有特定的特性,示例代码如下:
// Instantiate and initialize a security con
// established with the server
GSSContext context = manager.createContext(serverName,
krb5Mechanism, userCreds, GSSContext.DEFAULT_LIFETIME);
// t desired context options prior to context
// tablishment
context.requestConf(true);
context.requestMutualAuth(true);
context.requestReplayDet(true);
context.requestSequenceDet(true);
不过,并非所有底层机制都支持调用者可能期望的所有特性。上下文建立后,调用者可以使用各种查询方法检查该上下文提供的特性和服务。
1.2 上下文建立过程
上下文建立是在一个循环中进行的,发起者(如客户端)调用
initSecContext
,接受者(如服务器)调用
acceptSecContext
,直到上下文建立完成。在上下文建立阶段,可以调用
isProtReady
方法来确定上下文是否可用于尚未完全建立的逐消息操作。当
isProtReady
方法返回
true
时,可以使用查询例程来确定已建立上下文的特性和服务。
1.3 消息安全
MessageProp
实用类用于在逐消息操作中传达逐消息属性。在使用
GSSContext
接口的
wrap
和
getMIC
方法时,使用该类的实例来指示所需的保护质量(QOP),并指定是否对调用者提供的数据应用保密服务(仅适用于
wrap
方法)。若要请求默认QOP,应使用值
0
。示例代码如下:
// perform wrap on an application-supplied message, appMsg,
// using QOP = 0, and requesting privacy service
MessageProp mProp = new MessageProp(0, true);
byte[] tok = context.wrap(appMsg, 0, appMsg.length, mProp);
sendToken(tok);
应用程序还可以开始使用
wrap
和
getMIC
逐消息方法对应用程序提供的数据进行加密操作,然后将结果令牌发送给与之建立了安全上下文的其他应用程序。接收者调用
unwrap
或
verifyMIC
方法来解释每个令牌。当上下文不再需要时,应用程序应调用
dispose
方法释放上下文可能使用的任何系统资源。
2. JSSE核心类与机制
JSSE(Java Secure Socket Extension)为Java应用程序提供了身份验证、机密性和完整性控制,以保护通信对等方之间传递的数据免受未经授权的披露和篡改。其核心类位于
javax.net
和
javax.net.ssl
包中。
2.1 建立SSL上下文
SSLSocket
可以通过
SSLSocketFactory
创建,也可以由
SSLServerSocket
接受入站连接时创建。而
SSLSocketFactory
和
SSLServerSocketFactory
对象由
SSLContext
创建。
建立SSL上下文有两种主要方法:
-
使用默认方法
:调用
SSLSocketFactory
或
SSLServerSocketFactory
的静态
getDefault
方法,创建一个具有默认
KeyManager
、
TrustManager
和安全随机数生成器的默认
SSLContext
。对于Sun的JSSE实现,使用的密钥材料位于默认的密钥库/信任库中。
-
自定义方法
:调用
SSLContext
的静态
getInstance
方法获取实例,然后通过
init
方法初始化。
init
方法接受三个参数:
KeyManager
对象数组、
TrustManager
对象数组和一个随机数生成器。示例代码如下:
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(km, tm, random);
一旦建立了SSL连接,就会创建一个
SSLSession
,其中包含各种信息,如已建立的身份、使用的密码套件等。该会话用于描述两个实体之间正在进行的关系和状态信息。
2.2 SocketFactory和ServerSocketFactory类
javax.net.SocketFactory
抽象类用于创建套接字,其他工厂类必须继承该类,以创建特定的套接子类别,从而为套接字创建提供通用框架。
javax.net.ServerSocketFactory
类专门用于创建服务器套接字。
使用套接字工厂的优点如下:
- 由于工厂和套接字的多态性,同一应用程序代码可以通过传递不同类型的工厂来使用各种类型的套接字。
- 工厂本身可以使用套接字构造中使用的参数进行自定义,例如返回具有各种网络超时或安全参数的套接字。
- 返回给应用程序的套接字可以是
java.net.Socket
(或
javax.net.ssl.SSLSocket
)的子类,以便直接公开新的API,如压缩、安全、记录标记、统计信息收集或防火墙隧道等功能。
获取
SSLSocket
实例有两种方式:
- 通过
SSLSocketFactory
的
createSocket
方法创建。
- 通过
SSLServerSocket
的
accept
方法获取。
以下是获取
SSLSocketFactory
的三种主要方式:
1. 调用
SSLSocketFactory.getDefault
静态方法获取默认工厂。
2. 作为API参数接收工厂,例如
javax.net.ssl.HttpsURLConnection
有
setDefaultSSLSocketFactory
和
setSSLSocketFactory
方法。
3. 构造具有特定配置行为的新工厂。
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{选择获取方式}:::decision
B -->|默认方法| C(调用getDefault方法):::process
B -->|API参数| D(接收工厂作为参数):::process
B -->|自定义构造| E(构造新工厂):::process
C --> F(获取默认SSLSocketFactory):::process
D --> F
E --> F
F --> G([结束]):::startend
3. SSLSocket和SSLServerSocket类
javax.net.ssl.SSLSocket
类继承自
java.net.Socket
类,支持所有标准套接字方法,并添加了特定于安全套接字的方法。该类的实例封装了创建它们的
SSLContext
。虽然有API可以控制套接字实例的安全套接字会话的创建,但信任和密钥管理并不直接暴露。
javax.net.ssl.SSLServerSocket
类类似于
SSLSocket
类,专门用于创建服务器套接字。
4. 其他JSSE相关类
4.1 SSLSession接口
javax.net.ssl.SSLSession
表示在
SSLSocket
连接的两个对等方之间协商的安全上下文。假设对等方共享兼容的
SSLContext
数据,一旦安排好会话,未来在相同两个对等方之间连接的
SSLSocket
可以共享该会话。会话包含将用于安全套接字通信的密码套件,以及对远程对等方网络地址的非权威性提示和管理信息,如创建时间和最后使用时间。
4.2 HttpsURLConnection类
HTTPS协议类似于HTTP,但在请求/接收数据之前先通过SSL/TLS套接字建立安全通道。
javax.net.ssl.HttpsURLConnection
类扩展了
java.net.HttpsURLConnection
类,添加了对HTTPS特定功能的支持。
在获取
HttpsURLConnection
后,可以配置一些HTTP/HTTPS网络连接参数,例如:
-
设置分配的
HostnameVerifier
:如果URL的主机名与在SSL/TLS握手期间收到的凭据中的主机名不匹配,可能发生了URL欺骗。如果实现无法确定主机名匹配,则SSL实现将回调实例分配的
HostnameVerifier
进行进一步检查。
-
设置分配的
SSLSocketFactory
:在某些情况下,需要指定
HttpsURLConnection
实例使用的
SSLSocketFactory
。可以通过
setDefaultSSLSocketFactory
方法设置默认工厂,也可以通过
setSSLSocketFactory
方法为特定实例设置工厂。
4.3 SunJSSE提供者
Sun Microsystems的JSSE版本标配了一个名为Sun - JSSE的提供者。该提供者包提供以下服务:
| 服务类型 | 具体服务 |
| ---- | ---- |
| 密钥工厂实现 | 支持RSA算法 |
| 密钥对生成器 | 生成适用于RSA算法的公钥和私钥对 |
| 密钥库 | 支持PKCS12 |
| 数字签名算法 | 支持MD2withRSA、MD5withRSA和SHA1withRSA |
| 密钥管理器和信任管理器工厂 | 处理X.509证书 |
| SSLContext实现 | 支持SSL、SSLv3、TLS和TLSv1协议 |
4.4 SSLContext类
javax.net.ssl.SSLContext
是一个引擎类,其实例充当SSL套接字工厂的工厂。
SSLContext
保存了在该上下文下创建的所有套接字共享的所有状态信息。每个实例通过其
init
方法使用执行身份验证所需的密钥、证书链和受信任的根CA证书进行配置。
创建
SSLContext
对象的步骤如下:
1. 使用
SSLContext
类的
getInstance
工厂方法创建实例,需要指定协议名称,也可以指定提供者:
public static SSLContext getInstance(String protocol);
public static SSLContext getInstance(String protocol, String provider);
public static SSLContext getInstance(String protocol, Provider provider);
-
调用
init方法初始化:
public void init(KeyManager[] km, TrustManager[] tm, SecureRandom random);
如果
KeyManager[]
参数为
null
,将为该上下文定义一个空的
KeyManager
。如果
TrustManager[]
参数为
null
,将搜索已安装的安全提供者以获取
TrustManagerFactory
的最高优先级实现,并从中获取适当的
TrustManager
。随机数生成器参数可以为
null
,此时将使用默认实现。
4.5 TrustManager接口
javax.net.ssl.TrustManager
的主要职责是确定是否应信任呈现的身份验证凭据。如果凭据不可信,连接将被终止。要对安全套接字的远程身份进行身份验证,需要使用一个或多个
TrustManager
初始化
SSLContext
对象,为每个支持的身份验证机制传递一个
TrustManager
。如果在
SSLContext
初始化时传递
null
,将为你创建一个信任管理器。通常,
SSLContext
有一个单一的信任管理器,并且该信任管理器通常支持基于X.509公钥证书的身份验证。
5. Java GSS - API中的委托权限
在Java GSS - API中,
DelegationPermission
用于控制凭据的委托。当一个主体需要代表另一个主体向服务器进行操作时,就需要相应的委托权限。
DelegationPermission
的目标是一个由两个字符串组成的字符串,用于指定服务主体。创建
DelegationPermission
实例时,内部的引号需要用
\
进行转义。示例代码如下:
DelegationPermission permission = new DelegationPermission(
"\"ftp@EXAMPLE.COM\" \"krbtgt/EXAMPLE.COM@EXAMPLE.COM\"");
上述代码创建的权限允许Kerberos服务主体
ftp@EXAMPLE.COM
接收由
krbtgt/EXAMPLE.COM@EXAMPLE.COM
表示的转发TGT(Ticket Granting Ticket)。与无条件的转发TGT不同,使用两个主体名称的权限允许更细粒度的委托,例如特定服务的代理票据。
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(创建DelegationPermission实例):::process
B --> C(指定服务主体字符串):::process
C --> D(转义内部引号):::process
D --> E(完成权限创建):::process
E --> F([结束]):::startend
6. 上下文特性请求与检查
在使用Java GSS - API建立安全上下文之前,上下文发起者可以请求特定的上下文特性。以下是一些常见的特性请求示例:
// Instantiate and initialize a security con
// established with the server
GSSContext context = manager.createContext(serverName,
krb5Mechanism, userCreds, GSSContext.DEFAULT_LIFETIME);
// t desired context options prior to context
// tablishment
context.requestConf(true); // 请求机密性
context.requestMutualAuth(true); // 请求相互认证
context.requestReplayDet(true); // 请求重放检测
context.requestSequenceDet(true); // 请求序列检测
然而,并非所有底层机制都支持所有请求的特性。在上下文建立后,调用者可以使用各种查询方法检查上下文实际提供的特性和服务。例如:
if (context.isProtReady()) {
boolean isConfSupported = context.getConfState();
boolean isMutualAuthSupported = context.getMutualAuthState();
boolean isReplayDetSupported = context.getReplayDetState();
boolean isSequenceDetSupported = context.getSequenceDetState();
}
7. JSSE中的套接字工厂使用场景
在实际应用中,不同的场景可能需要不同的套接字工厂配置。以下是一些常见的使用场景及对应的操作步骤:
7.1 使用默认套接字工厂
当应用程序对安全上下文的配置要求不高,希望使用默认的安全设置时,可以使用默认的套接字工厂。操作步骤如下:
1. 获取默认的
SSLSocketFactory
:
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
-
使用工厂创建
SSLSocket:
SSLSocket socket = (SSLSocket) factory.createSocket("example.com", 443);
7.2 自定义套接字工厂
当应用程序需要特定的安全配置,如自定义的密钥管理器、信任管理器或随机数生成器时,可以自定义
SSLSocketFactory
。操作步骤如下:
1. 创建
SSLContext
实例:
SSLContext context = SSLContext.getInstance("TLS");
-
初始化
SSLContext:
KeyManager[] km = ...; // 自定义密钥管理器
TrustManager[] tm = ...; // 自定义信任管理器
SecureRandom random = ...; // 自定义随机数生成器
context.init(km, tm, random);
-
获取自定义的
SSLSocketFactory:
SSLSocketFactory factory = context.getSocketFactory();
-
使用工厂创建
SSLSocket:
SSLSocket socket = (SSLSocket) factory.createSocket("example.com", 443);
7.3 通过API参数传递套接字工厂
当代码需要创建套接字,但不关心套接字的具体配置时,可以通过API参数接收
SSLSocketFactory
。例如,在
javax.net.ssl.HttpsURLConnection
中:
SSLSocketFactory customFactory = ...; // 自定义的SSLSocketFactory
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://example.com").openConnection();
connection.setSSLSocketFactory(customFactory);
8. SSLSession的复用与管理
SSLSession
可以在多个
SSLSocket
连接之间复用,从而提高性能和效率。当两个对等方建立了一个
SSLSession
后,后续的连接可以尝试复用该会话。以下是一个简单的示例:
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket1 = (SSLSocket) factory.createSocket("example.com", 443);
SSLSession session1 = socket1.getSession();
// 后续连接尝试复用会话
SSLSocket socket2 = (SSLSocket) factory.createSocket("example.com", 443);
socket2.setUseClientMode(true);
socket2.startHandshake();
SSLSession session2 = socket2.getSession();
if (session1.getId().equals(session2.getId())) {
System.out.println("Session复用成功");
} else {
System.out.println("Session复用失败");
}
在管理
SSLSession
时,需要注意以下几点:
- 会话的有效性:会话有一定的生命周期,超过有效期后将无法复用。
- 会话的共享:只有在相同的
SSLContext
下创建的套接字才能共享会话。
- 会话的安全性:复用会话时,需要确保会话的安全性,避免会话劫持等安全问题。
9. HttpsURLConnection的配置要点
在使用
HttpsURLConnection
时,正确的配置可以提高连接的安全性和可靠性。以下是一些配置要点及对应的操作步骤:
9.1 设置HostnameVerifier
为了防止URL欺骗,需要设置合适的
HostnameVerifier
。操作步骤如下:
1. 创建自定义的
HostnameVerifier
:
HostnameVerifier verifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// 自定义主机名验证逻辑
return hostname.equals("example.com");
}
};
-
将
HostnameVerifier应用到HttpsURLConnection:
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://example.com").openConnection();
connection.setHostnameVerifier(verifier);
9.2 设置SSLSocketFactory
在某些情况下,需要指定
HttpsURLConnection
使用的
SSLSocketFactory
。操作步骤如下:
1. 获取或创建自定义的
SSLSocketFactory
:
SSLSocketFactory factory = ...; // 自定义的SSLSocketFactory
-
将
SSLSocketFactory应用到HttpsURLConnection:
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://example.com").openConnection();
connection.setSSLSocketFactory(factory);
10. 总结
Java平台提供了丰富的安全机制,包括Java GSS - API和JSSE,用于保护应用程序之间的通信安全。通过合理使用这些机制,可以实现身份验证、机密性、完整性保护和凭据委托等功能。
在实际应用中,需要根据具体的需求选择合适的安全机制和配置。例如,在需要细粒度的权限控制时,可以使用
DelegationPermission
;在建立安全连接时,可以根据不同的场景选择合适的套接字工厂和配置
SSLSession
。同时,要注意安全配置的正确性和安全性,避免出现安全漏洞。
以下是一个简单的总结表格:
| 安全机制 | 主要功能 | 关键类 |
| ---- | ---- | ---- |
| Java GSS - API | 建立安全上下文,实现消息安全 |
GSSContext
、
MessageProp
、
DelegationPermission
|
| JSSE | 提供身份验证、机密性和完整性控制 |
SSLContext
、
SSLSocketFactory
、
SSLSession
|
通过深入理解和掌握这些安全机制,开发者可以更好地保护Java应用程序的通信安全,为用户提供更安全可靠的服务。
超级会员免费看
15

被折叠的 条评论
为什么被折叠?



