`
hugang357
  • 浏览: 187392 次
  • 性别: Icon_minigender_2
  • 来自: 深圳
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
ssl
 Downloads  |  Product Documentation  |  Support 
 

Advanced Search           dev2dev Home      Dev Centers      Newsgroups      Community      CodeShare      
eDocs Home >  > WebLogic 安全性编程 > 在 Java 客户端中使用 SSL 身份验证 
WebLogic 安全性编程 
                  
 
在 Java 客户端中使用 SSL 身份验证 
本部分包含下列主题: 

■JSSE 和 WebLogic Server
■使用 JNDI 身份验证
■SSL 证书身份验证开发环境
■编写使用 SSL 的应用程序
■SSL 客户端代码示例
这些部分引用了可从 BEA 的 dev2dev 网站联机获得的示例代码,WebLogic Server 分发的以下位置也包括了这些示例代码: 

SAMPLES_HOME\server\examples\src\examples\security\sslclientsslclient 目录包含 instructions.html 文件、ant 生成文件以及下列 Java 和 JavaServer 页 (.jsp) 文件: 

■MyListener.java
■NulledHostnameVerifier.java
■NulledTrustManager.java
■SSLClient.java
■SSLClientServlet.java
■SSLSocketClient.java
■SnoopServlet.jsp
阅读以下各部分中的信息时,需要查看示例。 

 


--------------------------------------------------------------------------------

JSSE 和 WebLogic Server 
Java 安全套接口扩展(Java Secure Socket Extension,简称 JSSE)是一组支持和实现 SSL 和 TLS v1 协议的包,可使得这些功能能够通过编程方式使用。BEA WebLogic Server 支持使用安全套接口层(Secure Sockets Layer,简称 SSL)加密 WebLogic Server 客户端和服务器、Java 客户端、Web 浏览器以及其他服务器之间传输的数据。 

WebLogic Server 的 JSSE 实现可由 WebLogic 客户端使用,但并不是必需的。其他 JSSE 实现也可用于它们在服务器之外的客户端代码。 

在 WebLogic 服务器端应用程序使用 SSL 时,适用下列限制: 

■不支持使用其他(第三方)JSSE 实现开发 WebLogic Server 应用程序。WebLogic Server 使用的 SSL 实现对服务器配置来说是静态的,不可用客户应用程序替换。
■JSSE 的 WebLogic 实现确实支持 JCE 加密服务提供程序(Cryptographic Service Provider,简称 CSP);但由于对 JCE 的提供程序支持不一致,因此 BEA 无法保证未测试的提供程序将即时工作。BEA 已使用下列提供程序测试了 WebLogic Server:
•JDK 5.0 附带的默认 JCE 提供程序(SunJCE 提供程序)。有关 SunJCE 提供程序的信息,请参阅 http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/HowToImplAJCEProvider.html。
•nCipher JCE 提供程序。 
其他提供程序可能与 WebLogic Server 一起使用,但未测试的提供程序不大可能即时工作。有关使用 WebLogic Server 支持的 JCE 提供程序的详细信息,请参阅“确保 WebLogic Server 安全”中的配置 SSL。 

WebLogic Server 为安全套接口层 (SSL) 加密的通信使用 HTTPS 端口,在此端口上仅可以使用 SSL。 

注意: 为了在 WebLogic 客户端实现安全性,必须在 Java 客户端上安装 WebLogic Server 软件分发包。 

 


--------------------------------------------------------------------------------

使用 JNDI 身份验证 
Java 客户端使用 Java 命名和目录接口 (JNDI) 将凭据传送给 WebLogic Server。Java 客户端通过获取 JNDI InitialContext 来建立与 WebLogic Server 的连接。然后,Java 客户端使用 InitialContext,在 WebLogic Server JNDI 树中查找所需的资源。 

注意: JAAS 是首选的身份验证方法;但是,此身份验证提供程序的 LoginModule 只支持用户名和密码身份验证。因此,对于客户端证书身份验证(也称作双向 SSL 身份验证),应该使用 JNDI。要使用 JAAS 进行客户端证书身份验证,必须编写自定义身份验证提供程序(其 LoginModule 能进行证书身份验证)。  

要指定用户和用户凭据,请设置表 5-1 中列出的 JNDI 属性。 

这些属性存储在传递给 InitialContext 构造方法的散列表中。 

清单 5-1 说明了如何在 Java 客户端使用单向 SSL 证书身份验证。有关双向 SSL 身份验证代码示例,请参阅清单 5-4, 使用 JNDI 的双向 SSL 证书身份验证客户端示例。 

清单 5-1 使用 JNDI 的单向 SSL 身份验证示例 
...Hashtable env = new Hashtable();      env.put(Context.INITIAL_CONTEXT_FACTORY,              "weblogic.jndi.WLInitialContextFactory");      env.put(Context.PROVIDER_URL, "t3s://weblogic:7002");      env.put(Context.SECURITY_PRINCIPAL, "javaclient");      env.put(Context.SECURITY_CREDENTIALS, "javaclientpassword");      ctx = new InitialContext(env);注意: 有关 JNDI 上下文和线程以及如何避免潜在 JNDI 上下文问题的信息,请参阅“WebLogic JNDI 编程”中的 JNDI 上下文和线程和如何避免潜在的 JNDI 上下文问题。  

 


--------------------------------------------------------------------------------

SSL 证书身份验证开发环境 
本部分包含下列主题: 

■SSL 身份验证 API
■SSL 客户端应用程序组件
SSL 身份验证 API 
要实现在 WebLogic Server 上使用 SSL 身份验证的 Java 客户端,请使用 Java JDK 5.0 应用程序编程接口(Application Programming Interface,简称 API)和 WebLogic API 的组合。 

表 5-2 列出并描述了用于实现证书身份验证的 Java API 包。表 5-2 中的信息是从 Java API 文档获取的,并附带批注,以添加 WebLogic Server 特定信息。有关 Java API 的详细信息,请参阅位于 http://java.sun.com/j2se/1.5.0/docs/api/index.html 和 http://java.sun.com/j2ee/1.4/docs/api/index.html 的 Javadoc。 

表 5-3 列出并描述了用于实现证书身份验证的 WebLogic API 包。有关详细信息,请参阅 Javadocs for WebLogic Classes。 


表 5-2 Java 证书 API   Java 证书 API  描述  
javax.crypto  此包提供了用于加密操作的类和接口。此包中定义的加密操作包括加密、密钥生成和密钥协议,以及消息身份验证代码(Message Authentication Code,简称 MAC)生成。 
支持包括对称、非对称、块和流密码在内的加密。此包还支持安全流及封装的对象。 
此包中提供的许多类都基于提供程序(请参阅 java.security.Provider 类)。类自身会定义可以将应用程序写入其中的编程接口。然后,实现自身可由独立的第三方供应商编写并根据需要无缝插入。因此,应用程序开发人员可以利用任意数量的基于提供程序的实现,而无需添加或重写代码。  
javax.net  此包为网络应用程序提供了类。这些类包括用于创建套接口的工厂。使用套接口工厂,可以封装套接口创建和配置行为。  
javax.net.SSL  尽管此包中的类和接口受 WebLogic Server 的支持,但 BEA 建议在 WebLogic Server 中使用 SSL 时,使用 weblogic.security.SSL 包。  
java.security.cert  此包提供了用于解析和管理证书、证书撤销列表(Certificate Revocation List,简称 CRL)和证书路径的类和接口。它包含对 X.509 v3 证书和 X.509 v2 CRL 的支持。  
java.security.KeyStore  此类表示内存中的密钥和证书集合。它用于管理两种类型的密钥库条目: 
■密钥条目 
此类型的密钥库条目拥有加密密钥信息,此信息以受保护的格式存储,以便阻止未经授权的访问。 

此类型条目中存储的密钥通常是机密密钥,或相应公钥的证书链附带的私钥。 

给定实体使用私钥和证书链进行自身身份验证。此身份验证的应用程序中包括对 JAR 文件进行签名的软件分发组织,作为发布和/或授权软件的一部分。 

■可信证书条目 
此类型条目包含属于其他方的一个公钥证书。之所以称其为可信证书,是因为密钥库所有者相信,证书中的公钥确实属于由证书主题(所有者)识别的标识。 

此类型条目可用于对其他方进行身份验证。 
 
java.security.PrivateKey  一个私钥。此接口不包含任何方法或常量。它仅用于对所有私钥接口进行分组(并为其提供类型安全)。 

注意: 专用私钥接口扩展了此接口。例如,请参阅 java.security.interfaces 中的 DSAPrivateKey 接口。 

 
java.security.Provider  此类表示 Java 安全 API 的“加密服务提供程序”,其中的提供程序可实现某些或所有 Java 安全部分,包括: 
■算法(如 DSA、RSA、MD5 或 SHA-1)。
■密钥生成、转换和管理工具(例如用于算法特定的密钥)。
每个提供程序都具有名称和版本号,并在安装它的每个运行时进行配置。 
为了提供加密服务的实现,开发人员团队或第三方供应商编写了实现代码,并创建了 Provider 类的子类。  
javax.servlet.http.HttpServletRequest  此接口扩展了 ServletRequest 接口,以为 HTTP Servlet 提供请求信息。 
Servlet 容器可创建 HttpServletRequest 对象,并将其作为参数传递给 Servlet 的服务方法(doGet、doPost,等等)。  
javax.servlet.http.HttpServletResponse  此接口扩展了 ServletResponse 接口,以在发送响应时提供 HTTP 特定的功能。例如,它具有访问 HTTP 头和 cookie 的方法。 
Servlet 容器可创建 HttpServletRequest 对象,并将其作为参数传送给 Servlet 的服务方法(doGet、doPost,等等)。  
javax.servlet.ServletOutputStream  此类提供了用于将二进制数据发送给客户端的输出流。通常通过 ServletResponse.getOutputStream() 方法检索 ServletOutputStream 对象。 
这是由 Servlet 容器实现的抽象类。此类的子类必须实现 java.io.OutputStream.write(int) 方法。  
javax.servlet.ServletResponse  此类定义了用于协助 Servlet 将响应发送给客户端的对象。Servlet 容器可创建 ServletResponse 对象,并将其作为参数传递给 Servlet 的服务方法(doGet、doPost,等等)。  



表 5-3 WebLogic 证书 API   WebLogic 证书 API  描述  
weblogic.net.http.HttpsURLConnection  此类用于表示到远程对象的 HTTP with SSL (HTTPS) 连接。使用此类可进行从充当客户端的 WebLogic Server 到另一个 WebLogic Server 的出站 SSL 连接。  
weblogic.security.SSL.HostnameVerifier  在 SSL 握手期间,主机名验证可确定 URL 中的主机名是否与服务器标识中的主机名相匹配;需要进行此验证以防止中间人攻击。 
WebLogic Server 提供了基于证书的 HostnameVerifier 实现,默认情况下使用该实现,它用于验证 URL 主机名是否与服务器证书的 CN 字段值相匹配。 
您可以使用管理控制台“SSL”选项卡之下的“高级选项”窗格,用自定义主机名验证器替换此默认的主机名验证器;这将影响在使用 WebLogic SSL API 的服务器上运行的 SSL 客户端的默认值。另外,通过诸如 HttpsURLConnection 和 SSLContext 的 WebLogic SSL API,可以显式设置自定义 HostnameVerifier。  
weblogic.security.SSL.
TrustManager  通过此接口,用户可替换对等方证书链中的某些验证错误,并使握手继续。通过此接口,用户还可以在对等方证书链上执行其他验证,并根据需要中断握手。  
weblogic.security.SSL.
CertPathTrustManager  此类利用配置的 CertPathValidation 提供程序来执行附加验证,例如吊销检查。 
默认情况下将安装 CertPathTrustManager,但配置为不调用 CertPathValidator(由 SSLMBean 属性 InboundCertificateValidation 和 OutboundCertificateValidation 控制)。 
安装自定义 TrustManager 的应用程序将替换 CertPathTrustManager。要使用自定义 TrustManager 且同时要调用 CertPathProvider 的应用程序,可通过其自定义 TrustManager 委托给 CertPathTrustManager。  
weblogic.security.SSL.
SSLContext  此类用于存放在此上下文中创建的所有套接口所共享的所有状态信息。  
weblogic.security.SSL.
SSLSocketFactory  此类提供了用于创建 SSL 套接口的 API。  
weblogic.security.SSL.
SSLValidationConstants  此类可定义上下文元素名。SSL 在调用一个或多个 CertPathValidator 对象执行其他验证前,会执行某些内置验证。通过发现已完成的验证,验证器可以可以减少其必须完成的验证量。  


SSL 客户端应用程序组件 
SSL 客户端应用程序至少包含下列组件: 

■Java 客户端
通常,Java 客户端可执行下列功能: 

•使用客户端标识、信任、HostnameVerifier 和 TrustManager 初始化 SSLContext。
•加载密钥库,并检索私钥和证书链
•使用 SSLSocketFactory
•使用 HTTPS 连接到 WebLogic Server 实例所服务的 JSP 
■HostnameVerifier
HostnameVerifier 可实现 weblogic.security.SSL.HostnameVerifier 接口。 

■HandshakeCompletedListener
HandshakeCompletedListener 可实现 javax.net.ssl.HandshakeCompletedListener 接口。SSL 客户端用它来接收有关在给定 SSL 连接上完成 SSL 握手的通知。 

■TrustManager
TrustManager 可实现 weblogic.security.SSL.TrustManager 接口。 

有关可实现上述组件的完整工作 SSL 身份验证客户端,请参阅与 WebLogic Server 一同提供的 SSLClient 示例应用程序(位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下)。此示例也可从 BEA 的 dev2dev 站点联机获得 

有关 JSSE 身份验证的详细信息,请参阅 Sun 的“Java Secure Socket Extension (JSSE) Reference Guide”,可在以下位置找到:http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html。 

 


--------------------------------------------------------------------------------

编写使用 SSL 的应用程序 
本部分包含下列主题: 

■从某个 WebLogic Server 到其他 WebLogic Server 安全地通信
■编写 SSL 客户端
■使用双向 SSL 身份验证
■使用 JNDI 的双向 SSL 身份验证
■使用自定义主机名验证器
■使用信任管理器
■使用 SSLContext
■使用 URL 执行出站 SSL 连接
从某个 WebLogic Server 到其他 WebLogic Server 安全地通信 
可以使用 URL 对象执行从充当客户端的 WebLogic Server 实例到另一个 WebLogic Server 实例的出站 SSL 连接。weblogic.net.http.HttpsURLConnection 类为客户端提供了指定安全上下文信息(包括客户端的数字证书和私钥)的方法。 

weblogic.net.http.HttpsURLConnection 类提供了一些用于实现下列功能的方法:确定协商的密码组、获取/设置主机名验证器、获取服务器的证书链以及获取/设置 SSLSocketFactory 以便新建 SSL 套接口。 

SSLClient 代码示例使用 weblogic.net.http.HttpsURLConnection 类执行出站 SSL 连接。SSLClient 代码示例可从 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下的 examples.security.sslclient 包获取。 

编写 SSL 客户端 
此部分使用这些示例说明了如何编写各种类型的 SSL 客户端。提供了下列类型的 SSL 客户端示例: 

■SSLClient 示例
■SSLSocketClient 示例
■使用双向 SSL 身份验证
SSL 客户端许可要求:任何使用 WebLogic SSL 类 (weblogic.security.SSL) 来调用 Enterprise JavaBean (EJB) 的独立 Java 客户端必须使用 BEA 许可文件。在运行客户端应用程序时,请设置下列系统属性: 

■如果将许可保存在 bea.home 之外的位置,则请将该目录添加到 WebLogic CLASSPATH。
■在命令行上,设置 java.protocol.handler.pkgs=com.certicom.net.ssl
以下是一个运行使用许可文件默认位置 (c:\bea) 的命令的示例: 

java -Dbea.home=c:\bea \
-Djava.protocol.handler.pkgs=weblogic.net my_app 

SSLClient 示例 
SSLClient 示例说明了如何使用 WebLogic SSL 库通过使用 URL 和 URLConnection 对象执行传出 SSL 连接。它显示如何从独立应用程序以及 WebLogic Server 中的Servlet 执行此连接。 

注意: 充当 SSL 客户端的 WebLogic Server 为传出 SSL 连接使用服务器的标识证书。默认情况下,在 WebLogic Server 上运行并使用先前描述的 SSL API 的应用程序不会共享服务器的标识证书,仅是信任。 

清单 5-2 显示了 SSLClient 示例的代码片段,完整的示例位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下的 SSLClient.java 文件中。 

清单 5-2 SSLClient 示例代码片断 
package examples.security.sslclient;import java.io.*;import java.net.URL;import java.security.Provider;import javax.servlet.ServletOutputStream;...  /*   * 使用 WebLogic SSL 客户端类,   *  此方法包含如何使用 URL 和 URLConnection 对象   *  创建新 SSL 连接的示例。*/  public void wlsURLConnect(String host, String port,                            String sport, String query,                            OutputStream out)      throws Exception {...      URL wlsUrl = null;      try {        wlsUrl = new URL("http", host, Integer.valueOf(port).intValue(),                               query);        weblogic.net.http.HttpURLConnection connection =            new weblogic.net.http.HttpURLConnection(wlsUrl);        tryConnection(connection, out);      } ...      wlsUrl = new URL("https", host, Integer.valueOf(sport).intValue(),                             query);      weblogic.net.http.HttpsURLConnection sconnection =          new weblogic.net.http.HttpsURLConnection(wlsUrl);...SSLSocketClient 示例 
SSLSocketClient 示例说明了如何使用 SSL 套接口直接转至安全端口,以连接到 WebLogic Server 实例服务的 JSP,并显示该连接的结果。它显示了如何实现下列功能: 

■使用客户端标识、HostnameVerifier 和 TrustManager 初始化 SSLContext 
■加载密钥库,并检索私钥和证书链
■使用 SSLSocketFactory
■使用 HTTPS 连接到 WebLogic Server 所服务的 JSP 
■实现 javax.net.ssl.HandshakeCompletedListener 接口
■创建 weblogic.security.SSL.HostnameVerifier 类的虚拟实现,以验证此示例连接到的服务器是否在所需的主机上运行 
清单 5-3 显示了 SSLSocketClient 示例的代码片段,完整的示例位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下的 SSLSocketClient.java 文件中。(sslclient 目录中的 SSLClientServlet 示例是一个简单的 SSLClient 的 Servlet 包装示例。) 

清单 5-3 SSLSocketClient 示例代码片断 
package examples.security.sslclient;import java.io.*;import java.security.KeyStore;import java.security.PrivateKey;import java.security.cert.Certificate;import javax.net.ssl.HandshakeCompletedListener;import javax.net.ssl.SSLSocket;import weblogic.security.SSL.HostnameVerifier;import weblogic.security.SSL.SSLContext;import weblogic.security.SSL.SSLSocketFactory;import weblogic.security.SSL.TrustManager;...      SSLContext sslCtx = SSLContext.getInstance("https");      File KeyStoreFile  = new File ("mykeystore");...    // 打开密钥库、检索私钥和证书链      KeyStore ks = KeyStore.getInstance("jks");      ks.load(new FileInputStream("mykeystore"), null);      PrivateKey key = (PrivateKey)ks.getKey("mykey",                            "testkey".toCharArray());      Certificate [] certChain = ks.getCertificateChain("mykey");      sslCtx.loadLocalIdentity(certChain, key);      HostnameVerifier hVerifier = null;      if (argv.length < 3)        hVerifier = new NulledHostnameVerifier();      else         hVerifier = (HostnameVerifier)                         Class.forName(argv[2]).newInstance();      sslCtx.setHostnameVerifier(hVerifier);      TrustManager tManager = new NulledTrustManager();      sslCtx.setTrustManager(tManager);       System.out.println(" Creating new SSLSocketFactory with SSLContext");      SSLSocketFactory sslSF = (SSLSocketFactory)                                      sslCtx.getSocketFactory();      System.out.println(" Creating and opening new SSLSocket with                           SSLSocketFactory");      // 使用 createSocket(String hostname, int port)      SSLSocket sslSock = (SSLSocket) sslSF.createSocket(argv[0],                           new Integer(argv[1]).intValue());      System.out.println(" SSLSocket created");       HandshakeCompletedListener mListener = null;        mListener = new MyListener();       sslSock.addHandshakeCompletedListener(new MyListener());      ...使用双向 SSL 身份验证 
在使用证书身份验证时,WebLogic Server 会将数字证书发送到请求客户端。客户端将检查数字证书,以确保其可信、未过期,并且与提供它的 WebLogic Server 实例相匹配。 

使用双向 SSL 身份验证(一种相互身份验证的方式)时,请求客户端也需要向 WebLogic Server 提供数字证书。如果将 WebLogic Server 的实例配置为双向 SSL 身份验证,则请求客户端需要提供来自指定的一组证书颁发机构的数字证书。WebLogic Server 只接受由可信证书颁发机构签名的数字证书。 

有关如何配置双向 SSL 身份验证的 WebLogic Server 的信息,请参阅“确保 WebLogic Server 安全”中的配置 SSL。 

下列部分描述了可在 WebLogic Server 中实现的几种不同的双向 SSL 身份验证方式。 

■使用 JNDI 的双向 SSL 身份验证
■在 WebLogic Server 实例之间使用双向 SSL 身份验证
■使用带有 Servlet 的双向 SSL 身份验证
使用 JNDI 的双向 SSL 身份验证 
在 Java 客户端中使用 JNDI 进行双向 SSL 身份验证时,请使用 WebLogic JNDI Environment 类的 setSSLClientCertificate() 方法。此方法可为客户端身份验证设置私钥和 X.509 数字证书链。 

要将数字证书传送到 JNDI,请创建在包含 DER 编码数字证书的文件上打开的 InputStreams 数组,并在 JNDI 散列表中设置该数组。数组中的第一个元素必须包含在 Java 客户端的私钥文件上打开的 InputStream。第二个元素必须包含在 Java 客户端的数字证书文件上打开的 InputStream。(此文件包含 Java 客户端的公钥。)其他元素可能包含根证书颁发机构的数字证书,以及证书链中任何数字证书的签名者。如果某个数字证书不是直接由服务器的可信证书颁发机构发行的,则 WebLogic Server 可通过证书链对 Java 客户端的数字证书进行身份验证。 

可以使用 weblogic.security.PEMInputStream 类读取存储在增强型私人邮件(Privacy Enhanced Mail,简称 PEM)文件中的数字证书。此类提供了可通过 PEM 文件对 Base64 编码证书进行解码的筛选器。 

清单 5-4 说明了如何在 Java 客户端中使用双向 SSL 身份验证。 

清单 5-4 使用 JNDI 的双向 SSL 身份验证客户端示例 
import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import weblogic.jndi.Environment;import weblogic.security.PEMInputStream;import java.io.InputStream;import java.io.FileInputStream;public class SSLJNDIClient {   public static void main(String[] args) throws Exception  {    Context context = null;    try {      Environment env = new Environment();      // 设置连接参数      env.setProviderUrl("t3s://localhost:7002");            // 如果您正在使用 UserNameMapper 接口,      // 那么下两个设置方法是可选的。      env.setSecurityPrincipal("system");      env.setSecurityCredentials("weblogic");      InputStream key = new FileInputStream("certs/demokey.pem");      InputStream cert = new FileInputStream("certs/democert.pem");      // 如果 key/cert 在文件中,那么包装输入流      key = new PEMInputStream(key);      cert = new PEMInputStream(cert);      env.setSSLClientCertificate(new InputStream[] { key, cert});             env.setInitialContextFactory(Environment.DEFAULT_INITIAL_CONTEXT_FACTORY);       context = env.getInitialContext();      Object myEJB = (Object) context.lookup("myEJB");    }    finally {      if (context != null) context.close();    }  }}调用 JNDI getInitialContext() 方法时,Java 客户端和 WebLogic Server 将执行相互身份验证,其方式与 Web 浏览器执行相互身份验证以获取安全 Web 服务器连接相同。如果不能验证数字证书,或不能在默认(活动)安全领域中对 Java 客户端的数字证书进行身份验证,将引发异常。通过身份验证的用户对象存储在 Java 客户端的服务器线程上,并用于检查可控制 Java 客户端访问任何受保护 WebLogic 资源的权限。 

在使用 WebLogic JNDI Environment 类时,必须为对 getInitialContext() 方法的每个调用新建一个 Environment 对象。指定 User 对象和安全凭据后,用户及其关联凭据都将保持 Environment 对象中的设置。如果尝试重新设置它们,然后调用 JNDI getInitialContext() 方法,则将使用初始的用户和凭据。 

在 Java 客户端中使用双向 SSL 身份验证时,WebLogic Server 将获取每个客户端 JVM 的唯一 Java 虚拟机 (JVM) ID,以便 Java 客户端和 WebLogic Server 之间的连接始终不变。除非连接由于没有活动而超时,只要 Java 客户端的 JVM 继续执行,连接就会持续存在。Java 客户端能够可靠地协商新 SSL 连接的惟一方法是停止其 JVM,并运行 JVM 的其他实例。 

清单 5-4, 使用 JNDI 的双向身份验证客户端示例 中的代码可生成对实现 weblogic.security.providers.authentication.UserNameMapper 接口的 WebLogic 标识声明提供程序的调用。如果数字证书有效,则实现 UserNameMapper 接口的类将返回用户对象。WebLogic Server 将此通过身份验证的用户对象存储在 WebLogic Server 中 Java 客户端的线程上,在线程尝试使用受默认(活动)安全领域保护的 WebLogic 资源时,此用户对象将用于后续授权请求。 

注意: CLASSPATH 必须指定 weblogic.security.providers.authentication.UserNameMapper 接口的实现。 

如果您尚未配置可执行基于证书的身份验证的标识声明提供程序,则通过新建 JNDI InitialContext,并在 JNDI SECURITY_PRINCIPAL 和 SECURITY_CREDENTIALS 属性中提供新用户名和密码,在具有 SSL 连接的 JVM 中运行的 Java 客户端便可更改 WebLogic Server 用户标识。在进行 SSL 连接后,不使用由 Java 客户端传递的任何数据证书。新 WebLogic Server 用户可继续使用与初始用户的数字证书协商的 SSL 连接。 

如果您已经配置了执行基于证书的身份验证的标识声明提供程序,WebLogic Server 将把数据证书从 Java 客户端传递到实现 UserNameMapper 接口的类,UserNameMapper 类可将数字证书映射到 WebLogic Server 用户名。因此,如果要在使用基于证书的标识声明时设置新用户标识,将无法更改标识。这是因为对于每个 Environment,只在 JVM 发出第一个连接请求时处理数字证书。 

警告: 限制:在使用双向 SSL 和 JNDI 时,不支持多个并发用户从单一客户端 JVM 登录到 WebLogic Server。如果多个登录在不同线程上执行,结果将无法确定,并可能导致一个用户的请求在其他用户的登录上执行,从而使某个用户可以访问其他用户的数据。WebLogic Server 不支持来自单一客户端 JVM 的多个基于证书的并发登录。有关 JNDI 上下文和线程,以及如何避免潜在的 JNDI 上下文问题的信息,请参阅“WebLogic JNDI 编程”中的 JNDI 上下文和线程和如何避免潜在的 JNDI 上下文问题。  

编写用户名映射器 
在使用双向 SSL 时,WebLogic Server 在建立 SSL 连接时可验证 Web 浏览器或 Java 客户端的数字证书。但是,数字证书不会将 Web 浏览器或 Java 客户端标识为 WebLogic Server 安全领域中的用户。如果 Web 浏览器或 Java 客户端请求受安全策略保护的 WebLogic Server 资源,则 WebLogic Server 需要 Web 浏览器或 Java 客户端具有标识。为了处理此要求,WebLogic 标识声明提供程序允许启用用户名映射器,此映射器可将 Web 浏览器或 Java 客户端的数字证书映射到 WebLogic Server 安全领域中的用户。用户名映射器必须是 weblogic.security.providers.authentication.UserNameMapper 接口的实现。 

可以选择使用 weblogic.security.providers.authentication.UserNameMapper 接口的默认实现 DefaultUserNameMapperImpl,或开发自己的实现。 

WebLogic 标识声明提供程序可为下列类型的标识声明标记类型调用 UserNameMapper 接口的实现: 

■通过 SSL 握手传递的 X.509 数字证书
■通过 CSIv2 传递的 X.509 数字证书
■通过 CSIv2 传递的 X.501 识别名
如果您需要映射不同类型的证书,请编写自己的 UserNameMapper 接口实现。 

要实现可将数字证书映射到用户名的 UserNameMapper 接口,请编写可执行下列操作的 UserNameMapper 类: 

1.实例化 UserNameMapper 实现类。 
2.创建 UserNameMapper 接口实现。
3.使用 mapCertificateToUserName() 方法,根据客户端提供的证书链,将证书映射到用户名。
4.将字符串特性类型映射到相应的 Attribute Value Assertion 字段类型。
在 WebLogic Server 实例之间使用双向 SSL 身份验证 
可以在服务器到服务器通信中使用双向 SSL 身份验证,在此通信中其中一个 WebLogic Server 实例充当另一个 WebLogic Server 实例的客户端。通过在服务器到服务器通信中使用双向 SSL 身份验证,即使没有更常用的客户端/服务器环境,您也可以拥有可靠的、高度安全的连接。 

清单 5-5 显示了如何建立从在某个 WebLogic Server 实例中运行的 servlet,到第二个称为 server2.weblogic.com 的 WebLogic Server 实例的安全连接示例。 

清单 5-5 建立到另一个 WebLogic Server 实例的安全连接 
FileInputStream [] f = new FileInputStream[3];    f[0]= new FileInputStream("demokey.pem");   f[1]= new FileInputStream("democert.pem");   f[2]= new FileInputStream("ca.pem");Environment e = new Environment ();e.setProviderURL("t3s://server2.weblogic.com:443");e.setSSLClientCertificate(f);e.setSSLServerName("server2.weblogic.com");e.setSSLRootCAFingerprints("ac45e2d1ce492252acc27ee5c345ef26");e.setInitialContextFactory	("weblogic.jndi.WLInitialContextFactory");Context ctx = new InitialContext(e.getProperties())在清单 5-5 中,WebLogic JNDI Environment 类创建的散列表用于存储下列参数: 

■setProviderURL - 指定充当 SSL 服务器的 WebLogic Server 实例的 URL。WebLogic Server 实例充当 SSL 客户端来调用此方法。URL 用于指定为在 SSL 协议上构建的 WebLogic Server 专用协议的 t3 协议。SSL 协议可保护两个 WebLogic Server 实例之间的连接和通信。
■setSSLClientCertificate - 指定用于 SSL 连接的私钥和证书链。使用此方法可指定一个输入流数组,此数组由私钥(数组中的第一个输入流)以及 X.509 证书链(这构成了数组的剩余输入流)组成。证书链中的每份证书是链中位于它前面的证书的发行方。
■setSSLServerName - 指定充当 SSL 服务器的 WebLogic Server 实例的名称。在 SSL 服务器将其数字证书提供给充当 SSL 客户端的 WebLogic Server 时,会将使用 setSSLServerName 方法指定的名称与此数字证书中的公用名称字段进行比较。为使主机名验证获得成功,这两个名称必须相匹配。此参数用于防止中间人攻击。
■setSSLRootCAFingerprint - 指定可表示一组可信证书颁发机构的数字代码,从而基于可信证书指纹指定信任。从充当 SSL 服务器的 WebLogic Server 实例接收的证书链的根证书,必须与使用此方法指定的指纹之一匹配,才能达到可信的目的。此参数用于防止中间人攻击。它提供了附加的默认信任级别,此级别对于在 WebLogic Server 上运行的客户端而言,是由 WebLogic Server 信任配置指定的。 
注意: 有关 JNDI 上下文和线程,以及如何避免潜在的 JNDI 上下文问题的信息,请参阅“WebLogic JNDI 编程”中的 JNDI 上下文和线程和如何避免潜在的 JNDI 上下文问题。  

使用带有 Servlet 的双向 SSL 身份验证 
要在某个 Servlet(或其他任何服务器端 Java 类)中对 Java 客户端进行身份验证,必须检查客户端是否提供一份数字证书,如果是,检查此证书是否由可信的证书颁发机构发行。Servlet 开发人员负责询问 Java 客户端是否拥有有效的数字证书。在使用 WebLogic Servlet API 开发 Servlet 时,必须通过 HTTPServletRequest 对象的 getAttribute() 方法访问有关 SSL 连接的信息。 

WebLogic Server Servlet 中支持下列特性: 

■javax.servlet.request.X509Certificate
java.security.cert.X509Certificate [] - 可返回一组 X.509 证书。 
■javax.servlet.request.cipher_suite - 可返回 HTTPS 使用的表示密码组的字符串。
■javax.servlet.request.key_size - 可返回一个整数(0、40、56、128、168),此整数表示对称(批量加密)密钥算法的位大小。 
■weblogic.servlet.request.SSLSession
javax.net.ssl.SSLSession -可返回 SSL 会话对象,此对象包含创建和最后使用对象的密码组和日期。
您有权访问数字证书中定义的用户信息。在您获取 javax.servlet.request.X509Certificate 特性时,它是 java.security.cert.X509Certificate 类型的数组。您只需将数组转换为此类型,并检查证书。 

数据证书包括的信息如下所示: 

■主题名称(持有者、所有者)以及验证主题的唯一标识所需的其他标识信息。
■主题的公钥
■发布数字证书的证书颁发机构的名称
■序列号
■数字证书的有效期(或生命周期),由开始日期和终止日期定义 
使用自定义主机名验证器 
主机名验证器可验证进行 SSL 连接的主机是预定方还是授权方。如果 WebLogic 客户端或 WebLogic Server 实例充当另一个应用服务器的 SSL 客户端,则主机名验证器非常有用。它帮助防止中间人攻击。 

注意: 示范数字证书是在安装期间生成的,以便其包含安装 WebLogic Server 软件的系统的主机名。因此,在使用示范证书进行开发和测试时应保留主机名验证。 

在默认情况下,作为 SSL 握手的一项功能,WebLogic Server 会将 SSL 服务器证书主题 DN 的 CN 字段与用于连接到服务器的 URL 中的主机名进行比较。如果这些名称不匹配,则删除此 SSL 连接。 

删除 SSL 连接是由 SSL 客户端引起的,此客户端可根据服务器的数字证书对服务器的主机名进行验证。如果需要除默认行为之外的行为,可以关闭主机名验证,或注册自定义主机名验证器。关闭主机名验证将使 SSL 连接容易受到中间人的攻击。 

可以使用下列方式关闭主机名验证: 

■在管理控制台中,将位于服务器(例如 myserver)的“SSL”选项卡下“高级选项”窗格上的“主机名验证”字段指定为“无”。
■在 SSL 客户端的命令行上输入下列参数:
-Dweblogic.security.SSL.ignoreHostnameVerification=true 

您可以编写自定义主机名验证器。weblogic.security.SSL.HostnameVerifier 接口提供了回调机制,以便此接口的实施者能够提供是否允许连接到 URL 的主机名的策略。此策略可以基于证书,也可以取决于其他身份验证方案。 

要使用自定义主机名验证器,请创建可实现 weblogic.security.SSL.HostnameVerifier 接口的类,并定义可捕获服务器安全标识信息的方法。 

注意: 此接口采用新样式证书并将替换已不再使用的 weblogic.security.SSL.HostnameVerifierJSSE 接口。 

在可以使用自定义主机名验证器之前,需要使用下列方式实现指定实现的类: 

■在管理控制台中,将“服务器配置”下“SSL”选项卡上的“自定义主机名验证器”字段设置为实现此接口的类的名称。指定的类必须拥有公共的 no-arg 构造方法。
■在命令行上输入下列参数:
-Dweblogic.security.SSL.hostnameVerifier=hostnameverifier 

hostnameverifier 的值是实现自定义主机名验证器的类的名称。 

清单 5-6 显示了 NulledHostnameVerifier 示例的代码片断;完整的示例位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下的 NulledHostnameVerifier.java 文件中。此代码示例包含始终返回比较结果为 true 的 NulledHostnameVerifier 类。通过此示例,WebLogic SSL 客户端可以连接到任何 SSL 服务器,无论服务器的主机名和数字证书 SubjectDN 的比较结果如何。 

清单 5-6 主机名验证器示例代码片断 
public class NulledHostnameVerifier implements                     weblogic.security.SSL.HostnameVerifier {  public boolean verify(String urlHostname, javax.net.ssl.SSLSession session) {    return true;  }}使用信任管理器 
weblogic.security.SSL.TrustManager 接口提供执行以下功能: 

■忽略特定的证书验证错误
■对对等方证书链执行其他验证
注意: 此接口采用新样式证书并将替换已不再使用的 weblogic.security.SSL.TrustManagerJSSE 接口。 

当 SSL 客户端连接到 WebLogic Server 实例时,服务器会将其数字证书链提供给客户端以进行身份验证此链可能包含无效的数字证书。SSL 规范中规定在发现无效证书时,客户端应该删除 SSL 连接。可以使用 TrustManager 接口的自定义实现,来控制何时继续或中断 SSL 握手。使用信任管理器可以忽略某些验证错误,还可以根据需要执行验证检查,然后决定是否继续握手。 

可使用 weblogic.security.SSL.TrustManager 接口创建信任管理器。此接口包含一组证书验证的错误代码。也可以对对等方证书执行其他验证,并根据需要中断 SSL 握手。在验证数字证书后,weblogic.security.SSL.TrustManager 接口将使用回调功能替换数字证书的验证结果。可以通过 setTrustManager() 方法将信任管理器的实例与 SSL 上下文相关联。 

您只能以编程方式设置信任管理器;不能通过管理控制台或在命令行上定义其用法。 

注意: 使用信任管理器可能会影响性能,具体取决于所执行的检查。 

清单 5-7 显示了 NulledTrustManager 示例的代码片断;完整的示例位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下的 NulledTrustManager.java 文件。SSLSocketClient 示例使用自定义信任管理器。SSLSocketClient 显示了如何通过使用带信任管理器的 SSL 上下文设置新的 SSL 连接。 

清单 5-7 NulledTrustManager 示例代码片断 
package examples.security.sslclient;import weblogic.security.SSL.TrustManager;import java.security.cert.X509Certificate;...public class NulledTrustManager implements TrustManager{  public boolean certificateCallback(X509Certificate[] o, int validateErr) {   System.out.println(" --- Do Not Use In Production ---\n" +                           " By using this NulledTrustManager, the trust in" +                            " the server's identity is completely lost.\n" +                           " --------------------------------");   for (int i=0; i<o.length; i++)     System.out.println(" certificate " + i + " -- " + o[i].toString());   return true;  }}使用证书路径信任管理器 
CertPathTrustManager weblogic.security.SSL.CertPathTrustManager 利用默认安全领域的已配置证书路径验证提供程序执行附加验证(例如撤销检查)。 

默认情况下,服务器中使用出站 SSL 的应用程序代码只能访问内置的 SSL 证书验证。但是,应用程序代码可以指定 CertPathTrustManager,以便访问管理员已经为服务器配置的任何其他证书验证。如果想要应用程序代码也能运行证书路径验证器,则应用程序代码应使用 CertPathTrustManager。 

有三种使用此类的方法: 

■仅当管理器已经在 SSLMBean 上设置了切换,指明出站 SSL 应该使用验证器时,信任管理器才会调用已配置的证书路径验证器。即应用程序将验证完全委托给管理员所配置的所有内容。可使用 setUseConfiguredSSLValidation() 方法来达到此目的。此项为默认设置。 
■信任管理始终调用任何已配置的证书路径验证器。可使用 setBuiltinSSLValidationAndCertPathValidators() 方法来达到此目的。
■信任管理从不调用任何已配置的证书路径验证器。可使用 setBuiltinSSLValidationOnly() 方法来达到此目的。 
使用握手完成监听器 
javax.net.ssl.HandshakeCompletedListener 接口定义了 SSL 客户端接收有关给定 SSL 连接上 SSL 协议握手完成的通知的方式。清单 5-8 显示了 MyListener 示例的代码片断;完整的示例位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录下的 MyListener.java 文件。 

清单 5-8 MyListener (HandshakeCompletedListener) 示例代码片断 
package examples.security.sslclient;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.FileInputStream;import javax.net.ssl.HandshakeCompletedListener;import java.util.Hashtable;import javax.net.ssl.SSLSession;...  public class MyListener implements HandshakeCompletedListener  {    public void handshakeCompleted(javax.net.ssl.HandshakeCompletedEvent                                          event)    {      SSLSession session = event.getSession();      System.out.println("Handshake Completed with peer " +                               session.getPeerHost());      System.out.println("   cipher: " + session.getCipherSuite());      Certificate[] certs = null;      try      {        certs = session.getPeerCertificates();      }      catch (SSLPeerUnverifiedException puv)      {        certs = null;      }      if  (certs != null)      {        System.out.println("   peer certificates:");        for (int z=0; z<certs.length; z++)                  System.out.println("      certs["+z+"]: " + certs[z]);      }      else      {        System.out.println("No peer certificates presented");      }    }  }使用 SSLContext 
SSLContext 类用于以编程方式配置 SSL,并保留 SSL 会话信息。每个实例都可以使用将用于执行身份验证的密钥、证书链和可信 CA 证书进行配置。使用同一 SSLContext 创建,并用于连接到同一 SSL 服务器的 SSL 套接口可能重用 SSL 会话信息。是否实际重用会话信息取决于 SSL 服务器。 

有关会话缓存的详细信息,请参阅“确保 WebLogic Server 安全”中的 SSL 会话行为。要将信任管理器类的实例与其 SSL 上下文相关联,请使用 weblogic.security.SSL.SSLContext.setTrustManager() 方法。 

只能以编程方式设置 SSL 上下文;不能使用管理控制台或命令行进行设置。Java new 表达式或 SSLContext 类的 getInstance() 方法可以创建 SSLContext 对象。getInstance() 方法是静态的,它可生成实现指定的安全套接口协议的新 SSLContext 对象。 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录中的 SSLSocketClient.java 示例提供了使用 SSLContext 类的示例。SSLSocketClient 示例显示了如何新建将使用 SSLContext 新建 SSL 套接口的 SSL 套接口工厂。 

清单 5-9 显示了使用 getInstance() 方法进行实例化的示例。 

清单 5-9 SSL 上下文代码示例 
import weblogic.security.SSL.SSLContext;  SSLcontext sslctx = SSLContext.getInstance ("https")使用 URL 执行出站 SSL 连接 
可以使用 URL 对象执行从充当客户端的某个 WebLogic Server 实例到另一个 WebLogic Server 实例的出站 SSL 连接。WebLogic Server 支持出站 SSL 连接的单向和双向 SSL 身份验证。 

对于单向 SSL 身份验证,可使用 java.net.URL, java.net.URLConnection 和 java.net.HTTPURLConnection 类通过使用 URL 对象进行出站 SSL 连接。清单 5-10 显示了同时支持 HTTP 和 HTTPS URL 且仅使用这些 Java 类(即不需要 WebLogic 类) simpleURL 的类。要在 WebLogic Server 上为单向 SSL 身份验证 (HTTPS) 使用 simpleURL 类,只需在系统属性中为 java.protocols.handler.pkgs 定义“weblogic.net”即可。 

注意: 因为清单 5-10 中显示的 simpleURL 示例默认会执行信任和主机名检查,所以此示例需要连接到默认情况下可信并且会传递主机名检查的真正 Web 服务器。否则,必须在命令行上替换信任和主机名检查。 

清单 5-10 仅使用 Java 类的单向 SSL 身份验证 URL 出站 SSL 连接类 
import java.net.URL;import java.net.URLConnection;import java.net.HttpURLConnection;import java.io.IOException;public class simpleURL{   public static void main (String [] argv)   {     if (argv.length != 1)     {       System.out.println("Please provide a URL to connect to");       System.exit(-1);     }     setupHandler();     connectToURL(argv[0]);   }   private static void setupHandler()   {     java.util.Properties p = System.getProperties();     String s = p.getProperty("java.protocol.handler.pkgs");     if (s == null)       s = "weblogic.net";     else if (s.indexOf("weblogic.net") == -1)       s += "|weblogic.net";     p.put("java.protocol.handler.pkgs", s);     System.setProperties(p);   }   private static void connectToURL(String theURLSpec)   {     try     {       URL theURL = new URL(theURLSpec);       URLConnection urlConnection = theURL.openConnection();       HttpURLConnection connection = null;       if (!(urlConnection instanceof HttpURLConnection))       {         System.out.println("The URL is not using HTTP/HTTPS: " +                              theURLSpec);         return;       }       connection = (HttpURLConnection) urlConnection;       connection.connect();       String responseStr = "\t\t" +              connection.getResponseCode() + " -- " +              connection.getResponseMessage() + "\n\t\t" +                   connection.getContent().getClass().getName() + "\n";       connection.disconnect();       System.out.println(responseStr);     }     catch (IOException ioe)     {       System.out.println("Failure processing URL: " + theURLSpec);       ioe.printStackTrace();     }   }}对于双向 SSL 身份验证,weblogic.net.http.HttpsURLConnection 类为客户端提供了一种指定安全上下文信息(包括客户端的数字证书和私钥)的方法。此类的实例表示到远程对象的 HTTPS 连接。 

SSLClient 示例代码演示了使用 WebLogic URL 对象执行出站 SSL 连接(请参阅清单 5-11)。清单 5-11 中显示的代码示例摘自 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录中的 SSLClient.java 文件。 

清单 5-11 WebLogic 双向 SSL 身份验证 URL 出站 SSL 连接代码示例 
wlsUrl = new URL("https", host, Integer.valueOf(sport).intValue(),                 query);weblogic.net.http.HttpsURLConnection sconnection =         new weblogic.net.http.HttpsURLConnection(wlsUrl);...InputStream [] ins = new InputStream[2];      ins[0] = new FileInputStream("clientkey.pem");      ins[1] = new FileInputStream("client2certs.pem");       String pwd = "clientkey";      sconnection.loadLocalIdentity(ins[0], ins[1], pwd.toCharArray()); 


--------------------------------------------------------------------------------

SSL 客户端代码示例 
完整工作的 SSL 身份验证示例与 WebLogic Server 产品一起提供。此示例位于 SAMPLES_HOME\server\examples\src\examples\security\sslclient 目录。有关此示例的描述,以及如何生成、配置和运行此示例的说明,请参阅示例目录中的 package.html 文件。可以修改此代码示例,然后重新使用。 


            
Contact BEA | Feedback | Privacy

Copyright © 2008, Oracle Corporation and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. 
https
Discursive  HomeCommon Java CookbookTraining
 Popular Blog Posts
2011-Jan-31
An Open Source "Fork" You Might Have Missed: slf4j and log4j


2011-Jan-26
Creating a Simple Google Guice Application


2011-Jan-27
How to Create Compelling Examples in Programming Books


2011-Mar-23
Maven Repositories: How Not to Treat End-users

 Search
       Login | Register  User login
Log in using OpenID:  What is OpenID?
Username or e-mail: *  
Password: *  
    
Log in using OpenID
Cancel OpenID login
Create new account
Request new password
 Home » Blogs » tobrien's blog  Tomcat 4.0.3, J2SE 1.4, and Programmatic SSL
ViewRelatedContent Submitted by tobrien on Sat, 06/01/2002 - 23:36

  Uncategorized 
LATEST DISCLAIMER: This document is ancient, it is (likely) irrelevant unless you are stuck with an old JRE and Tomcat. 

Update (10/24/02): I have recieved two emails since publishing this article from people who were in the same situation, the same *dire* and *hopeless* situation, who found this article helpful. James Cushing wrote me and stated that if you download Tomcat 4.1 LE then the situation outlined below should disappear - I have not tried this myself, but I trust it works.. Good luck to all and may all your deadlines be met. 

DISCLAIMER: This document is dated. The Jakarta group is a very quick development team and there is no telling when the information in this document will be obsolete. Keep the following in mind: This document refers to Tomcat 4.0.3, but may be relevant to all Tomcat 4.0 releases, and possibly all Tomcat 4.x releases. If you find the information in this document to be incorrect please email me and I will correct it accordingly. 
 
This article deals with what may be a rare situation, when an instance of Tomcat is configured to serve pages over SSL, and it must also perform programmatic SSL. If you are using the J2SE 1.4 with Tomcat 4.0.3 there are some issues with using the newly available javax.net.ssl classes ( you must use the legacy classes from JSSE 1.0.2 - com.sun.net.ssl ). This is not a problem per se, but my journey to find this answer was long and relying on software documentation from Sun proved to be my biggest mistake. I hope that this article may help someone who is caught in a similar situation. 

For the purposes of this article, assume that you have two instances of Tomcat 4.0.3 installed with J2SE 1.4. Both instances are configured to serve pages via HTTPS, and both instances need to communicate with each other using SSL. To make this example more concrete, let's assume that one of the Tomcat instances is configured to provide Stock Quotes over SSL, and another machine is configured to programmatically query this "feed". 

The problems and solutions discussed below are also relevant if you are developing a process that runs within the same JVM as Tomcat that needs to access any URL with an "https" scheme. This article and the solutions described may also be relevant to anyone who is trying to use SOAP over HTTPS from a Tomcat 4.0.3 instance, and the following arcticle will be especially relevant to anyone who wishes to perform automatic key or trust management with the interfaces provided by the JSSE. I'm assuming that the reader already has a basic understanding of Tomcat configuration. 

DISCLAIMER: This document is dated. The Jakarta group is a very quick development team and there is no telling when the information in this document will be obsolete. Keep the following in mind: This document refers to Tomcat 4.0.3, but may be relevant to all Tomcat 4.0 releases, and possibly all Tomcat 4.x releases. If you find the information in this document to be incorrect please email me and I will correct it accordingly. 
 
The Easy Part: Configuring an HTTPS Connector 
Configuring Tomcat to serve pages over SSL is straightforward, and the process described in the documentation that is available on the Jakarta website. There are a few caveats such as installing JSSE 1.0.2 if you are running J2SE 1.3, or running into some problems using the keytool utility that ships with the JDK. This article deals only with the J2SE 1.4 which includes the JSSE and JCE and already has protocol handlers configured in ${JAVA_HOME}/jre/lib./security.properties. 

Note: This article doesn't deal with J2SE 1.3, but I need to mention that JSSE 1.0.2 does not fully implement an https handler. If you start using JSSE 1.0.2 with J2SE 1.3 you may run into some of the problems I ran into. Response Codes were not visible from clients, and fully configuring JSSE 1.0.2 taxed my patience. I'm very lazy, so I punted on 1.3 and upgraded to 1.4 where JSSE is ready and available ( or so I had thought.... ) 

The process for configuring Tomcat to respond to HTTPS request is fully explained in the Jakarta documentation, and I do not wish to duplicate that document here. Please refer to the documentation that is available in the references section of this article for more detail. The process for configuring Tomcat 4.0 for SSL consists of two steps:

•Create a keystore using the keytool utilities which is included in the JDK. 
•Modify your server.xml file to enable the HTTPS connector. 
 
Q: What is your name?, A: www.discursive.com  
keytool utility asks a very misleading question. 


 
The logical answer to this question would be your name - "Joe Smith". The less than obvious answer to this question is "machine.domain.com". 

You must supply the fully qualified host name of the server, or you will need to write your own HostnameVerifier. If you do enter your name into this field, the system will still work when a browser meets your HTTPS connector for the first time. The user will be asked to trust the certificate from "Joe Smith". If you try to access this HTTPS connector from another Java program using JSSE, you will encounter exception when the HostnameVerifier rejects "Joe Smith" as an invalid hostname. The reason why Sun chose to ask you for your first name and last name is beyond me, but if you type in "Joe Smith", you will run into problems with Programmatic SSL. 
 
Coding the Client - Take 1 
At this point, we've got our Server set up to serve content over HTTPS, and we're going to write a component that periodically retrieves the stock quotes for Lucent and Cicso. Assume that the quote feed is available on the server in a context with a path of "/feed" and the URL to get both the Cisco and Lucent quotes is "https://server.domain.com:8443/feed/Stock?CSCO+LU". This component will run on the client instance of Tomcat and it will be a simple TimerTask object that we've created at startup. 

The system in question could consist of an arbitrary number of machines, and in order for these machines to communicate over SSL with no extra programming we are required to export certificates from the server's keystore and import these certificates into the client's truststore. In order to avoid a very costly configuration, we have decided to implement an X509TrustManager and HostnameVerifier to relax the requirements for certificates. This is a relatively straightforward process, and the JSSE documentation provides examples. Since we are using J2SE 1.4 the process is even easier than JSSE 1.0.2 w/ J2SE 1.3. 

The example is simplistic to make this article more readable - you don't want to blindly trust all certificates that are presented to a given client. You should at least check some of the properties of the X509Certificate such as the Issuer Distinguished Name or the Subject Distinguished Name. 

Sample HttpsTest.java 

package com.discursive.sample.tomcatssl;

import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import javax.net.ssl.*;

public class HttpsTest extends java.util.TimerTask {

  // We would never hardcode this literal in a real system,
  // this is only for this article.
  private String url = "https://server:8443/feed/Stock?CSCO+LU";

  // Create an anonymous class to trust all certificates.
  // This is bad style, you should create a separate class.
  private X509TrustManager xtm = new X509TrustManager() {
       public void checkClientTrusted(X509Certificate[] chain,
                                String authType) {}
       public void checkServerTrusted(X509Certificate[] chain,
                                String authType) {}
       public X509Certificate[] getAcceptedIssuers() {
    return null;
 }
    };

  // Create an class to trust all hosts
  private HostnameVerifier hnv = new HostnameVerifier() {
       public boolean verify(String hostname,
                             SSLSession session) {
    return true;
       }
    };

  // In this function we configure our system with a less stringent
  // hostname verifier and X509 trust manager.  This code is
  // executed once, and calls the static methods of HttpsURLConnection
  public HttpsTest() {
    // Initialize the TLS SSLContext with
    // our TrustManager
    SSLContext sslContext = null;
    try {
       sslContext = SSLContext.getInstance("TLS");
 X509TrustManager[] xtmArray = new X509TrustManager[] { xtm };
 sslContext.init( null,
                        xtmArray,
                        new java.security.SecureRandom() );
    } catch( GeneralSecurityException gse ) {
       // Print out some error message and deal with this exception...
    }

    // Set the default SocketFactory and HostnameVerifier
    // for javax.net.ssl.HttpsURLConnection
    if( sslContext != null ) {
       HttpsURLConnection.setDefaultSSLSocketFactory(
                   sslContext.getSocketFactory() );
    }
    HttpsURLConnection.setDefaultHostnameVerifier( hnv );
  }

  // This function is called periodically, the important thing
  // to note here is that there is no special code that needs to
  // be added to deal with a "HTTPS" URL.  All of the trust
  // management, verification, is handled by the HttpsURLConnection.
  public void run() {
    try {

        URLConnection urlCon = (new URL(url)).openConnection();
        urlCon.getInputStream();
        //  .... Whatever we want to do with these quotes...

    } catch( MalformedURLException mue ) {
        // Log an error message, and handle this exception...
    } catch( IOException ioe ) {
        // ditto
    }
  }
}
 This code looks good, the calls to HttpsURLConnection, setDefaultHostnameVerifier and setDefaultSSLSocketFactory look adequate to configure this relaxed trust. You've written the code and you are getting ready to run this program, you've thrown away your JSSE 1.0.2 documentation and you are working off the the JDK 1.4 JSSE documentation. Excited to have finally solved this problem you are surprised by the fact that....it doesn't work. Determined not to let the computer win, you try to get to the bottom of the problem by reading up on JSSE in J2SE 1.4 - this will get you nowhere. If you get really frustrated, you'll checkout the jakarta-tomcat-4.0 module from the Apache CVS repository, and you'll be one step closer to the real problem. 

What you need to know is that code internal to Catalina ( I think org.apache.catalina.net.SSLServerSocketFactory), adds the old security provider and overrides the protocol handler that is already configured in the J2SE 1.4. You've created the X509TrustManager and HostnameVerifier, and you've even set the default properties of the HttpsURLConnection, but the javax.net.ssl.HttpsURLConnection class is *never* used. Probe a little further, add some debugging and print out the class name of the URLConnection in your run() function. The URLConnection is not a javax.net.ssl.HttpsURLConnection at all, it is a com.sun.net.ssl.HttpsConnection. 
 
Tomcat 4.0.3 with J2SE 1.4: You need to use deprecated JSSE classes 
Ignore the code example above, you need to use the deprecated classes in the com.sun.net.ssl package. Your X509TrustManager needs to extend com.sun.net.ssl.X509TrustManager, and your HostnameVerifier needs to extend com.sun.net.ssl.HostnameVerifier. The classes themselves have different method signatures and you will need to deal with deprecation warnings at compilation time. 

Another option would be to force the URL class to use a different URLStreamHandler, but, if your situation is similar to mine, you already have tons of code that uses URL.openConnection() and you don't feel like changing every call to this function. I haven't tested this or pursued this option as it is not feasible in my current development environment. If you are currently working in a large development team, it is preferable to find a solution that works "behind the scenes". I've found that getting groups of programmers to adhere to standards or change their ways is a difficult task at best. 

Another option would be to alter the Catalina source code and use a customized version of the product that does not change the protocol handlers or add security providers. I did not pursue this option because I wanted my code to work with an unmodified Tomcat 4.0.3 download from Jakarta. 
 
Coding the Client - Take 2 
Adapting the client code to work in J2SE 1.4 using the deprecated JSSE 1.0.2 is easy. Just pretend that you are using JSSE 1.0.2 in J2SE 1.4. Two things need to be done: 

1. Change "import javax.net.ssl.*;" to "import com.sun.net.ssl.*;". 

   import java.io.*;
 import java.net.*;
 import java.security.*;
 import java.security.cert.*;
 import java.util.*;
 import com.sun.net.ssl.*;
 2. Modify the X509TrustManager and the HostnameVerifier to match the deprecated API. 

   // Create an anonymous class to trust all certificates.
 // This is bad style, you should create a separate class.
 private X509TrustManager xtm = new X509TrustManager() {
    public boolean isClientTrusted(X509Certificate[] chain) {
 return true;
    }
    public boolean isServerTrusted(X509Certificate[] chain) {
 return true;
    }
    public X509Certificate[] getAcceptedIssuers() {
 return null;
    }
 };

 // Create an class to trust all hosts
 private HostnameVerifier hnv = new HostnameVerifier() {
    public boolean verify(String hostname,
      String session) {
 return true;
    }
 };
 You should put the calls to HttpsURLConnection.setDefaultHostnameVerifier() and setDefaultSSLSocketFactory() during a startup class when the web application is loaded, but to keep it simple I put the code in the constructor of the TimerTask object. You could also run the code in the constructor from a static initializer in a class that gets loaded on startup, but static initializers are hard to debug and can lead to confusing error messages. The best place for this code is the init() function of a Servlet that has a element in the web.xml of that particular web context. 
 
Summary 
The above can be summarized in a few points: 


•If you need to perform any work with the JSSE API in Tomcat 4.0.3, you will need to pretend that the new JSSE in J2SE 1.4 is not available. Import and use JSSE 1.0.2 classes from com.sun.net.ssl. 
•This is all happening because when you register a protocol handler or a new security provider you are doing so by calling a static method on a class like Security. This may be out of line, but wouldn't it be more flexible if you could have different security contexts? This would be like having multiple ClassLoader objects with hierarchical relationships. This way you could have a context that still used the JSSE 1.0.2 API ( say code internal to Catalina ), and you could have your own code run in a separate context. This would allow you to mix different security providers and protocol handlers in larger systems. Instead, what happens is that one vendor's library could call Security.addProvider(Provider provider) out of the blue and change everything. ( I fully expect someone to tell me that this already exists, and that I haven't looked hard enough. ) 
•It helps to know what is going on. If you are using Tomcat please take the time to download the source code and understand what is happening. Also, it is clear that the Jakarta group needs more help writing documentation. Maybe some of us lazy users should lend a hand. 
•This is no one's fault, ( this is not even a "bug" ). The computer was doing exactly as it was told. 
 
Useful References 
Jakarta Tomcat 4.0 "SSL Configuration HOW-TO" 

JSSE Homepage 

JSSE ( bundled with J2SE 1.4 ) Reference Guide 

JSSE 1.0.2 Reference Guide 

Apache Axis, an implementation of SOAP 
 
 tobrien's blog     
Global site tag (gtag.js) - Google Analytics