>>分享孙卫琴的Java技术专稿和著作 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 24792 个阅读者 刷新本主题
 * 贴子主题:  【Java网络编程专题】创建基于SSL的安全服务器和安全客户的范例 回复文章 点赞(0)  收藏  
作者:sunweiqin    发表时间:2019-12-23 10:44:49     消息  查看  搜索  好友  邮件  复制  引用

本文参考《Java网络编程核心技术详解》,作者:孙卫琴,电子工业出版社出版
源代码下载地址为:http://www.javathinker.net/kecheng/javanet/javanetsourcecode.rar

以下EchoServer类创建了一个基于SSL的安全服务器,它处于服务器模式。
/* EchoServer.java*/
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import java.security.*;
public class EchoServer {
  private int port=8000;
  private SSLServerSocket serverSocket;

  public EchoServer() throws Exception {
    //输出跟踪日志
    //System.setProperty("javax.net.debug", "all");
    SSLContext context=createSSLContext();
    SSLServerSocketFactory factory=context.getServerSocketFactory();
    serverSocket =(SSLServerSocket)factory.createServerSocket(port);
    System.out.println("服务器启动");
    System.out.println(
               serverSocket.getUseClientMode()? "客户模式":"服务器模式");
    System.out.println(serverSocket.getNeedClientAuth()?
             "需要验证对方身份":"不需要验证对方身份");
    
    String[] supported=serverSocket.getSupportedCipherSuites();
    serverSocket.setEnabledCipherSuites(supported);
  }
  
  
  public SSLContext createSSLContext() throws Exception {
    //服务器用于证实自己身份的安全证书所在的密钥库
    String keyStoreFile = "test.keystore";
    String passphrase = "123456";
    KeyStore ks = KeyStore.getInstance("JKS");
    char[] password = passphrase.toCharArray();
    ks.load(new FileInputStream(keyStoreFile), password);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    kmf.init(ks, password);

    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(kmf.getKeyManagers(), null, null);

    //当要求客户端提供安全证书时,服务器端可创建TrustManagerFactory,
    //并由它创建TrustManager,TrustManger根据与之关联的KeyStore中的信息,
    //来决定是否相信客户提供的安全证书。

    //客户端用于证实自己身份的安全证书所在的密钥库
    //String trustStoreFile = "test.keystore";  
    //KeyStore ts = KeyStore.getInstance("JKS");
    //ts.load(new FileInputStream(trustStoreFile), password);
    //TrustManagerFactory tmf =
    //    TrustManagerFactory.getInstance("SunX509");
    //tmf.init(ts);
    //sslContext.init(kmf.getKeyManagers(),
    //                 tmf.getTrustManagers(), null);
    
    return sslContext;
  }

  public String echo(String msg) {
    return "echo:" + msg;
  }

  private PrintWriter getWriter(Socket socket)throws IOException{
    OutputStream socketOut = socket.getOutputStream();
    return new PrintWriter(socketOut,true);
  }
  private BufferedReader getReader(Socket socket)throws IOException{
    InputStream socketIn = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(socketIn));
  }

  public void service() {
    while (true) {
      Socket socket=null;
      try {
        socket = serverSocket.accept();  //等待客户连接
        System.out.println("New connection accepted "
                        +socket.getInetAddress()
                       + ":" +socket.getPort());
        BufferedReader br =getReader(socket);
        PrintWriter pw = getWriter(socket);

        String msg = null;
        while ((msg = br.readLine()) != null) {
          System.out.println(msg);
          pw.println(echo(msg));
          if (msg.equals("bye")) //如果客户发送的消息为“bye”,就结束通信
            break;
        }
      }catch (IOException e) {
         e.printStackTrace();
      }finally {
         try{
           if(socket!=null)socket.close();  //断开连接
         }catch (IOException e) {e.printStackTrace();}
      }
    }
  }

  public static void main(String args[])throws Exception {
    new EchoServer().service();
  }
}

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


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

打印结果为:


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

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

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

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


/* EchoClient.java */
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import java.security.*;
public class EchoClient {
  private String host="localhost";
  private int port=8000;
  private SSLSocket socket;
  
