eclipse实现SSL单/双向认证

来源:互联网 发布:js修改css样式 生效 编辑:程序博客网 时间:2024/06/04 18:14

SSL(Security Socket Layer)是广泛使用的一种通信加密方式。它主要利用PKI和数字证书来实现通信双方的身份认证以及通信数据的加密传输。SSL提供单向与双向两种认证模式。

双向认证:客户端和服务器之间向彼此发送自身身份证书从而实现通信双方的身份认证。但是客户端证书造价较高,且对于服务器而言在认证过程中不易获取,因此大多数应用都采用单项认证的方式。

单向认证:只认证服务器,服务器将身份证书发送给客户端,而不要求客户端发送自身身份证书。 将客户端的认证部署到应用层,大多以 用户名/密码 的形式完成。


1.基础知识

PKI:公钥基础设施,利用公钥加密技术为电子商务的开展提供一套安全基础平台的技术和规范。解决了对称加密中秘钥安全保存、传输的问题,采用非对称加密的方式,秘钥以 公钥/私钥 对的形式存在。公钥加密的信息只有与其对应的私钥才能解密,反之私钥加密公钥解密。
数字证书:数字证书作为身份凭证,起到保证远程计算机的身份以及向远程计算机证明客户端的身份的作用。主要包括如下图所示的信息:

每一个证书绑定一个唯一的公钥,拥有者将含有自身公钥的证书发送给对方,约定现阶段双方之间的通信信息利用此公钥进行加密,之后利用自身私钥对加密后的信息进行解密。

2.SSL单向认证

单向认证中,服务器发送自身证书给客户端来完成自身认证,客户端不必发送自身证书。因此在认证之前,需要先为服务器签发身份证书。
在java里,可以使用keytool命令来生成证书。但是这个证书还需要在客户端的受信列表里。这个不难理解,和取款类似,总得有一个受信任的凭证。因此还需将这个服务器证书加入到客户端存放信任的证书的仓库,这个证书仓库就是keystore。具体过程如下:【此处命令及java代码参考http://www.iteye.com/topic/1125183,有改动】

1.生成服务器证书

在命令行输入以下命令:
  1. keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123  
生成名为:bluedash-ssl-demo-server的服务器证书,保存在serverkeystore里,进入serverkeystore的密码是server,使用证书的密码是123123.
结果如图:

2.将服务器证书加入到客户端的keystore里。

此时需要先为客户端创建一个keystore,然后导入服务端证书。

2.1在命令行输入如下命令,为客户端创建证书及仓库。

  1. keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456  
结果如图所示:

2.2将创建的客户端证书从serverkeystore里导出

在命令行输入如下命令,导出服务器证书。

  1. keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer 

运行结果如图:【此处的口令为 server 】


2.3将导出的服务器证书加入到客户端的keystore里

在命令行输入如下命令,在clientkeystore中加入服务器证书:

  1. keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks

运行结果如图:【此处的口令为client 同时在最后一步要选择信任此服务器证书,输入 Y


【java实现】

至此,前期准备工作完成,可以开始服务器与客户端间的SSL会话,并且在此会话中实现服务器单向认证。参考【http://www.iteye.com/topic/1125183】中的代码,简单在eclipse中实现一个客户端与服务器间的SSL支持的socket通信,服务器与客户端之间发送hello消息。

1工程截图


2 SSLClient.java

【此处增加了对该SSL单向认证连接的多次运行的时间统计】
  1. package org.bluedash.tryssl1;  


    import java.io.BufferedReader;  
    import java.io.InputStreamReader;  
    import java.io.PrintWriter;  
    import java.net.Socket;  
      


    import javax.net.SocketFactory;  
    import javax.net.ssl.SSLSocketFactory;  
      
    public class SSLClient {  
        // the path of client_ks
        private static String CLIENT_KEY_STORE = "C:/Users/Vivian/client_ks";  
        //private static String CLIENT_KEY_STORE_PASSWORD = "456456";
        public static void main(String[] args) throws Exception {  
        //Run time statistics
  2.                   String str="";
     long starTime=System.currentTimeMillis();
     for(int i=0;i<50;i++){
      str=str+i;
     }
     
            // Set the key store to use for validating the server cert.  
            System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
              
            System.setProperty("javax.net.debug", "ssl,handshake");  
      
            SSLClient client = new SSLClient();  
            Socket s = client.clientWithoutCert();  
      
            PrintWriter writer = new PrintWriter(s.getOutputStream());  
            BufferedReader reader = new BufferedReader(new InputStreamReader(s  
                    .getInputStream()));  
            writer.println("hello");  
            writer.flush();  
            System.out.println(reader.readLine());  
            s.close();  
            
            long endTime=System.currentTimeMillis();
     long Time=endTime-starTime;
     System.out.println("程序运行时间:"+Time+ "ms");
           
        }  
      
        private Socket clientWithoutCert() throws Exception {  
            SocketFactory sf = SSLSocketFactory.getDefault();  
            Socket s = sf.createSocket("localhost", 8443);  
            return s;  
            
        } 
    }

