Oracle关于Java密码体系结构(JCA, Java Cryptography Architecture)说明的文档如下。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#Introduction |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#Introduction |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html#Introduction |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#Introduction |
Java平台非常强调安全,包括语言安全、密码、公钥基础设施(PKI, public key infrastructure)、认证、安全通信与访问控制。
JCA是Java安全平台的主要部分,JCA包含一个"提供者"体系,以及一系列的API,用于数字签名、消息摘要(hashs)、证书与证书验证、加密(对称/非对称,块/流密码)、密钥生成与管理以及安全随机数产生等。
JDK中其他可用的加密通信库也使用了JCA提供者体系结构,如Java安全套接字扩展(JSSE, Java Secure Socket Extension)提供了访问SSL与TLS的实现。Java通用安全服务(JGSS, Java Generic Security Services),简单身份验证和安全层(SASL, Simple Authentication and Security Layer)也可用于需要通信的应用程序之间安全地交换消息。
到JDK1.4之前,Java密码扩展(JCE, Java Cryptographic Extension)是一个未捆绑的产品。因此,JCA与JCE经常被称为独立的不同的组件。由于JCE目前已捆绑到JDK中,JCE与JCA间的区别变得越来越不明显。由于JCE使用了和JCA相同的体系结构,将JCE考虑为JCA的一部分更合适一些。
JDK中的JCA包含两个软件组件:
JCA提供的核心类与接口如下:
Oracle关于Java SSL实现的文档如下。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html |
Java不同版本对SSL版本的支持如下。
JRE版本 | URL |
---|---|
1.5 | SSL 2.0, SSL 3.0, TLS 1.0 |
1.6 | SSL 2.0, SSL 3.0, TLS 1.0 |
1.7 | SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.1, TLS 1.2 |
1.8 | SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.1, TLS 1.2 |
在Java中进行SSL相关处理时,需要使用Java安全套接字扩展(JSSE, Java Secure Socket Extension)相关API。
JSSE包含以下特点:
JSSE标准API在javax.net与javax.net.ssl包中,包含以下内容:
为了安全地进行通信,连接的两端都必须启用SSL。在JSSE API中,连接的终点类为SSLSocket 与SSLEngine。SSLSocket为阻塞模式,SSLEngine为非阻塞模式。
下图中用于创建SSLSocket/SSLEngines的主要类以逻辑顺序排列,下图的URL为:http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/classes1.jpg,JRE1.5、JRE1.6、JRE1.7、JRE1.8对应文档中均使用该图。
以下均基于SSLSocket进行分析。
JSSE的核心类在javax.net与javax.net.ssl包中。
略。
javax.net.ssl.SSLSocketFactory类作为创建安全套接字的工厂类使用。该类为javax.net.SocketFactory类的虚拟子类。
安全套接字工厂对创建和初始配置安全套接字的详细信息进行了封装。这些包括认证密钥,对等体证书验证,启用的密码套件等。
javax.net.ssl.SSLServerSocketFactory类与SSLSocketFactory类似,用于创建服务端套接字。
javax.net.ssl.SSLSocket是Java标准java.net.Socket类的子类。SSLSocket类支持标准套接字的全部方法,并添加了特定于安全套接字的额外方法。SSLSocket类的实例封装了创建它们的SSLContext类。
javax.net.ssl.SSLServerSocket与SSLSocket类类似,用于创建服务端套接字。
SSLSocket类的实例可通过两种方式获得。第一种,SSLSocket可以通过SSLSocketFactory实例的众多createSocket方法创建。第二种,可以通过SSLServerSocket类的accept方法获得。
略。
略。
见下文。
以下的类与接口可支持SSLContext对象的创建与初始化,可用于创建SSLSocketFactory、SSLServerSocketFactory及SSLEngine对象。JSSE支持的类与接口在javax.net.ssl包中。
以下的三个类(SSLContext、KeyManagerFactory与TrustManagerFactory)是引擎类。引擎类是支持特定算法的类(或是支持特定协议的类,如果是SSLContext的话),引擎类的实现可以在一个或多个加密服务提供者(provider)包中。更多关于提供者与引擎类的信息,可以查看Java密码体系文档的“设计原则”与“概念”章节。
JSSE标准的SunJSSE 提供者提供了SSLContext、KeyManagerFactory与TrustManagerFactory实现。与标准Java安全API(java.security)中的引擎类实现一样,SunJSSE提供的实现如下。
引擎类实现 | 算法或协议 | JDK版本 |
---|---|---|
KeyStore | PKCS12 | JDK1.5、JDK1.6、JDK1.7、JDK1.8 |
KeyManagerFactory | SunX509、NewSunX509 | JDK1.5、JDK1.6 |
KeyManagerFactory | PKIX、SunX509 | JDK1.7、JDK1.8 |
TrustManagerFactory | SunPKIX(又名X509/PKIX)、SunX509 | JDK1.5 |
PKIX(又名X509/SunPKIX)、SunX509 | JDK1.6、JDK1.7、JDK1.8 | |
SSLContext | SSLv3(又名SSL)、TLSv1(又名TLS) | JDK1.5、JDK1.6 |
SSLContext | SSLv3(又名SSL)、TLSv1(又名TLS)、TLSv1.1、TLSv1.2 | JDK1.7、JDK1.8 |
javax.net.ssl.SSLContext是实现安全套接字协议的引擎类。SSLContext类的实例用作SSL套接字的工厂类与SSL引擎类。SSLContext保存了基于该上下文创建的全部对象的状态信息。
每个实例通过其init方法配置需要执行身份验证的密钥,证书链及受信任根CA证书。这些配置以密钥与信任管理器的形式提供。这些管理器为上下文所支持的密码套件的认证及密钥协商方面提供了支持。
目前(JDK1.5-JDK1.8),仅支持基于X.509的管理器。
与其他基于提供者的JCA引擎类类似,SSLContext对象在创建时使用SSLContext类的getInstance工厂方法。这些静态方法每个返回一个至少实现所请求的安全套接字协议的实例。返回的实例可能会也会实现其他协议。例如,getInstance(“SSLv3”)可能返回一个实现 "SSLv3"与"TLSv1"的实例。当通过SSLContext生成SSLSocket、SSLServerSocket或SSLEngine时,可使用getSupportedProtocols方法获取支持的协议列表。使用setEnabledProtocols(String[] protocols)方法可以控制SSL连接实际启用的协议版本。
通过调用getInstance工厂方法创建SSLContext对象时,必须指定协议名称。也可以指定提供了指定协议的提供者,如下所示。
public static SSLContext getInstance(String protocol);
public static SSLContext getInstance(String protocol, String provider);
public static SSLContext getInstance(String protocol, Provider provider);
若仅指定了协议名称,系统将会确定环境中是否存在所请求协议的实现,若存在多于一个,则确定优选的协议。
若协议名称与提供者均已指定,系统会确定请求的提供者中是否存在对请求的协议的实现,若不存在则抛出异常。
刚创建的SSLContext必须调用init方法进行初始化。
public void init(KeyManager[] km, TrustManager[] tm, SecureRandom random);
若KeyManager[]参数为空,一个空的KeyManager将会为当前上下文定义。若TrustManager[]参数为空,将搜索已安装的安全提供者,以获取TrustManagerFactory的最高优先级实现,从中将获取最合适的TrustManager。同样,SecureRandom参数可为空,在这种情况下将使用默认实现。
若使用内部的默认上下文(如通过 SSLSocketFactory.getDefault()或SSLServerSocketFactory.getDefault()生成的SSLContext),会创建一个默认的KeyManager 与TrustManager,也会选择默认的SecureRandom实现。
TrustManager的主要责任是确定所呈现的认证凭证是否应该被信任。若凭证不被信任,连接将会被终止。为了验证安全套接字对端的远程身份,需要初始化一个包含一个或多个TrustManager的SSLContext。你需要为支持的每种身份验证机制传递一个TrustManager。若在SSLContext初始化时传入的TrustManager为空,一个信任管理器会被创建。通常,存在一个单独的支持基于X.509公钥证书(如X509TrustManager)验证的信任管理器。
TrustManager可通过TrustManagerFactory创建,或通过提供对应接口的具体实现创建。
javax.net.ssl.TrustManagerFactory是基于提供者服务的引擎类,用作一种或多种类型的TrustManager对象的工厂。由于它是基于提供者的,额外的工厂可被实现并配置,这些额外的工厂可能提供额外的或替代的信任管理器,或提供更复杂的服务,或实现特定安装的身份验证策略。
创建TrustManagerFactory实例的方式与SSLContext类似,区别在于getInstance方法不需要传入protocol参数,而是需要algorithm参数,如下所示。
public static TrustManagerFactory getInstance(String algorithm);
public static TrustManagerFactory getInstance(String algorithm, String provider);
public static TrustManagerFactory getInstance(String algorithm, Provider provider);
在初始化SSLContext时,可以使用通过信任管理器工厂创建的信任管理器,也可以使用自定义的信任管理器,可能使用CertPath API。当使用X509TrustManager接口实现信任管理器时,不需要使用信任管理器工厂。
新创建的工厂需要调用init方法进行初始化。
public void init(KeyStore ks);
public void init(ManagerFactoryParameters spec);
在调用init方法时,需要选择当前使用的TrustManagerFactory对应的init方法。
对于很多工厂,例如SunJSSE提供者中的名为"SunX509"的TrustManagerFactory,TrustManagerFactory在初始化时唯一需要的信息是KeyStore,因此需要调用上述的前一个init方法。在进行授权检查时,TrustManagerFactory会从KeyStore查询信息,决定远程证书是否应受信任。
在某些情况下,TrustManagerFactory在初始化时不需要KeyStore参数,而需要ManagerFactoryParameters参数。
默认的信任管理器算法是"PKIX"。可通过修改java.security文件中的ssl.TrustManagerFactory.algorithm属性进行修改。
PKIX信任管理器工厂使用已安装的安全提供者中的CertPath PKIX实现。在JDK1.6中,提供了名为"SUN"的CertPath提供者。信任管理器工厂可以使用通常的init(KeyStore ks)方法进行初始化,或向PKIX信任管理器传入CertPath参数,使用javax.net.ssl.CertPathTrustManagerParameters类。
javax.net.ssl.X509TrustManager接口扩展了普通的TrustManager接口。在使用基于X.509的认证时,该接口必须被信任管理器实现。
为了通过JSSE支持远程套接字对端的X.509认证,必须将该接口的实例传入SSLContext对象的init方法。
在实现X509TrustManager接口时,可以直接进行实现,或从基于提供者的TrustManagerFactory中获取一个(例如SunJSSE提供者所提供的)。可以实现自己的X509TrustManager,代理到工厂生成的信任管理器。
当传入SunJSSE的"PKIX"或"SunX509"对应的TrustManagerFactory的KeyStore参数为空时,工厂会使用如下步骤尝试找到信任的信息。
若系统参数javax.net.ssl.trustStore被定义,TrustManagerFactory会尝试尝试找到该系统参数指定的文件名,并将该文件作为KeyStore使用。
若系统参数javax.net.ssl.trustStorePassword也被定义,该参数的值会作为受信任密钥库的密码使用。
若javax.net.ssl.trustStore参数被定义,但指定的文件不存在,会创建一个使用空的密钥库的默认TrustManager。
若系统参数javax.net.ssl.trustStore未定义,且/lib/security/jssecacerts文件存在,会使用该文件作为受信任密钥库。
若以上条件均不满足,且/lib/security/cacerts文件存在,会使用该文件作为受信任密钥库。
若以上文件均不存在,也是允许的,因为存在SSL密码套件是匿名的,这些不需要进行认证,也不需要使用受信任密钥库。
当提供的X509TrustManager行为不满足要求时,可以创建的自定义的X509TrustManager,可通过创建并注册自定义TrustManagerFactory的方式实现,或通过直接实现X509TrustManager接口的方式实现。
在创建了自定义的信任管理器后,可将其通过init方法将其分配给SSLContext,该SSLContext在之后创建的套接字工厂将使用指定的TrustManager做出信任决策。
可对自定义X509TrustManager进行增强,以进行动态密钥库更新。当checkClientTrusted或checkServerTrusted方法检测失败,且未创建受信任证书链时,可将需要的受信任证书加入密钥库中。需要基于使用更新过的密钥库进行初始化的TrustManagerFactory创建新的TrustManagerFactory。当创建新的连接(使用之前初始化的SSLContext)时,新加入的证书将会参与信任决策。
KeyManager的主要责任是选择最终发送给远程主机的认证凭证(在SSL通信过程中)。为了向远程对端认证你自己(本地安全套接字对端),你需要初始化一个带有一个或多个KeyManager的SSLContext对象。你需要为支持的每种不同的身份验证机制传递一个KeyManager。若在SSLContext初始化时传入的KeyManager为空,一个空的KeyManager会被创建。当使用内部默认上下文(如SSLSocketFactory.getDefault()或SSLServerSocketFactory.getDefault()创建的SSLContext)时,会创建一个默认的KeyManager。通常,存在一个单独的支持基于X.509公钥证书(如X509TrustManager)验证的证书管理器。
KeyManager可通过KeyManagerFactory创建,或通过提供对应接口的具体实现创建。
javax.net.ssl.KeyManagerFactory是基于提供者服务的引擎类,用作一种或多种类型的KeyManager对象的工厂。SunJSSE提供者实现了一个可以返回基本X.509密钥管理器的工厂。由于KeyManagerFactory是基于提供者的,额外的工厂可被实现并配置,这些额外的工厂可能提供额外的或替代的密钥管理器。
创建KeyManagerFactory实例的方式与SSLContext类似,区别在于getInstance方法不需要传入protocol参数,而是需要algorithm参数,如下所示。
public static KeyManagerFactory getInstance(String algorithm);
public static KeyManagerFactory getInstance(String algorithm, String provider);
public static KeyManagerFactory getInstance(String algorithm, Provider provider);
新创建的工厂需要调用init方法进行初始化。
public void init(KeyStore ks, char[] password);
public void init(ManagerFactoryParameters spec);
在调用init方法时,需要选择当前使用的KeyManagerFactory对应的init方法。
对于很多工厂,例如SunJSSE提供者中默认的名为"SunX509"的KeyManagerFactory,KeyManagerFactory在初始化时唯一需要的信息是KeyStore与密码,因此需要调用上述的前一个init方法。KeyManagerFactory会从KeyStore中查询私钥与匹配的公钥证书,用于验证远程套接字对端。init方法中的密码参数对应KeyStore的保护密码,KeyStore中的所有密钥应使用相同的密码进行保护。
在某些情况下,KeyManagerFactory在初始化时不需要KeyStore与密码参数,而需要ManagerFactoryParameters参数。
综上所述,SunJSSE提供者支持一个"SunX509"工厂,在初始化时必须使用KeyStore参数。
javax.net.ssl.X509KeyManager接口扩展了普通的KeyManager接口。在使用基于X.509的认证时,该接口必须被密钥管理器实现。
为了通过JSSE支持远程套接字对端的X.509认证,必须将该接口的实例传入SSLContext对象的init方法。
在实现X509KeyManager接口时,可以直接进行实现,或从基于提供者的KeyManagerFactory中获取一个(例如SunJSSE提供者所提供的)。可以实现自己的X509TrustManager,代理到工厂生成的密钥管理器。
当默认的X509KeyManager行为不满足要求时,可以创建的自定义的X509KeyManager。与创建自定义X509TrustManager的方法类似。
TrustManager与KeyManager的职责容易混淆,以下为各管理器的主要责任:
类型 | 作用 |
---|---|
TrustManager | 确定远程认证凭证(以及连接)是否应应该受信任 |
KeyManager | 确定要发送到远程主机的认证凭证 |
这些类作为JSSE API的一部分提供,以支持安全套接字的创建,使用和管理。与JSSE的核心和支持类不同,它们被安全套接字应用程序使用的可能性相对较低。辅助支持类和接口是javax.net.ssl和javax.security.cert包的一部分。
javax.net.ssl.SSLSessionContext是与单个实体相关联的SSLSessions的分组。
javax.net.ssl.SSLSessionBindingListener是由以下对象实现的接口,当这些对象从SSLSession绑定或解除绑定时,它们会收到通知。
javax.net.ssl.SSLSessionBindingEvent是当从SSLSession绑定或解除绑定时传递给SSLSessionBindingListener的事件。
javax.net.ssl.HandShakeCompletedListener是由以下类实现的接口,在给定的SSLSocket连接上完成SSL协议握手时,这些类会收到通知。
javax.net.ssl.HandShakeCompletedEvent是在给定SSLSocket连接上完成SSL协议握手后传递给HandShakeCompletedListener的事件。
当SSL/TLS实现的标准主机名验证逻辑失败,将会调用实现了HostnameVerifier接口且被分配给HttpsURLConnection实例的类的verify方法。若该回调类可以确定主机名是可接受的,它将会报告连接应被允许。不可接受的响应将导致连接终止。
很多安全套接字协议使用公钥证书执行认证,也称为X.509证书。这是SSL和TLS协议的默认身份验证机制。
java.security.cert.X509Certificate抽象类提供了访问X.509证书属性的标准方法。
JSSE包括的实现可被所有用户使用。如果需要,还可定制JSSE的多个方面,配置不同的实现或指定默认密钥库等。
以下的表格总结了JSSE可以自定义的方面,默认值以及用于提供自定义的机制。
下表对应JDK1.5的自定义参数。
可配置项 | 默认值 | 配置方式 |
---|---|---|
X509Certificate实现 | 来自Sun Microsystems的X509Certificate实现 | cert.provider.x509v1安全属性 |
HTTPS协议实现 | 来自Sun Microsystems的实现 | java.protocol.handler.pkgs系统属性 |
提供者实现 | SunJSSE | 安全属性文件中的security.provider.n=配置 |
默认SSLSocketFactory实现 | 来自Sun Microsystems的SSLSocketFactory实现 | ssl.SocketFactory.provider安全属性 |
默认SSLServerSocketFactory实现 | 来自Sun Microsystems的SSLServerSocketFactory实现 | ssl.ServerSocketFactory.provider安全属性 |
默认密钥库 | 无 | javax.net.ssl.keyStore系统属性 |
默认密钥库密码 | 无 | javax.net.ssl.keyStorePassword系统属性 |
默认密钥库提供者 | 无 | javax.net.ssl.keyStoreProvider系统属性 |
默认密钥库类型 | KeyStore.getDefaultType() | javax.net.ssl.keyStoreType系统属性 |
默认信任库 | jssecacerts/cacerts | javax.net.ssl.trustStore系统属性 |
默认信任库密码 | 无 | javax.net.ssl.trustStorePassword系统属性 |
默认信任库提供者 | 无 | javax.net.ssl.trustStoreProvider系统属性 |
默认信任库类型 | KeyStore.getDefaultType() | javax.net.ssl.trustStoreType系统属性 |
默认密钥管理器工厂算法 | SunX509 | ssl.KeyManagerFactory.algorithm安全属性 |
默认信任管理器工厂算法 | PKIX | ssl.TrustManagerFactory.algorithm安全属性 |
默认代理主机 | 无 | https.proxyHost系统属性 |
默认代理端口 | 80 | https.proxyPort系统属性 |
默认密码套件 | 由套接字工厂决定 | https.cipherSuites系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledCipherSuites(String[])方法 |
默认握手协议 | 由套接字工厂决定 | https.protocols系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledProtocols(String[])方法 |
默认https端口 | 443 | 由https URL决定 |
SunJSSE提供者使用的JCE加密算法 | SunJCE实现 | 为可选的JCE算法提供者配置比SunJSSE提供者更高的优先级 |
与JDK1.5相比,JDK1.6增加了以下自定义参数:
允许旧版Hello消息(重新协商)
允许不安全的SSL/TLS重新协商
SSL/TLS大数据包默认缓冲区大小
下表对应JDK1.6的自定义参数。
可配置项 | 默认值 | 配置方式 |
---|---|---|
X509Certificate实现 | 来自Sun Microsystems的X509Certificate实现 | cert.provider.x509v1安全属性 |
HTTPS协议实现 | 来自Sun Microsystems的实现 | java.protocol.handler.pkgs系统属性 |
提供者实现 | SunJSSE | 安全属性文件中的security.provider.n=配置 |
默认SSLSocketFactory实现 | 来自Sun Microsystems的SSLSocketFactory实现 | ssl.SocketFactory.provider安全属性 |
默认SSLServerSocketFactory实现 | 来自Sun Microsystems的SSLServerSocketFactory实现 | ssl.ServerSocketFactory.provider安全属性 |
默认密钥库 | 无 | javax.net.ssl.keyStore系统属性 |
默认密钥库密码 | 无 | javax.net.ssl.keyStorePassword系统属性 |
默认密钥库提供者 | 无 | javax.net.ssl.keyStoreProvider系统属性 |
默认密钥库类型 | KeyStore.getDefaultType() | javax.net.ssl.keyStoreType系统属性 |
默认信任库 | jssecacerts/cacerts | javax.net.ssl.trustStore系统属性 |
默认信任库密码 | 无 | javax.net.ssl.trustStorePassword系统属性 |
默认信任库提供者 | 无 | javax.net.ssl.trustStoreProvider系统属性 |
默认信任库类型 | KeyStore.getDefaultType() | javax.net.ssl.trustStoreType系统属性 |
默认密钥管理器工厂算法 | SunX509 | ssl.KeyManagerFactory.algorithm安全属性 |
默认信任管理器工厂算法 | PKIX | ssl.TrustManagerFactory.algorithm安全属性 |
默认代理主机 | 无 | https.proxyHost系统属性 |
默认代理端口 | 80 | https.proxyPort系统属性 |
默认密码套件 | 由套接字工厂决定 | https.cipherSuites系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledCipherSuites(String[])方法 |
默认握手协议 | 由套接字工厂决定 | https.protocols系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledProtocols(String[])方法 |
默认https端口 | 443 | 由https URL决定 |
SunJSSE提供者使用的JCE加密算法 | SunJCE实现 | 为可选的JCE算法提供者配置比SunJSSE提供者更高的优先级 |
SSL/TLS大数据包默认缓冲区大小 | 无 | jsse.SSLEngine.acceptLargeFragments系统属性 |
允许不安全的SSL/TLS重新协商 | false | sun.security.ssl.allowUnsafeRenegotiation系统属性 |
允许旧版Hello消息(重新协商) | true | sun.security.ssl.allowLegacyHelloMessages系统属性 |
与JDK1.6相比,JDK1.7增加了以下自定义参数:
禁用的证书验证加密算法
禁用的密码套件密码算法
服务器名称指示选项
下表对应JDK1.7的自定义参数。
可配置项 | 默认值 | 配置方式 |
---|---|---|
X509Certificate实现 | 来自Oracle的X509Certificate实现 | cert.provider.x509v1安全属性 |
HTTPS协议实现 | 来自Oracle的实现 | java.protocol.handler.pkgs系统属性 |
提供者实现 | SunJSSE | 安全属性文件中的security.provider.n=配置 |
默认SSLSocketFactory实现 | 来自Oracle的SSLSocketFactory实现 | ssl.SocketFactory.provider安全属性 |
默认SSLServerSocketFactory实现 | 来自Oracle的SSLServerSocketFactory实现 | ssl.ServerSocketFactory.provider安全属性 |
默认密钥库 | 无 | javax.net.ssl.keyStore系统属性 |
默认密钥库密码 | 无 | javax.net.ssl.keyStorePassword系统属性 |
默认密钥库提供者 | 无 | javax.net.ssl.keyStoreProvider系统属性 |
默认密钥库类型 | KeyStore.getDefaultType() | javax.net.ssl.keyStoreType系统属性 |
默认信任库 | jssecacerts/cacerts | javax.net.ssl.trustStore系统属性 |
默认信任库密码 | 无 | javax.net.ssl.trustStorePassword系统属性 |
默认信任库提供者 | 无 | javax.net.ssl.trustStoreProvider系统属性 |
默认信任库类型 | KeyStore.getDefaultType() | javax.net.ssl.trustStoreType系统属性 |
默认密钥管理器工厂算法 | SunX509 | ssl.KeyManagerFactory.algorithm安全属性 |
默认信任管理器工厂算法 | PKIX | ssl.TrustManagerFactory.algorithm安全属性 |
禁用的证书验证加密算法 | MD2, RSA keySize < 1024 | jdk.certpath.disabledAlgorithms安全属性 |
禁用的密码套件密码算法 | 无 | jdk.tls.disabledAlgorithms安全属性 |
默认代理主机 | 无 | https.proxyHost系统属性 |
默认代理端口 | 80 | https.proxyPort系统属性 |
默认密码套件 | 由套接字工厂决定 | https.cipherSuites系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledCipherSuites(String[])方法 |
默认握手协议 | 由套接字工厂决定 | https.protocols系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledProtocols(String[])方法 |
默认https端口 | 443 | 由https URL决定 |
SunJSSE提供者使用的JCE加密算法 | SunJCE实现 | 为可选的JCE算法提供者配置比SunJSSE提供者更高的优先级 |
SSL/TLS大数据包默认缓冲区大小 | 无 | jsse.SSLEngine.acceptLargeFragments系统属性 |
允许不安全的SSL/TLS重新协商 | false | sun.security.ssl.allowUnsafeRenegotiation系统属性 |
允许旧版Hello消息(重新协商) | true | sun.security.ssl.allowLegacyHelloMessages系统属性 |
与JDK1.7相比,JDK1.8增加了以下自定义参数:
禁用/限制的算法
默认启用的TLS协议
临时Diffie-Hellman密钥大小
下表对应JDK1.8的自定义参数。
可配置项 | 默认值 | 配置方式 |
---|---|---|
X509Certificate实现 | 来自Oracle的X509Certificate实现 | cert.provider.x509v1安全属性 |
HTTPS协议实现 | 来自Oracle的实现 | java.protocol.handler.pkgs系统属性 |
提供者实现 | SunJSSE | 安全属性文件中的security.provider.n=配置 |
默认SSLSocketFactory实现 | 来自Oracle的SSLSocketFactory实现 | ssl.SocketFactory.provider安全属性 |
默认SSLServerSocketFactory实现 | 来自Oracle的SSLServerSocketFactory实现 | ssl.ServerSocketFactory.provider安全属性 |
默认密钥库 | 无 | javax.net.ssl.keyStore系统属性 |
默认密钥库密码 | 无 | javax.net.ssl.keyStorePassword系统属性 |
默认密钥库提供者 | 无 | javax.net.ssl.keyStoreProvider系统属性 |
默认密钥库类型 | KeyStore.getDefaultType() | javax.net.ssl.keyStoreType系统属性 |
默认信任库 | jssecacerts/cacerts | javax.net.ssl.trustStore系统属性 |
默认信任库密码 | 无 | javax.net.ssl.trustStorePassword系统属性 |
默认信任库提供者 | 无 | javax.net.ssl.trustStoreProvider系统属性 |
默认信任库类型 | KeyStore.getDefaultType() | javax.net.ssl.trustStoreType系统属性 |
默认密钥管理器工厂算法 | SunX509 | ssl.KeyManagerFactory.algorithm安全属性 |
默认信任管理器工厂算法 | PKIX | ssl.TrustManagerFactory.algorithm安全属性 |
禁用的证书验证加密算法 | MD2, RSA keySize < 1024 | jdk.certpath.disabledAlgorithms安全属性 |
禁用/限制的算法 | SSLv3 | jdk.tls.disabledAlgorithms安全属性 禁用特定算法(协议版本,密码套件,密钥交换机制等),它们将不为SSL/TLS连接协商,即使它们在应用程序中显式启用。 |
默认代理主机 | 无 | https.proxyHost系统属性 |
默认代理端口 | 80 | https.proxyPort系统属性 |
默认密码套件 | 由套接字工厂决定 | https.cipherSuites系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledCipherSuites(String[])方法 |
默认握手协议 | 由套接字工厂决定 | https.protocols系统属性,由逗号分隔的列表,供HttpsURLConnection使用,参考SSLSocket setEnabledProtocols(String[])方法 |
默认https端口 | 443 | 由https URL决定 |
SunJSSE提供者使用的JCE加密算法 | SunJCE实现 | 为可选的JCE算法提供者配置比SunJSSE提供者更高的优先级 |
SSL/TLS大数据包默认缓冲区大小 | 无 | jsse.SSLEngine.acceptLargeFragments系统属性 |
允许不安全的SSL/TLS重新协商 | false | sun.security.ssl.allowUnsafeRenegotiation系统属性 |
允许旧版Hello消息(重新协商) | true | sun.security.ssl.allowLegacyHelloMessages系统属性 |
默认启用的TLS协议 | 无 | jdk.tls.client.protocols系统属性 为了在客户端上启用特定的SunJSSE协议,请在引号内以逗号分隔的列表指定它们,所有其他支持的未指定的协议会在客户端上被禁用。 |
临时Diffie-Hellman密钥大小 | 1024 bits | jdk.tls.ephemeralDHKeySize系统属性 |
支持的JDK版本:JDK1.5-JDK1.8。
JSSE的某些方面可通过系统属性进行自定义,可通过如下方式进行设置。
在java命令中使用-D选项可静态配置Java系统属性,示例如下。
java -Djavax.net.ssl.trustStore=MyCacertsFile MyApp
在Java程序中调用java.lang.System.setProperty方法可以动态配置Java系统属性,示例如下。
System.setProperty(propertyName, "propertyValue");
略。
支持的JDK版本:JDK1.5-JDK1.8。
JSSE的某些方面可通过安全属性进行自定义,可通过如下方式进行设置。
在安全属性文件中添加一行配置,可以静态地设置安全属性。安全属性文件路径为/lib/security/java.security。
在安全属性文件中指定安全属性值,可添加一行以下形式的配置:
propertyName=propertyValue
例如将密钥管理器工厂的默认算法修改由“SunX509”修改为“MyX509”,可添加以下配置。
ssl.KeyManagerFactory.algorithm=MyX509
在程序中调用java.security.Security.setProperty方法,可以动态设置安全属性。
Security.setProperty(propertyName, "propertyValue");
在调用上述方法时,需要替换适当的属性名称和值,示例如下。
Security.setProperty("ssl.KeyManagerFactory.algorithm", "MyX509");
支持的JDK版本:JDK1.5-JDK1.8。
默认情况下,X509Certificate.getInstance方法返回的X509Certificate实现是来自JSSE提供的实现。
可以选择性地返回不同的X509Certificate实现。为此,需要将备用实现的类名称(及包名)指定为名为cert.provider.x509v1的安全属性的值。
支持的JDK版本:JDK1.5-JDK1.8。
可以使用java.net.URL类的“https”URL体系与启用了SSL的Web服务器安全通信。 JDK提供了默认的https的URL实现。
如果需要使用备用的https协议实现,请将java.protocol.handler.pkgs系统属性设置为新的类名。
支持的JDK版本:JDK1.5-JDK1.8。
在1.4及更高的版本中,JDK标准配置了JSSE加密服务提供者,或简称为“SunJSSE”提供者。提供者在本质上是为特定加密算法实现一个或多个引擎类的包。JSSE引擎类包含SSLContext、KeyManagerFactory以及TrustManagerFactory。
提供者在使用前需要被注册,可通过静态或动态的方式进行注册。不需要对“SunJSSE”提供者进行注册,因为它是预先注册的。
在安全属性文件中添加以下形式的配置,可以静态地注册提供者。
security.provider.n=providerClassName
以上配置声明了一个提供者,并指定了它的优先级顺序“n”。优先级顺序是在搜索请求算法对应的提供者时的顺序(当未请求特定提供者时)。该顺序以1开始,1为最高优先级,其次为2,依此类推。
可以同时注册多个JSSE提供程序。它们可以包括用于不同引擎类的不同算法的不同实现,或者支持某些或全部类型相同的算法和引擎类。当搜索用于特定算法的特定引擎类实现时,如果没有指定搜索指定特定提供者,则以优选顺序搜索提供者,并且使用指定算法的第一提供者的实现。
除了静态地注册提供外,还可以在运行时调用Security.addProvider方法动态添加提供者。例如动态添加com.ABC.MyProvider提供者,示例如下。
Security.addProvider(new com.ABC.MyProvider());
Security.addProvider方法将指定的提供程序添加到下一个可用的首选位置。
以上类型的注册不是持久的,只能由具有足够权限的程序来完成。
支持的JDK版本:JDK1.5-JDK1.8。
当默认SSLSocketFactory或SSLServerSocketFactory被创建时(通过调用SSLSocketFactory.getDefault或SSLServerSocketFactory.getDefault),默认的SSLSocketFactory或SSLServerSocketFactory来自JSSE参考实现,默认的SSLContext与上述套接工厂相关联(默认的套接字工厂将来自JSSE实现)。
默认的SSLContext使用默认的KeyManager与TrustManager进行初始化。当通过javax.net.ssl.keyStore与javax.net.ssl.keyStorePassword系统属性分别指定了密钥库与对应的密码时,由默认的SSLContext创建的KeyManager将是一个用于管理指定密钥库的KeyManager实现。当未指定上述系统属性时,该KeyManager管理的密钥库将会是一个新的空密钥库。
通常,在SSL握手中充当服务器的一端将需要使用其KeyManager的密钥库,以便获得客户端用于认证的凭证。当使用匿名密码套件时,则服务器的KeyManager不再需要密钥库。同样地,当服务器不要求客户端提供认证时,客户端也不需要KeyManager密钥库。因此,在以上情况下不需要指定javax.net.ssl.keyStore系统属性。
类似地,当通过javax.net.ssl.trustStore系统属性指定了信任库时,由默认的SSLContext创建的TrustManager将是一个用于管理指定信任库的TrustManager实现。在这种情况下,若上述系统属性存在,但其指定的文件不存在,则不会使用信任库。当未指定javax.net.ssl.trustStore属性时,将会搜索默认的信任库,会依次尝试查找/lib/security/jssecacerts与/lib/security/cacerts文件。若上述方式均未找到信任库,该TrustManager管理的信任库将会是一个新的空信任库。
当指定了javax.net.ssl.keyStoreType或javax.net.ssl.keyStorePassword系统属性时,会分别用作默认密钥库的类型与密码使用。当未指定类型时,会使用KeyStore.getDefaultType()的返回值作为默认类型,对应安全属性文件中的keystore.type属性。当未通过以上方式指定密钥库类型时,会使用“jks”作为密钥库类型。当未指定密钥库密码时,会假设密码为空。
类似地,当指定了javax.net.ssl.trustStoreType或javax.net.ssl.trustStorePassword系统属性时,会分别用作默认信任库的类型与密码使用。当未指定类型时,会使用KeyStore.getDefaultType()的返回值作为默认类型。当未指定信任库密码时,会假设密码为空。
支持的JDK版本:JDK1.5-JDK1.8。
当需要选择KeyManager实现时,首先检查安全属性ssl.KeyManagerFactory.algorithm,当该属性的值已指定时,会使用该值对应的算法搜索KeyManagerFactory实现。第一提供者的实现会被使用,其getKeyManagers方法用于确定提供给默认SSLContext的KeyManager。(在技术上,getKeyManagers方法返回KeyManager数组,一个KeyManager对应每种密钥类型)。当未指定上机安全属性的值时,使用默认值“SunX509”进行搜索。
SunJSSE提供者提供了“SunX509”算法的KeyManagerFactory实现,其指定的KeyManager是javax.net.ssl.X509KeyManager的一个实现。
类似地,当需要选择TrustManager实现时,首先检查安全属性ssl.TrustManagerFactory.algorithm,当该属性的值已指定时,会使用该值对应的算法搜索TrustManagerFactory实现。第一提供者的实现会被使用,其getTrustManagers方法用于确定提供给默认SSLContext的KeyManager。(在技术上,getTrustManagers方法返回TrustManager数组,一个TrustManager对应每种信任类型)。当未指定上机安全属性的值时,使用默认值“PKIX”进行搜索。
SunJSSE提供者提供了“PKIX”算法的TrustManagerFactory实现,其指定的TrustManager是javax.net.ssl.X509TrustManager的一个实现。
支持的JDK版本:JDK1.5-JDK1.8。
SunJSSE提供者使用SunJCE实现来满足所有加密需求。建议将Sun的提供者保持在正常的位置,也可以将其他JCA/JCE的提供者的实现注册到SunJCE提供者的前面。标准JCA机制可用于配置提供者,可通过静态的方式修改安全属性文件/lib/security/java.security进行配置,或通过调用java.security.Security类的addProvider/insertProviderAt方法动态注册。
支持的JDK版本:JDK1.7。
Java SE 7版本包含了两个新的安全属性及一个新的API用于支持禁用特定的加密算法。
jdk.tls.disabledAlgorithms属性适用于TLS握手,jdk.certpath.disabledAlgorithms属性适用于认证路径处理。
从JDK7u40开始,jdk.certpath.disabledAlgorithms的默认值包含“RSA keySize < 1024”,这意味着使用长度小于1024位的RSA密钥的证书是受限制的。jdk.certpath.disabledAlgorithms的默认值如下。
jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
这意味着任意使用MD2算法签名或使用长度小于1024位的RSA密钥的证书都不再允许。
支持的JDK版本:JDK1.8。
在JDK1.8中,对于加密算法的限制,除了JDK1.7中禁用的加密算法的限制外,还包括以下限制。
从JDK8u31版本开始,SSLv3协议已被默认禁用,jdk.tls.disabledAlgorithms安全属性的默认值如下:
jdk.tls.disabledAlgorithms=SSLv3
如果一定要使用SSLv3,可通过在JRE层面清除java.security的jdk.tls.disabledAlgorithms属性中的SSLv3参数,或者在JSSE初始化前设置Security属性。
若需要在部署层面启动SSLv3,可编辑deployment.properties文件,添加“deployment.security.SSLv3=true”配置。
上述内容可查看以下文档:http://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#enable-sslv3。
支持的JDK版本:JDK1.8。
长度小于1024位的Diffie-Hellman(DH)密钥已经被弃用,因为它们的强度不足。 在JDK1.8中,可以使用系统属性jdk.tls.ephemeralDHKeySize自定义临时DH密钥大小。
使用javacpl命令可以查看证书信息。
SSL客户端与服务器套接字分别使用javax.net.ssl.SSLSocket与javax.net.ssl.SSLServerSocket类。
SSLSocket与SSLServerSocket类均存在以下方法。
方法 | 作用 | 返回值 |
---|---|---|
getSupportedProtocols | 获取支持的SSL协议 | String[] |
getSupportedCipherSuites | 获取支持的密码套件 | String[] |
getEnabledProtocols | 获取启用的SSL协议 | String[] |
getEnabledCipherSuites | 获取启用的密码套件 | String[] |
setEnabledProtocols | 设置启用的SSL协议 | String[] |
setEnabledCipherSuites | 设置启用的密码套件 | String[] |
使用SSLContext.getInstance方法获取SSLContext对象后,调用SSLContext对象的getSocketFactory方法,可获得SSLSocketFactory对象,调用SSLSocketFactory对象的createSocket方法,可获得SSLSocket对象。
调用SSLContext对象的getServerSocketFactory方法,可获得SSLServerSocketFactory对象,调用SSLServerSocketFactory对象的createServerSocket方法,可获得SSLServerSocket对象。
在调用SSLContext.getInstance方法时,需要指定String类型的协议名称,当指定不同的协议名称时,生成的套接字所启用的协议版本及密码套件会不同。
不同版本的JDK支持的SSL协议版本如下。
JDK | 支持的SSL协议版本 |
---|---|
JDK1.5、JDK1.6 | SSLv2Hello、SSLv3、TLSv1 |
JDK1.7、JDK1.8 | SSLv2Hello、SSLv3、TLSv1、TLSv1.1、TLSv1.2 |
不同版本的JDK支持的SSL密码套件如下。
JDK | 支持的SSL协议版本 |
---|---|
JDK1.5 | SSL_RSA_WITH_RC4_128_MD5 SSL_RSA_WITH_RC4_128_SHA TLS_RSA_WITH_AES_128_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA SSL_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_DES_CBC_SHA SSL_DHE_RSA_WITH_DES_CBC_SHA SSL_DHE_DSS_WITH_DES_CBC_SHA SSL_RSA_EXPORT_WITH_RC4_40_MD5 SSL_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA SSL_RSA_WITH_NULL_MD5 SSL_RSA_WITH_NULL_SHA SSL_DH_anon_WITH_RC4_128_MD5 TLS_DH_anon_WITH_AES_128_CBC_SHA SSL_DH_anon_WITH_3DES_EDE_CBC_SHA SSL_DH_anon_WITH_DES_CBC_SHA SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA TLS_KRB5_WITH_RC4_128_SHA TLS_KRB5_WITH_RC4_128_MD5 TLS_KRB5_WITH_3DES_EDE_CBC_SHA TLS_KRB5_WITH_3DES_EDE_CBC_MD5 TLS_KRB5_WITH_DES_CBC_SHA TLS_KRB5_WITH_DES_CBC_MD5 TLS_KRB5_EXPORT_WITH_RC4_40_SHA TLS_KRB5_EXPORT_WITH_RC4_40_MD5 TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 |
JDK1.6 | SSL_RSA_WITH_RC4_128_MD5 SSL_RSA_WITH_RC4_128_SHA TLS_RSA_WITH_AES_128_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA SSL_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_DES_CBC_SHA SSL_DHE_RSA_WITH_DES_CBC_SHA SSL_DHE_DSS_WITH_DES_CBC_SHA SSL_RSA_EXPORT_WITH_RC4_40_MD5 SSL_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA TLS_EMPTY_RENEGOTIATION_INFO_SCSV SSL_RSA_WITH_NULL_MD5 SSL_RSA_WITH_NULL_SHA SSL_DH_anon_WITH_RC4_128_MD5 TLS_DH_anon_WITH_AES_128_CBC_SHA SSL_DH_anon_WITH_3DES_EDE_CBC_SHA SSL_DH_anon_WITH_DES_CBC_SHA SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA TLS_KRB5_WITH_RC4_128_SHA TLS_KRB5_WITH_RC4_128_MD5 TLS_KRB5_WITH_3DES_EDE_CBC_SHA TLS_KRB5_WITH_3DES_EDE_CBC_MD5 TLS_KRB5_WITH_DES_CBC_SHA TLS_KRB5_WITH_DES_CBC_MD5 TLS_KRB5_EXPORT_WITH_RC4_40_SHA TLS_KRB5_EXPORT_WITH_RC4_40_MD5 TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 |
JDK1.7 | TLS_ECDH_ECDSA_WITH_NULL_SHA TLS_DH_anon_WITH_AES_128_CBC_SHA256 TLS_ECDH_anon_WITH_RC4_128_SHA TLS_DH_anon_WITH_AES_128_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA TLS_KRB5_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_RC4_128_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 TLS_KRB5_EXPORT_WITH_RC4_40_SHA TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_RC4_128_SHA TLS_ECDH_ECDSA_WITH_RC4_128_SHA TLS_ECDH_anon_WITH_NULL_SHA TLS_ECDHE_ECDSA_WITH_RC4_128_SHA TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_NULL_SHA256 TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA TLS_KRB5_WITH_RC4_128_MD5 SSL_RSA_WITH_DES_CBC_SHA TLS_ECDHE_ECDSA_WITH_NULL_SHA TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA TLS_ECDH_RSA_WITH_RC4_128_SHA TLS_EMPTY_RENEGOTIATION_INFO_SCSV SSL_RSA_EXPORT_WITH_DES40_CBC_SHA TLS_KRB5_WITH_3DES_EDE_CBC_MD5 TLS_KRB5_WITH_RC4_128_SHA SSL_DH_anon_WITH_DES_CBC_SHA TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA TLS_ECDH_RSA_WITH_NULL_SHA TLS_ECDH_RSA_WITH_AES_128_CBC_SHA SSL_RSA_EXPORT_WITH_RC4_40_MD5 TLS_KRB5_WITH_DES_CBC_MD5 TLS_KRB5_EXPORT_WITH_RC4_40_MD5 TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA TLS_ECDH_anon_WITH_AES_128_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 SSL_DHE_DSS_WITH_DES_CBC_SHA TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_KRB5_WITH_DES_CBC_SHA SSL_RSA_WITH_NULL_MD5 SSL_DH_anon_WITH_3DES_EDE_CBC_SHA TLS_RSA_WITH_AES_128_CBC_SHA SSL_DHE_RSA_WITH_DES_CBC_SHA TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 SSL_RSA_WITH_NULL_SHA TLS_ECDHE_RSA_WITH_NULL_SHA SSL_DH_anon_WITH_RC4_128_MD5 TLS_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_RC4_128_MD5 TLS_DHE_DSS_WITH_AES_128_CBC_SHA SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_3DES_EDE_CBC_SHA |
JDK1.8 | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_128_CBC_SHA TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDH_RSA_WITH_AES_128_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_3DES_EDE_CBC_SHA TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA TLS_EMPTY_RENEGOTIATION_INFO_SCSV TLS_DH_anon_WITH_AES_128_GCM_SHA256 TLS_DH_anon_WITH_AES_128_CBC_SHA256 TLS_ECDH_anon_WITH_AES_128_CBC_SHA TLS_DH_anon_WITH_AES_128_CBC_SHA TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA SSL_DH_anon_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_DES_CBC_SHA SSL_DHE_RSA_WITH_DES_CBC_SHA SSL_DHE_DSS_WITH_DES_CBC_SHA SSL_DH_anon_WITH_DES_CBC_SHA SSL_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA TLS_RSA_WITH_NULL_SHA256 TLS_ECDHE_ECDSA_WITH_NULL_SHA TLS_ECDHE_RSA_WITH_NULL_SHA SSL_RSA_WITH_NULL_SHA TLS_ECDH_ECDSA_WITH_NULL_SHA TLS_ECDH_RSA_WITH_NULL_SHA TLS_ECDH_anon_WITH_NULL_SHA SSL_RSA_WITH_NULL_MD5 TLS_KRB5_WITH_3DES_EDE_CBC_SHA TLS_KRB5_WITH_3DES_EDE_CBC_MD5 TLS_KRB5_WITH_DES_CBC_SHA TLS_KRB5_WITH_DES_CBC_MD5 TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 |
不同版本的JDK在调用SSLContext.getInstance方法时指定的协议名称与生成的套接字启用的SSL协议版本如下。
JDK | SSLContext.getInstance方法指定的协议名称 | 生成的套接字启用的SSL协议版本 |
---|---|---|
JDK1.5 | “SSL” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.5 | “SSLv2” | 不支持 |
JDK1.5 | “SSLv2Hello” | 不支持 |
JDK1.5 | “SSLv3” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.5 | “TLS” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.5 | “TLSv1” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.5 | “TLSv1.1” | 不支持 |
JDK1.5 | “TLSv1.2” | 不支持 |
JDK1.6 | “SSL” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.6 | “SSLv2” | 不支持 |
JDK1.6 | “SSLv2Hello” | 不支持 |
JDK1.6 | “SSLv3” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.6 | “TLS” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.6 | “TLSv1” | SSLv2Hello、SSLv3、TLSv1 |
JDK1.6 | “TLSv1.1” | 不支持 |
JDK1.6 | “TLSv1.2” | 不支持 |
JDK1.7 | “SSL” | SSLv3、TLSv1 |
JDK1.7 | “SSLv2” | 不支持 |
JDK1.7 | “SSLv2Hello” | 不支持 |
JDK1.7 | “SSLv3” | SSLv3、TLSv1 |
JDK1.7 | “TLS” | SSLv3、TLSv1 |
JDK1.7 | “TLSv1” | SSLv3、TLSv1 |
JDK1.7 | “TLSv1.1” | SSLv3、TLSv1、TLSv1.1 |
JDK1.7 | “TLSv1.2” | SSLv3、TLSv1、TLSv1.1、TLSv1.2 |
JDK1.8(大于JDK8u31) | “SSL” | TLSv1、TLSv1.1、TLSv1.2 |
JDK1.8(大于JDK8u31) | “SSLv2” | 不支持 |
JDK1.8(大于JDK8u31) | “SSLv2Hello” | 不支持 |
JDK1.8(大于JDK8u31) | “SSLv3” | TLSv1 |
JDK1.8(大于JDK8u31) | “TLS” | TLSv1、TLSv1.1、TLSv1.2 |
JDK1.8(大于JDK8u31) | “TLSv1” | TLSv1 |
JDK1.8(大于JDK8u31) | “TLSv1.1” | TLSv1、TLSv1.1 |
JDK1.8(大于JDK8u31) | “TLSv1.2” | TLSv1、TLSv1.1、TLSv1.2 |
以下为不同的配置密钥管理器的方法:
1.使用默认套接字工厂(使用自定义密钥库)
使用SSLSocketFactory/SSLServerSocketFactory类的静态方法getDefault方法可分别获取SSLSocketFactory/SSLServerSocketFactory的默认对象,可使用默认的套接字工厂。
当使用默认套接字工厂时,javax.net.ssl.keyStore系统属性可设置默认密钥库,javax.net.ssl.keyStorePassword系统属性可设置默认密钥库密码,javax.net.ssl.keyStoreProvider系统属性可设置默认密钥库提供者,javax.net.ssl.keyStoreType系统属性可设置默认密钥库类型。
javax.net.ssl.keyStore系统属性可设置为自定义密钥库。被指定密钥库的密码及密钥的密码需要相同,否则会出现异常“java.security.UnrecoverableKeyException: Cannot recover key”。
2.使用默认套接字工厂(不使用密钥库)
当使用默认套接字工厂时,若不指定javax.net.ssl.keyStore系统属性,则不会使用密钥库。
当不使用密钥库时,SSL客户端与服务器均需要使用匿名密码套件,匿名密码套件的名称中带有“anon”(见前文“SSLContext.getInstance与SSL协议/密码套件的关系”)。若不使用匿名密码套件,则会出现异常“javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled.”。浏览器不支持匿名密码套件,无法使用浏览器访问仅提供匿名密码套件的SSL服务器。
3.使用自定义套接字工厂(使用KeyManagerFactory)
使用SSLContext类的getSocketFactory/getServerSocketFactory方法时,可使用自定义的套接字工厂。
SSLContext类在使用时需要调用init方法进行初始化,init方法的参数一类型为KeyManager[],即密钥管理器数组。
SSLContext.init方法的KeyManager数组可使用KeyManagerFactory生成,KeyManagerFactory的初始化方法init可指定KeyStore及需要使用的密钥的密码,KeyStore在使用时需要指定密钥库的文件路径及其密码。
若SSLContext.init方法的KeyManager数组设置为空,则会使用空的密钥库。
4.使用自定义套接字工厂(使用自定义X509KeyManager)
SSLContext.init方法的KeyManager数组也可使用自定义的X509KeyManager,自定义的X509KeyManager实现的方法中,可以调用由KeyManagerFactory对象的X509KeyManager对象对应的方法。
以下为不同的配置信任管理器的方法:
1.使用默认套接字工厂(使用自定义信任库)
当使用默认套接字工厂时,javax.net.ssl.trustStore系统属性可设置默认信任库,javax.net.ssl.trustStorePassword系统属性可设置默认信任库密码,javax.net.ssl.trustStoreProvider系统属性可设置默认信任库提供者,javax.net.ssl.trustStoreType系统属性可设置默认信任库类型。
javax.net.ssl.keyStore系统属性可设置为自定义密钥库。
2.使用默认套接字工厂(使用jssecacerts/cacerts)
当使用默认套接字工厂时,若javax.net.ssl.trustStore系统属性为空,则会依次尝试使用jssecacerts与cacerts信任库。
3.使用默认套接字工厂(不使用信任库)
当使用默认套接字工厂时,若仅使用匿名密码套件,则不会使用信任库。
4.使用自定义套接字工厂(使用TrustManagerFactory)
SSLContext类在使用时需要调用init方法进行初始化,TrustManager[],即信任管理器数组。
SSLContext.init方法的TrustManager数组可使用TrustManagerFactory生成,TrustManagerFactory的初始化方法init可指定KeyStore。
若SSLContext.init方法的TrustManager数组设置为空,则会使用默认的信任库。
5.使用自定义套接字工厂(使用自定义X509TrustManager)
SSLContext.init方法的TrustManager数组也可使用自定义的X509TrustManager。X509TrustManager接口的checkClientTrusted与checkServerTrusted方法分别决定是否信任SSL客户端/服务器,若在方法中未抛出异常则代表信任SSL的对端。
自定义的X509TrustManager实现的方法中,可调用由TrustManagerFactory对象的X509TrustManager对象对应的方法。
自定义的X509TrustManager实现的方法中,也可以不调用TrustManagerFactory对象的X509TrustManager对象,若checkClientTrusted与checkServerTrusted方法中均不抛出异常,则会信任SSL的对端(若不对SSL对端进行验证,可使用该方法)。
X509TrustManager接口的checkClientTrusted与checkServerTrusted方法的参数一类型均为X509Certificate[],其中保存了SSL对端的SSL证书链。X509Certificate类的getSubjectDN方法可获取证书主体识别名,getEncoded方法可获取证书的DER编码格式数据。通过对X509Certificate数组进行遍历,可将SSL证书链的数据保存至文件中。
使用SSL通信时,存在以下不同的认证模式。
当使用匿名密码套件时,客户端与服务器均不对对方进行认证,均不需要使用密钥库及信任库。
如下图所示。
当不进行认证(使用密钥库)时,客户端与服务器均不对对方进行认证,客户端不需要使用密钥库及信任库,服务器需要使用密钥库,不需要使用信任库。客户端与服务器均需要使用匿名密码套件。
如下图所示。
当进行单向认证时,客户端对服务器进行认证,服务器不对客户端进行认证,客户端需要使用信任库,不需要使用密钥库,服务器需要使用密钥库,不需要使用信任库。客户端使用的信任库与服务器使用的密钥库对应。
如下图所示。
当进行双向认证时,客户端与服务器均需要对对方进行认证,均需要使用密钥库及信任库。客户端使用的信任库与服务器使用的密钥库对应,服务器使用的信任库与客户端使用的密钥库对应。
如下图所示。
在X509TrustManager接口默认实现的checkClientTrusted或checkServerTrusted方法中,仅验证证书的匹配关系,不验证证书有效期。因此当证书过期时,使用X509TrustManager接口默认实现的checkClientTrusted或checkServerTrusted方法不会验证失败。
当需要检查证书是否过期时,需要使用X509Certificate类的checkValidity方法,可用于检查当前的日期和时间是否仍在证书中所给定的有效期内,当证书已过期时会抛出javax.security.cert.CertificateExpiredException异常,当证书还未生效时会抛出javax.security.cert.CertificateNotYetValidException异常。
JSSE提供了对动态调试跟踪的支持。Java通用动态跟踪支持通过系统属性java.security.debug获取,JSSE专用动态跟踪支持通过系统属性javax.net.debug获取。
javax.net.debug系统属性必须指定all或ssl,可选的调试说明符可跟随其后,可以使用一个或多个选项。不同选项间可以不设置分隔符,但使用“:”或“,”等作为分隔符更具有可读性。可使用任意可用的分隔符,不同选项的顺序也不会造成影响。
all:用于打开全部的调试
ssl:用于打开ssl调试
record:启用每条记录跟踪
handshake:打印每个握手消息
keygen:打印密钥生成数据
session:打印会话活动
defaultctx:打印默认SSL初始化
sslctx:打印SSLContext跟踪
sessioncache:打印会话缓存跟踪
keymanager:打印密钥管理器跟踪
trustmanager:打印信任管理器跟踪
data:使用十六进制转储显示每个握手消息
verbose:打印详细的握手消息
plaintext:显示十六进制转储的明文记录
packet:打印原始SSL/TLS数据包
使用以下形式的调试选项均是合法的:
-Djavax.net.debug=all
-Djavax.net.debug=ssl:handshake:data
-Djavax.net.debug=SSL,handshake,data,trustmanager
更多关于调试信息的内容,可查看文档“Debugging SSL/TLS Connections”,链接如下:
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/ReadDebug.html |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/ReadDebug.html |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/ReadDebug.html |
以下示例使用JDK1.6完成。
在SSL某一端使用不同的调试选项并进行SSL通信,输出的日志内容有所不同。
当使用“ssl,sslctx”调试选项时,输出信息较少,仅包含SSLContext跟踪信息。
当使用以下调试选项时,输出的日志内容相同:
ssl
ssl,defaultctx
ssl,handshake
ssl,handshake,verbose
ssl,keygen
ssl,keymanager
ssl,record
ssl,session
ssl,sessioncache
ssl,trustmanager
以上调试选项的输出信息包含以下内容:
打开ssl调试
打印默认SSL初始化
打印每个握手消息
打印详细的握手消息
打印密钥生成数据
打印密钥管理器跟踪
启用每条记录跟踪
打印会话活动
打印会话缓存跟踪
打印信任管理器跟踪
当使用“ssl,record,plaintext”调试选项时,相比“ssl”等选项,输出信息增加了通信数据的明文内容,输出形式如下:
Padded plaintext before ENCRYPTION: len = xx
0000: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
Padded plaintext after DECRYPTION: len = xx
0000: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
当使用“ssl,handshake,data”调试选项时,相比“ssl”等选项,输出信息增加了SSL握手的每个消息。
将Java程序的javax.net.debug系统属性设置为all或ssl,可通过JSSE的调试日志查看密钥管理器的初始化过程,在日志中会显示当前使用的密钥信息,示例如下:
found key for : [xx]
chain [x] = [
[
Version: xx
Subject: CN=xx, OU=xx, O=xx, L=xx, ST=xx, C=xx
Signature Algorithm: xx
Key: xx key, xx bits
modulus: xx
public exponent: xx
Validity: [From: xx,
To: xx]
Issuer: CN=xx, OU=xx, O=xx, L=xx, ST=xx, C=xx
SerialNumber: [xx]
]
Algorithm: [xx]
Signature:
xx
]
将Java程序的javax.net.debug系统属性设置为all或ssl,可通过JSSE的调试日志查看信任管理器的初始化过程,在日志中会显示当前信任的证书信息,示例如下:
trustStore is: xx
trustStore type is : xx
trustStore provider is :
init truststore
adding as trusted cert:
Subject: CN=xx, OU=xx, OU=xx, O=xx, C=xx
Issuer: CN=xx, OU=xx, OU=xx, O=xx, C=xx
Algorithm: xx; Serial number: xx
Valid from xx until xx
JSSE中提供了HttpsURLConnection类实现了对HTTPS的支持,与HttpURLConnection类使用方式类似,可通过调用java.net.URL类的openConnection方法获取HttpsURLConnection/HttpURLConnection对象。
javax.net.ssl.HttpsURLConnection继承自java.net.HttpsURLConnection class类,并添加了对HTTPS特性的支持。
在获得了HttpsURLConnection后,可以在调用URLConnection.connect方法初始化网络连接前配置一些http/https参数,例如SSLSocketFactory与HostnameVerifier。
HttpsURLConnection类在加载时会指定一个默认的SSLSocketFactory(对应SSLSocketFactory.getDefault方法返回的工厂类)。当调用HttpsURLConnection.setDefaultSSLSocketFactory静态方法对HttpsURLConnection指定了新的默认SSLSocketFactory后,之后创建的HttpsURLConnection类的实例将会继承当前设置的默认SSLSocketFactory。对于已创建的HttpsURLConnection实例,可调用setSSLSocketFactory方法对继承的SSLSocketFactory进行覆盖。
当指定了HttpsURLConnection使用的SSLSocketFactory后,可通过KeyManager与TrustManager设置HttpsURLConnection的密钥管理器与信任管理器,决定其在进行SSL通信时使用的密钥及信任的证书信息。具体说明可参考前文。
当SSL/TLS握手时收到的证书中的主机名与URL的主机名不匹配时,可能是出现了URL欺骗。如果SSL实现无法确认主机名是否匹配,SSL实现会通过回调的方式调用当前实例指定的HostnameVerifier方法进行进一步检查。若主机名验证失败,当前连接会被关闭。
可通过setHostnameVerifier/setDefaultHostnameVerifier方法设置HttpsURLConnection对象的HostnameVerifier。自定义的HostnameVerifier需要实现HostnameVerifier接口的verify方法。
当进行HTTPS通信时,若服务器的主机名与服务器返回的证书中的主机名不符,自定义的HostnameVerifier的verify方法将会被调用(例如使用IP访问SSL服务器,服务器返回的证书信息中的主机名为域名形式时,会出现服务器的主机名与服务器返回的证书中的主机名不符的情况);若服务器的主机名与服务器返回的证书中的主机名相符,自定义的HostnameVerifier的verify方法不会被调用。
HostnameVerifier接口的verify方法的参数一为String类型,为当前服务器的主机名,参数二为SSLSession类型,为当前连接使用的SSLSession,调用SSLSession的getPeerCertificateChain方法可获取SSL服务器的证书链信息,返回数据类型为X509Certificate[],可获取证书中的commonName信息,或对服务器证书链进行进一步的验证等操作(X509TrustManager接口的checkClientTrusted与checkServerTrusted方法也可以获取到类型为X509Certificate[]的服务器证书链信息)。
与HTTPS相关的JSSE自定义参数如下(JDK1.5-1.8均适用)。
可配置项 | 默认值 | 配置方式 |
---|---|---|
默认代理主机名 | 无默认值 | https.proxyHost系统属性 |
默认代理端口 | 80 | https.proxyPort系统属性 |
默认密码套件 | 由套接字工厂决定 | https.cipherSuites系统属性。该属性包含了由逗号分隔的密码套件名称列表,被指定的密码套件在使用当前HttpsURLConnection时会被启用。参考SSLSocket的setEnabledCipherSuites方法 |
默认握手协议 | 由套接字工厂决定 | https.protocols系统属性。该属性包含了由逗号分隔的协议名称列表,被指定的协议在使用当前HttpsURLConnection时会被启用。参考SSLSocket的setEnabledProtocols方法 |
通过以上自定义参数可分别设置HttpsURLConnection对象的代理主机名、代理端口、启用的密码套件,以及启用的SSL协议版本。
当使用HttpsURLConnection类通过HTTP代理进行HTTPS访问时,除可设置https.proxyHost与https.protocols系统属性实现外,在调用java.net.URL类的openConnection方法时,指定Proxy参数,也可以使用指定的代理信息。
使用HttpsURLConnection类通过HTTP代理进行HTTPS访问时,获取到的证书为代理服务器的证书,而不是目标服务器的证书,若需要对SSL服务器证书进行验证,需要信任代理服务器的证书,而不是目标服务器的证书。
当前连接使用的SSLSession,调用SSLSession的getPeerCertificateChain方法可获取SSL服务器的证书链信息,返回数据类型为X509Certificate[],可获取证书中的commonName信息,或对服务器证书链进行进一步的验证等操作(X509TrustManager接口的checkClientTrusted与checkServerTrusted方法也可以获取到类型为X509Certificate[]的服务器证书链信息)。
与HTTPS相关的JSSE自定义参数如下(JDK1.5-1.8均适用)。
可配置项 | 默认值 | 配置方式 |
---|---|---|
默认代理主机名 | 无默认值 | https.proxyHost系统属性 |
默认代理端口 | 80 | https.proxyPort系统属性 |
默认密码套件 | 由套接字工厂决定 | https.cipherSuites系统属性。该属性包含了由逗号分隔的密码套件名称列表,被指定的密码套件在使用当前HttpsURLConnection时会被启用。参考SSLSocket的setEnabledCipherSuites方法 |
默认握手协议 | 由套接字工厂决定 | https.protocols系统属性。该属性包含了由逗号分隔的协议名称列表,被指定的协议在使用当前HttpsURLConnection时会被启用。参考SSLSocket的setEnabledProtocols方法 |
通过以上自定义参数可分别设置HttpsURLConnection对象的代理主机名、代理端口、启用的密码套件,以及启用的SSL协议版本。
当使用HttpsURLConnection类通过HTTP代理进行HTTPS访问时,除可设置https.proxyHost与https.protocols系统属性实现外,在调用java.net.URL类的openConnection方法时,指定Proxy参数,也可以使用指定的代理信息。
使用HttpsURLConnection类通过HTTP代理进行HTTPS访问时,获取到的证书为代理服务器的证书,而不是目标服务器的证书,若需要对SSL服务器证书进行验证,需要信任代理服务器的证书,而不是目标服务器的证书。