  public EchoClient()throws IOException{
    SSLContext context=createSSLContext();
    SSLSocketFactory factory=context.getSocketFactory();
    socket=(SSLSocket)factory.createSocket(host,port);
    String[] supported=socket.getSupportedCipherSuites();
    socket.setEnabledCipherSuites(supported);
    System.out.println(socket.getUseClientMode()?
                           "客户模式":"服务器模式");
  }

  public SSLContext createSSLContext() throws Exception {
    String passphrase = "123456";
    char[] password = passphrase.toCharArray();

    //设置客户端所信任的安全证书所在的密钥库
    String trustStoreFile = "test.keystore";    
    KeyStore ts = KeyStore.getInstance("JKS");
    ts.load(new FileInputStream(trustStoreFile), password);
    TrustManagerFactory tmf =
                 TrustManagerFactory.getInstance("SunX509");
    tmf.init(ts);

    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null,tmf.getTrustManagers(), null);
    return sslContext;
  }
  public static void main(String args[])throws IOException{
    new EchoClient().talk();
  }
  private PrintWriter getWriter(Socket socket)throws IOException{
    OutputStream socketOut = socket.getOutputStream();
    return new PrintWriter(socketOut,true);
  }
  private BufferedReader getReader(Socket socket)throws IOException{
    InputStream socketIn = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(socketIn));
  }
  public void talk()throws IOException {
    try{
      BufferedReader br=getReader(socket);
      PrintWriter pw=getWriter(socket);
      BufferedReader localReader=
          new BufferedReader(new InputStreamReader(System.in));
      String msg=null;
      while((msg=localReader.readLine())!=null){
        pw.println(msg);
        System.out.println(br.readLine());

        if(msg.equals("bye"))
          break;
      }
    }catch(IOException e){
       e.printStackTrace();
    }finally{
       try{socket.close();}catch(IOException e){e.printStackTrace();}
    }
  }
}

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


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

打印结果为:


客户模式

由此可见,默认情况下,由SSLSocketFactory创建的SSLSocket对象处于客户模式,不必向对方证实自身的身份。
EchoClient类依靠TrustManager来决定是否信任EchoServer出示的安全证书。EchoClient类的SSLSocketFactory对象是由SSLContext对象来创建的。这个SSLContext对象通过TrustManager来管理所信任的安全证书。在本例中,TrustManager所信任的安全证书位于test.keystore密钥库文件中。

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

假定该文件与EchoServer.class以及EchoClient.class文件位于同一目录下。在DOS命令行中转到范例所在的chapter15目录下,按照以下步骤运行EchoServer和EchoClient:
(1)设置classpath,运行命令“set classpath=C:\chapter15\classes”。
(2)运行“start java EchoServer”命令,启动EchoServer服务器。
(3)运行“ java  EchoClient”命令,启动EchoClient客户。



程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 admin 在 2021-10-09 10:54:28 重新编辑]
  Java面向对象编程-->接口
  JavaWeb开发-->Servlet技术详解(Ⅲ)
  JSP与Hibernate开发-->使用JPA和注解
  Java网络编程-->客户端协议处理框架
  精通Spring-->CSS过渡和动画
  Vue3开发-->创建综合购物网站应用
  【Vue.js技术专题】Vue组件的单向数据流
  【Vue.js技术专题】插槽slot的基本用法
  【Spring Cloud Alibaba专题】GateWay的内置断言工厂
  【Spring Cloud Alibaba专题】SkyWalking整合MySQL
  【Spring Cloud Alibaba专题】ShardingSphere核心概念
  【Spring Cloud Alibaba专题】Nacos集群的Raft算法
  【Spring Cloud Alibaba专题】Nacos配置属性的持久化
  【Spring专题】服务器端推送
  【Spring专题】@Query和@Modifying注解的增删改操作
  从《精通Spring》和《精通Vue.js》的写作分享学习新技术的经...
  【持久化专题】FetchType.LAZY延迟检索策略
  【持久化专题】Spring与Hibernate与JPA的整合
  【Java网络编程专题】用Apache HttpClients下载网上的图片等...
  【JavaWeb专题】Tomcat与IIS集成详解
  我的计算机书籍创作心得
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。