3 SSLServer.java

  1. package org.bluedash.tryssl1;  


    import java.io.BufferedReader;  
    import java.io.FileInputStream;  
    import java.io.IOException;  
    import java.io.InputStreamReader;  
    import java.io.PrintWriter;  
    import java.net.ServerSocket;  
    import java.net.Socket;  
    import java.security.KeyStore;  
      




    import javax.net.ServerSocketFactory;  
    import javax.net.ssl.KeyManagerFactory;  
    import javax.net.ssl.SSLContext;  
    import javax.net.ssl.SSLServerSocket;  
      
    public class SSLServer extends Thread {  
        private Socket socket;  
      
        public SSLServer(Socket socket) {  
            this.socket = socket;  
        }  
      
        public void run() {  
            try {  
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
                PrintWriter writer = new PrintWriter(socket.getOutputStream());  
      
                String data = reader.readLine();  
                writer.println(data);  
                writer.close();  
                socket.close();  
            } catch (IOException e) {  
      
            }  
        }  
        private static String SERVER_KEY_STORE = "C:/Users/Vivian/server_ks";  
        private static String SERVER_KEY_STORE_PASSWORD = "123123";  
        public static void main(String[] args) throws Exception {  
         String str="";
      long starTime=System.currentTimeMillis();
      for(int i=0;i<1;i++){
       str=str+i;
      }
            System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);  
            SSLContext context = SSLContext.getInstance("TLS");  
              
            KeyStore ks = KeyStore.getInstance("jceks");  
            ks.load(new FileInputStream(SERVER_KEY_STORE), null);  
            KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
            kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());  
              
            context.init(kf.getKeyManagers(), null, null);  
      
            ServerSocketFactory factory = context.getServerSocketFactory();  
            ServerSocket _socket = factory.createServerSocket(8443);  
            ((SSLServerSocket) _socket).setNeedClientAuth(false);  
      
            while (true) {  
                new SSLServer(_socket.accept()).start();  
                long endTime=System.currentTimeMillis();
         long Time=endTime-starTime;
         System.out.println("程序运行时间:"+Time+ "ms");
            }  
        }  



4运行结果

【具体的SSL握手信息见下篇】

3.SSL双向认证

3.1 SSL双向认证和单向认证相比,多了对客户端身份的认证,故需要做如下改动:

①将SSLServer.java中的
  1. ((SSLServerSocket) _socket).setNeedClientAuth(false);  
改为:
  1. ((SSLServerSocket) _socket).setNeedClientAuth(true);  

②与单向认证中证书的操作类似,需要将生成的客户端证书加入到服务器的keystore中,以上已经分别为server和client生成了证书和keystore,此时还需完成的是将客户端证书导出并加入到serverkeystore中。

3.2 在命令行输入如下命令,导出客户端证书并加入到serverkeystore中。



【java实现】

1工程截图


