SSLSocket 客户端、服务端通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sarafina527/article/details/89449143

1.SSL安全套接层

SSL:(Secure Socket Layer) 安全套接层,于 1994 年由网景公司设计,并于 1995 年发布了 3.0 版本
TLS:(Transport Layer Security)传输层安全性协议,是 IETF 在 SSL3.0 的基础上设计的协议

这两种协议总体差别不大,实现的功能类似,以下都以SSL统称。

ssl处于网络层次中的会话层,位于传输层TCP之上,相比于TCP层上的Socke的连接,SSL上是SSLSocket,基于socket但比socket安全,安全在于认证通信两端身份、加密传输应用数据

之前https://blog.csdn.net/sarafina527/article/details/89449006 TCP的通信,SSL是比TCP多了一层握手和加密传输

1.1 服务端实现

package communication.sslSocket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.security.KeyStore;
import java.util.Arrays;

public class SSlServer {
    private static final Logger logger = LoggerFactory.getLogger(SSlServer.class);
    private static final char[] ksPass = "123456".toCharArray();
    private static final char[] keyPass = "123456".toCharArray();
    private static final int port = 8443;


    public static void main(String[] args) throws Exception{
        // 类似于serverSocket
        SSLServerSocket sslServerSocket = createSSLServerSocket();
        // 等待客户端连接 阻塞
        SSLSocket socket = (SSLSocket) sslServerSocket.accept();
        logger.info("客户端连接 " + socket.getInetAddress()+ " " +socket.getPort());
        // 读取
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        BufferedReader br = new BufferedReader(reader);
        // 写入
        PrintWriter writer = new PrintWriter(socket.getOutputStream());
        String msg = br.readLine();
        try{
            while (true) {
                while (msg != null) {
                    if("bye".equals(msg)) {
                        break;
                    }
                    System.out.println(msg);
                    writer.println(msg);
                    writer.flush();
                    msg = br.readLine();
                }
            }
        }finally {
            if(socket != null) {
                socket.close();
            }
        }


    }
    // 创建服务端socket
    public static SSLServerSocket createSSLServerSocket() throws Exception{
        SSLContext sslContext = createContext();
        SSLServerSocketFactory sslssf = sslContext.getServerSocketFactory();
        // 绑定服务端口
        SSLServerSocket serverSocket = (SSLServerSocket) sslssf.createServerSocket(port);
        // 设置加密算法套件
        String[] supported = sslssf.getSupportedCipherSuites();
        serverSocket.setEnabledCipherSuites(supported);
        logger.info("服务端可支持的加密算法套件有:" + Arrays.toString(supported));
        return serverSocket;
    }

    // 创建上下文环境,主要用于保存安全通信的基本信息,如协议版本、证书管理方式
    public static SSLContext createContext() throws Exception{
        logger.info("开始服务端创建sslContext上下文......" );
        SSLContext sslContext = SSLContext.getInstance("TLS");

        logger.info(sslContext.getProtocol());
        // 管理本端 的密钥(发送本端的证书用于对方认证我的身份)
        logger.info(KeyManagerFactory.getDefaultAlgorithm());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        // 管理对端证书的信任证书,用于认证对方的身份
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream("/Users/wangying49/certs/sslTestServer.jks"),ksPass);
        kmf.init(ks,keyPass);
        tmf.init(ks); // 信任证书和服务端私钥 放在同一个库里
        // 上下文 初始化、两端证书的加载
        sslContext.init(kmf.getKeyManagers(),tmf.getTrustManagers(),null);
        logger.info("上下文信息:" + sslContext.getProtocol() + sslContext.getSocketFactory());

        return sslContext;
    }
}

1.2 客户端

package communication.sslSocket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;

/**
 * 〈一句话功能简述〉<br>
 * 〈避免需要证书用于进行Https请求的HttpClient  〉
 */
public class SSLClient {

    private static final Logger logger = LoggerFactory.getLogger(SSLClient.class);

    private static final char[] ksPass = "123456".toCharArray();
    private static final char[] keyPass = "123456".toCharArray();
    private static final String protocol = "TLS";

    public static void main(String[] args) throws Exception{
        String host = "localhost";
        String charset = "utf-8";
        int port = 8443;
        String msg = "hello ssl";
        BufferedReader reader = null;
        BufferedWriter writer = null;
        SSLSocketFactory ssf = createSocketFactory();
        SSLSocket socket = (SSLSocket) ssf.createSocket(host, port);
        socket.setSoTimeout(10000);
        msg = socket.getNeedClientAuth() + " " ;
        // 写入请求
        writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), charset));
        logger.info("send data to {}:{}", new Object[] { host, port });
        logger.info("发送 REQUEST:{}", msg);
        writer.write(msg + "\n");
        writer.flush();// 在真正发送的时候才会握手
        // 读取响应
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), charset));
        String result = reader.readLine();
        logger.info("接收响应 :" + result);
    }

    @SuppressWarnings("unchecked")
    public static SSLSocketFactory createSocketFactory() throws Exception
    {
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream("/Users/wangying49/certs/sslTestClient.jks"), ksPass);
        // sun.security.validator.ValidatorException: PKIX path building failed: 选择不匹配的信任证书
//        ks.load(new FileInputStream("/Users/wangying49/certs/ceb0305.jks"), ksPass);
        if (ks == null) {
            throw new Exception("没有证书库配置");
        }
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
        tmf.init(ks);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, keyPass); // 这里是私钥的密码
        //
        SSLContext ctx = SSLContext.getInstance(protocol);
        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        SSLSocketFactory ssf = ctx.getSocketFactory();
        return ssf;
    }
}

1.3 执行结果

上文代码中的证书库里的证书是两对匹配的密钥,如sslTestServer.jks存放的是服务端的私钥,和客户端的信任证书,由于此处是用的自签名的证书,所以信任证书就是客户端证书  

客户端的证书库存放的是客户端的私钥和服务端的信任证书

匹配时效果与普通的TCP通信差不多,但是当证书不匹配时,如我将客户端的证书库更换了,(见代码注释内容),就会报如下错误:

 

可见多了一个握手的过程,默认使用X509TrustManagerImpl来校验服务端证书

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/89449143