您的位置 首页 java

Java网络编程进阶:通过JSSE创建安全的数据通信

Java网络编程进阶:通过JSSE创建安全的数据通信

小编说:本文作者孙卫琴,知名IT作家和Java专家。本文将通过一个范例向大家介绍JSSE是如何实现安全的网络通信的。


在网络上,信息在由源主机到目标主机的传输过程中 会经过其他计算机。 一般情况下,中间的计算机不会监听路过的信息。但在使用网上银行或者进行信用卡交易时,网络上的信息有可能被非法分子监听,从而导致个人隐私的泄露。

Java网络编程进阶:通过JSSE创建安全的数据通信

由于Internet和Intranet体系结构存在一些安全漏洞,总有某些人能够截获并替换用户发出的原始信息。

随着电子商务的不断发展,人们对信息安全的要求越来越高,于是Netscape公司提出了 SSL协议 ,目的为了能 在开放网络上安全保密地传输信息

Java网络编程进阶:通过JSSE创建安全的数据通信

Java安全套接字扩展 (JSSE,Java Secure Socket Extension)为基于SSL和TLS协议的Java网络应用程序提供了Java API以及参考实现。

JSSE支持数据加密、服务器端身份验证、数据完整性以及可选的客户端身份验证。使用JSSE,能保证采用各种应用层协议(比如HTTP、Telnet和FTP等)的客户程序与服务器程序安全地交换数据。

Java网络编程进阶:通过JSSE创建安全的数据通信

JSSE封装了底层复杂的安全通信细节,使得开发人员能方便地利用它来开发安全的网络应用程序。

下文参考了 《Java网络编程核心技术详解》 一书的第15章,将结合具体范例来向大家介绍JSSE的用法。


JSSE API 简介

JSSE封装了底层复杂的安全通信细节,使得开发人员能方便地用它来开发安全的网络应用程序。JSSE主要包括四个包:

  • javax.net.ssl包: 包括进行安全通信的类,比如SSLServerSocket和SSLSocket类。
  • javax.net包: 包括安全套接字的工厂类,比如SSLServerSocketFactory和SSLSocketFactory类。
  • java.security.cert包: 包括处理安全证书的类,如X509Certificate类。X.509是由国际电信联盟(ITU-T)制定的安全证书的标准。
  • com.sun.net.ssl包: 包括Oracle公司提供的JSSE的实现类。

JSSE具有以下重要特征:

  • 纯粹用Java语言编写。
  • 可以出口到大多数国家。
  • 提供了支持SSL的JSSE API和JSSE实现。
  • 提供了支持TLS的JSSE API和JSSE实现。
  • 提供了用于创建安全连接的类,如SSLSocket、 SSLServerSocket 和 SSLEngine。
  • 支持加密通信。
  • 支持客户端和服务器端的身份验证。
  • 支持SSL会话。
  • JSSE的具体实现会支持一些常用的加密算法,比如RSA(加密长度2048位)、RC4(密钥长度128位)和DH(密钥长度1024位)。

下面展示了JSSE API的主要类框图。

Java网络编程进阶:通过JSSE创建安全的数据通信

JSSE中负责安全通信的最核心的类是 SSLServerSocket 类与SSLSocket 类,它们分别是 ServerSocket 与 Socket 类的子类。SSLSocket 对象由 SSLSocketFactory 创建,此外, SSLServerSocket 的 accept() 方法也会创建 SSLSocket。SSLServerSocket 对象由 SSLServerSocketFactory 创建。SSLSocketFactory 、 SSLServerSocketFactory 以及 SSLEngine 对象都由 SSLContext 对象创建。SSLEngine 类用于支持非阻塞的安全通信。

创建安全服务器