2 SSLClient.java

  1. package examples.ssll;
    import java.io.BufferedReader;  
    import java.io.FileInputStream;  
    import java.io.InputStreamReader;  
    import java.io.PrintWriter;  
    import java.net.Socket;  
    import java.security.KeyStore;  


    import javax.net.SocketFactory;  
    import javax.net.ssl.KeyManagerFactory;  
    import javax.net.ssl.SSLContext;  
    import javax.net.ssl.SSLSocketFactory;  
      
    public class SSLClient {  
        private static String CLIENT_KEY_STORE = "C:/Users/Vivian/client_ks";  
        private static String CLIENT_KEY_STORE_PASSWORD = "456456";  
          
        public static void main(String[] args) throws Exception {  
            // Set the key store to use for validating the server cert.  
        String str="";
     long starTime=System.currentTimeMillis();
     //计算循环10000的时间
     for(int i=0;i<50;i++){
      str=str+i;
     }
     
        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
            System.setProperty("javax.net.debug", "ssl,handshake");  
            SSLClient client = new SSLClient();  
            Socket s = client.clientWithCert();  
              
            PrintWriter writer = new PrintWriter(s.getOutputStream());  
            BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
            writer.println("hello");  
            writer.flush();  
            System.out.println(reader.readLine());  
            s.close(); 
            
            long endTime=System.currentTimeMillis();
     long Time=endTime-starTime;
     System.out.println("程序运行时间:"+Time+ "ms");
        }  
      
        private Socket clientWithoutCert() throws Exception {  
            SocketFactory sf = SSLSocketFactory.getDefault();  
            Socket s = sf.createSocket("localhost", 8443);  
            return s;  
        }  
      
        private Socket clientWithCert() throws Exception {  
            SSLContext context = SSLContext.getInstance("TLS");  
            KeyStore ks = KeyStore.getInstance("jceks");  
              
            ks.load(new FileInputStream(CLIENT_KEY_STORE), null);  
            KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
            kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());  
            context.init(kf.getKeyManagers(), null, null);  
              
            SocketFactory factory = context.getSocketFactory();  
            Socket s = factory.createSocket("localhost", 8443);  
            return s;  
        }  
    }  

3 SSLServer.java

  1. package org.bluedash.tryssl1;  




    import java.io.BufferedReader;  
    import java.io.FileInputStream;  
    import java.io.IOException;  
    import java.io.InputStreamReader;  
    import java.io.PrintWriter;  
    import java.net.ServerSocket;  
    import java.net.Socket;  
    import java.security.KeyStore;  
      








    import javax.net.ServerSocketFactory;  
    import javax.net.ssl.KeyManagerFactory;  
    import javax.net.ssl.SSLContext;  
    import javax.net.ssl.SSLServerSocket;  
      
    public class SSLServer extends Thread {  
        private Socket socket;  
      
        public SSLServer(Socket socket) {  
            this.socket = socket;  
        }  
      
        public void run() {  
            try {  
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
                PrintWriter writer = new PrintWriter(socket.getOutputStream());  
      
                String data = reader.readLine();  
                writer.println(data);  
                writer.close();  
                socket.close();  
            } catch (IOException e) {  
      
            }  
        }  
        private static String SERVER_KEY_STORE = "C:/Users/Vivian/server_ks";  
        private static String SERVER_KEY_STORE_PASSWORD = "123123";  
        public static void main(String[] args) throws Exception {  
        String str="";
     long starTime=System.currentTimeMillis();
     for(int i=0;i<1;i++){
      str=str+i;
     }
            System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);  
            SSLContext context = SSLContext.getInstance("TLS");  
              
            KeyStore ks = KeyStore.getInstance("jceks");  
            ks.load(new FileInputStream(SERVER_KEY_STORE), null);  
            KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
            kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());  
              
            context.init(kf.getKeyManagers(), null, null);  
      
            ServerSocketFactory factory = context.getServerSocketFactory();  
            ServerSocket _socket = factory.createServerSocket(8443);  
            ((SSLServerSocket) _socket).setNeedClientAuth(false);  
      
            while (true) {  
                new SSLServer(_socket.accept()).start();  
                long endTime=System.currentTimeMillis();
       long Time=endTime-starTime;
       System.out.println("程序运行时间:"+Time+ "ms");
            }  
        }  


4运行结果

【具体的SSL握手信息见下篇】

4.总结

4.1证书与keystore

这四个文件的存储默认存储位置如下:

可以用如下命令来自定义存储位置:

  1. keytool -genkey -alias svrkey -keyalg RSA -keystore d:\svr.jks -validity 365

4.2认证

整个实现过程并不复杂,一是生成证书,二是将证书导入到通信双方彼此的证书仓库中去,实现相互信任。在之后的SSL通信中完成单向/双向认证。
代码实现也是以基本的socket通信为基础,加入SSL的相关通信配置。
0 0
原创粉丝点击