Java 安全套接字编程以及 keytool 使用最佳实践(三)

2014-11-24 07:42:52 · 作者: · 浏览: 2
private static final int DEFAULT_PORT=54321; private static final String DEFAULT_HOST="localhost"; public static void main(String args[]){ SSLSocket socket=null; // 使用默认的方式获取工厂实例 SSLSocketFactory sf=(SSLSocketFactory)SSLSocketFactory.getDefault(); try{ // 连接服务端的端口,完成握手过程 socket=(SSLSocket)sf.createSocket(DEFAULT_HOST, DEFAULT_PORT); socket.startHandshake(); System.out.println("Connected to "+DEFAULT_HOST+":"+DEFAULT_PORT+" !"); // 从控制台输入要发送给服务端的文字 BufferedReader reader=new BufferedReader(new InputStreamReader(System.in)); Writer writer=new OutputStreamWriter(socket.getOutputStream()); // 可以反复向服务端发送消息 boolean done=false; while (!done) { System.out.print("Send Message: "); String line=reader.readLine(); if (line!=null) { writer.write(line+"\n"); writer.flush(); }else{ done=true; } } socket.close(); }catch (Exception e) { System.out.println("Connection failed: "+e); try{ socket.close(); }catch(IOException ioe){} socket=null; } } }

通过 SSLContext 指定证书库和信任库

前文描述的,通过系统参数指定证书库和信任库的方法,虽然简单易用,但是缺点也是显而易见的,整个程序的环境都得使用同样的 jks 文件。如果程序里有不同的 SSL/TSL 通信,则需要使用不同的 jks 文件,该怎么做呢?

可以使用 SSLContext 来指定 jks 文件,只需要把清单 3 的代码片段替换到清单 1 的“SSLServerSocketFactory ssf”生成处;把清单 4 的代码片段替换到清单 2 的“SSLSocketFactory sf”生成处,再稍作代码调整即可。

(注:实际上,在使用 SSLSocketFactory.getDefault() 或者 SSLServerSocketFactory.getDefault() 创建套接字的时候,程序内部已经使用了默认的 context,其参数就是通过系统属性指定的 )

清单 3. SSLContext 指定证书库
 // 相关的 jks 文件及其密码定义
 private final static String CERT_STORE="D:/test_server_cert.jks"; 
 private final static String CERT_STORE_PASSWORD="Testpassw0rd";
 // 载入 jks 文件
 FileInputStream f_certStore=new FileInputStream(CERT_STORE); 
 KeyStore ks=KeyStore.getInstance("jks"); 
 ks.load(f_certStore, CERT_STORE_PASSWORD.toCharArray()); 
 f_certStore.close(); 

 // 创建并初始化证书库工厂
 String alg=KeyManagerFactory.getDefaultAlgorithm(); 
 KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg); 
 kmFact.init(ks, CERT_STORE_PASSWORD.toCharArray()); 

 KeyManager[] kms=kmFact.getKeyManagers(); 

 // 创建并初始化 SSLContext 实例
 SSLContext context=SSLContext.getInstance("SSL"); 
 context.init(kms, null, null); 
 SSLServerSocketFactory ssf=(SSLServerSocketFactory)context.getServerSocketFactory();
清单 4. SSLContext 指定信任库
 // 相关的 jks 文件及其密码定义
 private final static String TRUST_STORE="D:/test_client_trust.jks"; 
 private final static String TRUST_STORE_PASSWORD="Testpassw0rd";

// 载入 jks 文件
 FileInputStream f_trustStore=new FileInputStream(TRUST_STORE); 
 KeyStore ks=KeyStore.getInstance("jks"); 
 ks.load(f_trustStore, TRUST_STORE_PASSWORD.toCharArray()); 
 f_trustStore.close(); 

 // 创建并初始化信任库工厂
 String alg=TrustManagerFactory.getDefaultAlgorithm(); 
 TrustManagerFactory tmFact=TrustManagerFactory.getInstance(alg); 
 tmFact.init(ks); 

 TrustManager[] tms=tmFact.getTrustManagers(); 

 // 创建并初始化 SSLContext 实例
 SSLContext context=SSLContext.getInstance("SSL"); 
 context.init(null, tms, null); 
 SSLSocketFactory sf=context.getSocketFactory();

当然,如果要在服务端或者客户端同时使用证书库和信任库,可将清单 3 和清单 4 的代码用在同一处 context.init() 的地方。

这里需要说明的是,如果 context.init() 的第一个 KeyManager[] 参数为 null,则表示不提供证书;如果第二个 TrustManager[] 参数为 null,则会寻找系统默认提供的信任库 ( 例如:JRE 安装目录下的 lib/security/cacerts)。

使用 X509 证书信任管理器

X509TrustManager 接口扩展了 TrustManager 接口,使用 TrustManager 接口,我们已经可以在程序中自