以下EchoServer类创建了一个基于SSL的安全服务器,它处于服务器模式。

   1/* EchoServer.java*/  2import java.net.*;
  3import java.io.*;
  4import javax.net.ssl.*;
  5import java.security.*;
  6
  7public class EchoServer {
  8  private int port=8000;
  9  private SSLServerSocket serverSocket;
 10
 11  public EchoServer() throws Exception {
 12    //输出跟踪日志
 13    //System.setProperty("javax.net.debug", "all");
 14    SSLContext context=createSSLContext();
 15    SSLServerSocketFactory factory=context.getServerSocketFactory();
 16    serverSocket =(SSLServerSocket)factory.createServerSocket(port);
 17    System.out.println("服务器启动");
 18    System.out.println(
 19               serverSocket.getUseClientMode()? "客户模式":"服务器模式");
 20    System.out.println(serverSocket.getNeedClientAuth()?
 21             "需要验证对方身份":"不需要验证对方身份");
 22
 23    String[] supported=serverSocket.getSupportedCipherSuites();
 24    serverSocket.setEnabledCipherSuites(supported);
 25  }
 26
 27  public SSLContext createSSLContext() throws Exception {
 28    //服务器用于证实自己身份的安全证书所在的密钥库
 29    String keyStoreFile = "test.keystore";
 30    String passphrase = "123456";
 31    KeyStore ks = KeyStore.getInstance("JKS");
 32    char[] password = passphrase.toCharArray();
 33    ks.load(new FileInputStream(keyStoreFile), password);
 34    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 35    kmf.init(ks, password);
 36
 37    SSLContext sslContext = SSLContext.getInstance("SSL");
 38    sslContext.init(kmf.getKeyManagers(), null, null);
 39
 40    //当要求客户端提供安全证书时,服务器端可创建TrustManagerFactory,
 41    //并由它创建TrustManager,TrustManger根据与之关联的KeyStore中的信息,
 42    //来决定是否相信客户提供的安全证书。
 43
 44    //客户端用于证实自己身份的安全证书所在的密钥库
 45    //String trustStoreFile = "test.keystore";  
 46    //KeyStore ts = KeyStore.getInstance("JKS");
 47    //ts.load(new FileInputStream(trustStoreFile), password);
 48    //TrustManagerFactory tmf =
 49    //    TrustManagerFactory.getInstance("SunX509");
 50    //tmf.init(ts);
 51    //sslContext.init(kmf.getKeyManagers(),
 52    //                 tmf.getTrustManagers(), null);
 53
 54    return sslContext;
 55  }
 56
 57  public String echo(String msg) {
 58    return "echo:" + msg;
 59  }
 60
 61  private PrintWriter getWriter(Socket socket)throws IOException{
 62    OutputStream socketOut = socket.getOutputStream();
 63    return new PrintWriter(socketOut,true);
 64  }
 65  private BufferedReader getReader(Socket socket)throws IOException{
 66    InputStream socketIn = socket.getInputStream();
 67    return new BufferedReader(new InputStreamReader(socketIn));
 68  }
 69
 70  public void service() {
 71    while (true) {
 72      Socket socket=null;
 73      try {
 74        socket = serverSocket.accept();  //等待客户连接
 75        System.out.println("New connection accepted "
 76                        +socket.getInetAddress()
 77                       + ":" +socket.getPort());
 78        BufferedReader br =getReader(socket);
 79        PrintWriter pw = getWriter(socket);
 80
 81        String msg = null;
 82        while ((msg = br.readLine()) != null) {
 83          System.out.println(msg);
 84          pw.println(echo(msg));
 85          if (msg.equals("bye")) //如果客户发送的消息为“bye”,就结束通信
 86            break;
 87        }
 88      }catch (IOException e) {
 89         e.printStackTrace();
 90      }finally {
 91         try{
 92           if(socket!=null)socket.close();  //断开连接
 93         }catch (IOException e) {e.printStackTrace();}
 94      }
 95    }
 96  }
 97
 98  public static void main(String args[])throws Exception {
 99    new EchoServer().service();
100  }
101}  

以上EchoServer类先创建了SSLContext对象,然后由它创建SSLServerSocketFactory对象,再由该工厂对象创建SSLServerSocket对象。对于以下程序代码:

 1System.out.println(serverSocket.getUseClientMode()?
2               "客户模式":"服务器模式");
3System.out.println(serverSocket.getNeedClientAuth()?
4               "需要验证对方身份":"不需要需要验证对方身份");
  

打印结果为:

 1服务器模式
2不需要验证对方身份
  

由此可见,默认情况下,SSLServerSocket处于服务器模式,必须向对方证实自身的身份,但不需要验证对方的身份,即不要求对方出示安全证书。

如果希望程序运行时输出底层JSSE实现的日志信息,可以把“javax.net.debug”系统属性设为“all”:

 1System.setProperty("javax.net.debug", "all");
  

创建安全客户程序

