java安全(八)终极装备——HTTPS协议之SSL/TLS协议

来源:互联网 发布:四十部网络禁书名单 编辑:程序博客网 时间:2024/05/14 10:31

SSL/TLS协议我的理解就是客户端(比如浏览器)和服务器端(比如tomcat)商量他们之间数据传输用的对称加密的算法和秘钥,最后用这个算法和秘钥进行数据加密传输。

一、SSL协议的握手过程

“商量”被大牛们升华了就是握手过程.
开始加密通信之前,客户端和服务器首先必须建立连接和交换参数,这个过程叫做握手(handshake)。
假定客户端叫做爱丽丝,服务器叫做鲍勃,整个握手过程可以用下图说明。
这里写图片描述
握手阶段分成五步(实际的细节比这复杂一点)。
这里写图片描述

整个握手阶段都不加密(也没法加密),都是明文的。因此,如果有人窃听通信,他可以知道双方选择的加密方法,以及三个随机数中的两个。整个通话的安全,只取决于第三个随机数(Premaster secret)能不能被破解。
虽然理论上,只要服务器的公钥足够长(比如2048位),那么Premaster secret可以保证不被破解。但是为了足够安全,我们可以考虑把握手阶段的算法从默认的RSA算法,改为 Diffie-Hellman算法(简称DH算法)。采用DH算法后,Premaster secret不需要传递,双方只要交换各自的参数,就可以算出这个随机数。
(DH算法是非对称加密算法的基础,暂时不准备写关于DH的博文,如需要自行网上学习。)

二、动手操作展示握手过程

我们首先要为服务端生成一个数字证书。Java环境下,数字证书是用keytool生成的,这些证书被存储在store的概念中,就是证书仓库。我们来调用命令为服务端生成数字证书和保存它使用的证书仓库:
我的证书仓库是存放在c:/study/keysotre目录下。
这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

最后生成了如下3个文件:
这里写图片描述
上面3步目的是 给服务器端生成证书,并把证书安装到客户端信任证书里面。因为是自签名证书,不添加到信任证书里面则不能完成通信。


代码经过测试可以直接运行:
Server端代码:

package ssl;import java.io.BufferedReader;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.Writer;import java.net.ServerSocket;import java.net.Socket;import java.security.KeyManagementException;import java.security.KeyStore;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.security.UnrecoverableKeyException;import java.security.cert.CertificateException;import javax.net.ServerSocketFactory;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLServerSocket;import org.apache.commons.codec.binary.Base64;public class Server {     private static String SERVER_KEY_STORE ="C:/study/keyStore/server_ks";     private static String SERVER_KEY_STORE_PASSWORD ="123123";     public static void main(String args[]) throws IOException,                 NoSuchAlgorithmException, KeyStoreException, CertificateException,                UnrecoverableKeyException, KeyManagementException {         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());         //打印出私钥         System.out.println(Base64.encodeBase64String(ks.getKey("bluedash-ssl-demo-server", "123123".toCharArray()).getEncoded()));         context.init(kf.getKeyManagers(), null, null);         ServerSocketFactory factory = context.getServerSocketFactory();         int port = 8899;         ServerSocket server = factory.createServerSocket(port);         //为了简单起见,所有的异常信息都往外抛         ((SSLServerSocket) server).setNeedClientAuth(false);         //定义一个ServerSocket监听在端口8899上         //ServerSocket server = new ServerSocket(port);         while (true) {             //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的             Socket socket = server.accept();             //每接收到一个Socket就建立一个新的线程来处理它             new Thread(new Task(socket)).start();         }   }   /**    * 用来处理Socket请求的   */   static class Task implements Runnable {       private Socket socket;       public Task(Socket socket) {           this.socket = socket;       }       public void run() {        try {                handleSocket();            } catch (Exception e) {                e.printStackTrace();            }       }      /**       * 跟客户端Socket进行通信      * @throws Exception       */      private void handleSocket() throws Exception {         BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));         StringBuilder sb = new StringBuilder();         String temp;         int index;         while ((temp=br.readLine()) != null) {            //System.out.println(temp);            if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收             sb.append(temp.substring(0, index));                break;            }            sb.append(temp);         }         System.out.println("from client: " + sb);         //读完后写一句         Writer writer = new OutputStreamWriter(socket.getOutputStream());         writer.write("Hello Client.");         writer.write("eof\n");         writer.flush();         writer.close();         br.close();         socket.close();      }   }}

Client端:

package ssl;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.Writer;import java.net.Socket;import java.util.Date;import javax.net.SocketFactory;import javax.net.ssl.SSLSocketFactory;public class Client {    private static String CLIENT_KEY_STORE = "C:/study/keyStore/client_ks";    public static void main(String args[]) throws Exception {        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);        System.setProperty("javax.net.debug", "ssl,handshake");        SocketFactory sf = SSLSocketFactory.getDefault();        Socket client = sf.createSocket("localhost", 8899);        /*         * //为了简单起见,所有的异常都直接往外抛 String host = "localhost"; //要连接的服务端IP地址 int         * port = 8899; //要连接的服务端对应的监听端口 //与服务端建立连接 Socket client = new         * Socket(host, port);         */        // 建立连接后就可以往服务端写数据了        Writer writer = new OutputStreamWriter(client.getOutputStream());        writer.write("Hello Server.");        writer.write("eof\n");        writer.flush();        // 写完以后进行读操作        System.out.println(new Date());        BufferedReader br = new BufferedReader(new InputStreamReader(                client.getInputStream()));        StringBuffer sb = new StringBuffer();        String temp;        int index;        while ((temp = br.readLine()) != null) {            if ((index = temp.indexOf("eof")) != -1) {                sb.append(temp.substring(0, index));                break;            }            sb.append(temp);        }        System.out.println(new Date());        System.out.println("from server: " + sb);        writer.close();        br.close();        client.close();    }}

结果(部分截图):
第一次Client向服务器握手 发出它支持的加密算法 随机数等等
这里写图片描述

服务器接到请求 选择加密算法 并返回一个随机数等
这里写图片描述

参考:
1.http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
2.http://blog.csdn.net/cuishi0/article/details/6805179
3.http://blog.chinaunix.net/uid-17102734-id-2830223.html

0 0
原创粉丝点击