以下EchoClient类创建了一个基于SSL的安全客户,它处于客户模式

  1/* EchoClient.java */ 2import java.net.*;
 3import java.io.*;
 4import javax.net.ssl.*;
 5import java.security.*;
 6
 7public class EchoClient {
 8  private String host="localhost";
 9  private int port=8000;
10  private SSLSocket socket;
11
12  public EchoClient()throws IOException{
13    SSLContext context=createSSLContext();
14    SSLSocketFactory factory=context.getSocketFactory();
15    socket=(SSLSocket)factory.createSocket(host,port);
16    String[] supported=socket.getSupportedCipherSuites();
17    socket.setEnabledCipherSuites(supported);
18    System.out.println(socket.getUseClientMode()?
19                           "客户模式":"服务器模式");
20  }
21
22  public SSLContext createSSLContext() throws Exception {
23    String passphrase = "123456";
24    char[] password = passphrase.toCharArray();
25
26    //设置客户端所信任的安全证书所在的密钥库
27    String trustStoreFile = "test.keystore";    
28    KeyStore ts = KeyStore.getInstance("JKS");
29    ts.load(new FileInputStream(trustStoreFile), password);
30    TrustManagerFactory tmf =
31                 TrustManagerFactory.getInstance("SunX509");
32    tmf.init(ts);
33
34    SSLContext sslContext = SSLContext.getInstance("SSL");
35    sslContext.init(null,tmf.getTrustManagers(), null);
36    return sslContext;
37  }
38  public static void main(String args[])throws IOException{
39    new EchoClient().talk();
40  }
41  private PrintWriter getWriter(Socket socket)throws IOException{
42    OutputStream socketOut = socket.getOutputStream();
43    return new PrintWriter(socketOut,true);
44  }
45  private BufferedReader getReader(Socket socket)throws IOException{
46    InputStream socketIn = socket.getInputStream();
47    return new BufferedReader(new InputStreamReader(socketIn));
48  }
49  public void talk()throws IOException {
50    try{
51      BufferedReader br=getReader(socket);
52      PrintWriter pw=getWriter(socket);
53      BufferedReader localReader=
54          new BufferedReader(new InputStreamReader(System.in));
55      String msg=null;
56      while((msg=localReader.readLine())!=null){
57        pw.println(msg);
58        System.out.println(br.readLine());
59
60        if(msg.equals("bye"))
61          break;
62      }
63    }catch(IOException e){
64       e.printStackTrace();
65    }finally{
66       try{socket.close();}catch(IOException e){e.printStackTrace();}
67    }
68  }
69}
  

以上EchoClient类先创建了一个SSLSocketFactory对象,然后由它创建了SSLSocket对象。对于以下程序代码:

 1System.out.println(socket.getUseClientMode()?"客户模式":"服务器模式");
  

打印结果为:

 1客户模式
  

由此可见,默认情况下,由SSLSocketFactory创建的SSLSocket对象处于客户模式,不必向对方证实自身的身份。

EchoClient类依靠TrustManager来决定是否信任EchoServer出示的安全证书。EchoClient类的SSLSocketFactory对象是由SSLContext对象来创建的。这个SSLContext对象通过TrustManager来管理所信任的安全证书。在本例中,TrustManager所信任的安全证书位于test.keystore密钥库文件中。

在本例中,服务器端向客户端出示的安全证书位于 test.keystore 密钥库文件中。在实际应用中,服务器端的密钥库文件中包含密钥对,从安全角度出发,客户端所信任的密钥库文件中应该仅仅包含公钥,所以服务器和客户端应该使用不同的密钥库文件。

Java网络编程进阶:通过JSSE创建安全的数据通信

在 IT 行业,大多数 Java 程序员都看过孙卫琴老师的书。

孙老师的书,清晰严谨,把复杂的技术架构层层剖析,结合典型的实例细致讲解,读者只要静下心来好好品读,就能深入 Java 技术的殿堂!

如今,Java 在网络应用开发领域得到了非常广泛的运用,孙卫琴老师新书上市之际,博文视点学院联合孙老师共同打造 技术视频课《Java网络编程核心技术详解》 (含同名新书一本)!

Java网络编程进阶:通过JSSE创建安全的数据通信

通过课程+图书的学习,不仅可以帮助你掌握网络编程的实用技术,还可以进一步提高按照面向对象的思想来设计和开发Java软件的能力!

Java网络编程进阶:通过JSSE创建安全的数据通信

热文推荐

  • 声纹技术:让智能语音助手真正“认得”自己
  • 为什么人人都需要懂一点高阶(中台)产品思维
  • 苏杰:如果可以重来,你还会做工作狂么?
  • 超详细丨完整的推荐系统架构设计

文章来源:智云一二三科技

文章标题:Java网络编程进阶:通过JSSE创建安全的数据通信

文章地址:https://www.zhihuclub.com/178763.shtml

关于作者: 智云科技

热门文章

网站地图