java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java实现SSL Socket长连接

Java实现SSL Socket长连接方式

作者:走马川行雪

这篇文章主要介绍了Java实现SSL Socket长连接方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、单向认证

1、生成服务端密钥(配置了jdk的环境变量即可用keytool命令)

命令:keytool -genkey -keystore server_ks.jks -storepass server_password -keyalg RSA -keypass server_password

结果:会生成server_ks.jks密钥文件

操作:将生成的server_ks.jks密钥文件配置到服务端

2、生成服务端证书

命令:keytool -export -keystore server_ks.jks -storepass server_password -file server.cer

结果:会生成server.cer证书文件

操作:将生成的server.cer证书文件配置到客户端

3、服务端代码

package com.ssl.server;
 
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
 
public class ServerTest {
 
    public static File writeToFile1 = new File("C:\\Users\\Li\\Desktop\\temp\\配置.txt");
    public static File writeToFile2 = new File("C:\\Users\\Li\\Desktop\\temp\\配置2.txt");
 
    public static void main(String[] args) {
        new Thread(new ServerOne()).start();
    }
 
    public static class ServerOne implements Runnable {
        //SSL协议版本
        private static final String TLS = "TLSv1.2";
        //KeyStore的类型
        private static final String PROVIDER = "SunX509";
        //秘钥类型,java默认是JKS
        private static final String STORE_TYPE = "JKS";
        //秘钥的路径
        private static final String KEY_STORE_NAME = "E:\\02-IDEA_Project\\Test02_HttpsServer\\server_ks.jks";
        //Server的端口
        private static final int DEFAULT_PORT = 8090; //自定义端口
        //秘钥的密码
        private static final String SERVER_KEY_STORE_PASSWORD = "server_password";
 
        @Override
        public void run() {
            SSLServerSocket sslServerSocket = null;
            try {
                //获取SSLContext
                SSLContext sslContext = SSLContext.getInstance(TLS);
                //生成秘钥的manager
                KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER);
                //加载秘钥
                KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE);
                keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray());
                //秘钥初始化
                keyManagerFactory.init(keyStoreOne, SERVER_KEY_STORE_PASSWORD.toCharArray());
                //初始化SSLContext
                sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
                //获取SSLContext的SocketFactory
                sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(DEFAULT_PORT);
                //是否开启双向验证
                sslServerSocket.setNeedClientAuth(false);
                System.out.println("服务器已开启,等待连接 .....");
                while (true) {
                    Socket accept = sslServerSocket.accept();
                    accept.setKeepAlive(true);
                    System.out.println("客户端 : " + accept.getInetAddress().getHostAddress());
 
                    try (
                            InputStream inputStream = accept.getInputStream();
                            OutputStream outputStream = accept.getOutputStream();
                    ) {
                        byteMsgReader(inputStream, outputStream);
//                        socket.shutdownOutput();        // 长连接则不关闭输出流
//                        socket.shutdownInput();            // 长连接则不关闭输入流
                    } catch (Exception e) {
                        System.out.println(e.toString());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
 
                try {
                    if (sslServerSocket != null) {
                        sslServerSocket.close();
                        System.out.println("服务器关闭!");
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
 
    /**
     * 字节流-解析客户端的消息
     */
    public static void byteMsgReader(InputStream inputStream, OutputStream outputStream) throws Exception {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        FileOutputStream bufferedOutputStream1 = new FileOutputStream(writeToFile1, false);
        FileOutputStream bufferedOutputStream2 = new FileOutputStream(writeToFile2, false);
        byte[] b = new byte[4096];
        int i = 0;
        while ((i = bufferedInputStream.read(b)) > 0) {
            bufferedOutputStream1.write(b, 0, i);
            bufferedOutputStream1.flush();
            if (i < b.length) {    // 根据读取的长度是否满格判断当前文件是否已读取完毕
                bufferedOutputStream1.close();
                break;
            }
        }
        System.out.println("接收完成第一个文件");
        byteMsgWriter(outputStream);
        i = 0;
        byte[] b2 = new byte[4096];
        while ((i = bufferedInputStream.read(b2)) > 0) {
            bufferedOutputStream2.write(b2, 0, i);
            bufferedOutputStream2.flush();
            if (i < b2.length) {   // 根据读取的长度是否满格判断当前文件是否已读取完毕
                bufferedOutputStream2.close();
                break;
            }
        }
        System.out.println("接收完成第二个文件");
        byteMsgWriter(outputStream);
    }
 
    /**
     * 字节流-发送给客户端回执消息
     */
    public static void byteMsgWriter(OutputStream outputStream) throws Exception {
        BufferedOutputStream bufferedOutputStreamBack = new BufferedOutputStream(outputStream);
        bufferedOutputStreamBack.write("服务端已成功接收文件.".getBytes(StandardCharsets.UTF_8));
        bufferedOutputStreamBack.write("\n".getBytes(StandardCharsets.UTF_8));
        bufferedOutputStreamBack.flush();                // 第一次发送消息,长连接实现方式
    }
 
}

4、客户端代码

package com.ssl.client;
 
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
 
public class ClientTest {
 
    public static File readFile = new File("C:\\Users\\Li\\Desktop\\temp\\sqlV2.0.sql");
 
    public static void main(String[] args) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new ClientOne()).start();
    }
 
    public static class ClientOne implements Runnable {
 
        private static final String TLS = "TLSv1.2";
        private static final String PROVIDER = "SunX509";
        private static final String STORE_TYPE = "JKS";
        private static final String TRUST_STORE_NAME = "E:\\02-IDEA_Project\\Test01_HttpsClient\\server.cer";
 
        @Override
        public void run() {
            SSLSocket socket = null;
            try {
                SSLContext sslContext = SSLContext.getInstance(TLS);
                //生成信任证书Manager,默认系统会信任CA机构颁发的证书,自定的证书需要手动的加载
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER);
                KeyStore keyStore = KeyStore.getInstance(STORE_TYPE);
                keyStore.load(null);
                //生成验证工厂
                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                //生成别名(可以随便填写)
                String certificateAlias = Integer.toString(0);
                //加载证书
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(new FileInputStream(TRUST_STORE_NAME)));
                //初始化
                trustManagerFactory.init(keyStore);
                //初始化
                sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
                socket = (SSLSocket) sslContext.getSocketFactory().createSocket("localhost", 8090);
                socket.setKeepAlive(true);
//                socket.setSoTimeout(10000);   //设置超时时间
                try (
                        InputStream inputStream = socket.getInputStream();
                        OutputStream outputStream = socket.getOutputStream();
                ) {
                    byteMsgWriter(outputStream, inputStream);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } catch (Exception e) {
                e.printStackTrace();
 
                try {
                    if (socket != null) {
                        socket.close();
                        System.out.println("客户端关闭");
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
 
    /**
     * 字节流-发送给服务端
     */
    public static void byteMsgWriter(OutputStream outputStream, InputStream inputStream) throws Exception {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(readFile));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        //mark后读取超过readlimit字节数据,mark标记就会失效
        bufferedInputStream.mark(909600000);
        byte[] b = new byte[4096];
        int i;
        while ((i = bufferedInputStream.read(b)) > 0) {
            bufferedOutputStream.write(b, 0, i);
            bufferedOutputStream.flush();                    // 第一次发送消息,长连接实现方式
        }
 
        bufferedInputStream.reset();
        Thread.sleep(1);                                // 必须加休眠,否则第二个文件流会发生错乱
        characterMsgReader(inputStream);                    // 从服务端接收消息
        while ((i = bufferedInputStream.read(b)) > 0) {
            bufferedOutputStream.write(b, 0, i);
            bufferedOutputStream.flush();                    // 第二次发送消息,长连接实现方式
        }
        characterMsgReader(inputStream);                    // 从服务端接收消息
    }
 
    /**
     * 字符流-解析服务端的回执消息-不间断解析
     */
    public static void characterMsgReader(InputStream inputStream) throws Exception {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println("服务端消息: " + line);
        }
    }
 
}

二、双向认证

1、生成客户端密钥

命令:keytool -genkey -keystore client_ks.jks -storepass client_password -keyalg RSA -keypass client_password

结果:会生成client_ks.jks密钥文件

操作:将生成的client_ks.jks密钥文件配置到客户端

2、生成客户端证书

命令:keytool -export -keystore client_ks.jks -storepass client_password -file client.cer

结果:会生成client.cer证书文件

3、将server端证书添加到serverTrust_ks.jks文件中

命令:keytool -import -keystore serverTrust_ks.jks -storepass client -file server.cer

结果:会生成serverTrust_ks.jks密钥文件

操作:将生成的serverTrust_ks.jks密钥文件配置到客户端

4、将client端证书添加到clientTrust_ks.jks文件中

命令:keytool -import -keystore clientTrust_ks.jks -storepass server -file client.cer

结果:会生成clientTrust_ks.jks密钥文件

操作:将生成的clientTrust_ks.jks密钥文件配置到服务端

5、服务端代码

package com.ssl.server;
 
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
 
public class Server {
 
    public static File writeToFile1 = new File("C:\\Users\\Li\\Desktop\\temp\\配置.txt");
    public static File writeToFile2 = new File("C:\\Users\\Li\\Desktop\\temp\\配置2.txt");
 
    public static void main(String[] args) {
        new Thread(new ServerOne()).start();
    }
 
    public static class ServerOne implements Runnable {
 
        //SSL协议版本
        private static final String TLS = "TLSv1.2";
        //KeyStore的类型
        private static final String PROVIDER = "SunX509";
        //秘钥类型,java默认是JKS,Android不支持JKS,只能用BKS
        private static final String STORE_TYPE = "JKS";
        //秘钥的路径
        private static final String KEY_STORE_NAME = "E:\\02-IDEA_Project\\Test02_HttpsServer\\server_ks.jks";
 
        private static final String TRUST_STORE_NAME = "E:\\02-IDEA_Project\\Test02_HttpsServer\\clientTrust_ks.jks";
        //Server的端口
        private static final int DEFAULT_PORT = 8090; //自定义端口
        //秘钥的密码
        private static final String SERVER_KEY_STORE_PASSWORD = "server_password"; //秘钥的密码
        private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "server";//密码
 
        @Override
        public void run() {
 
            SSLServerSocket sslServerSocket = null;
            try {
                //获取SSLContext
                SSLContext sslContext = SSLContext.getInstance(TLS);
 
                //生成秘钥的manager
                KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER);
                //加载信任的证书
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER);
                //加载秘钥
                KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE);
                KeyStore keyStoreTwo = KeyStore.getInstance(STORE_TYPE);
                keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray());
                keyStoreTwo.load(new FileInputStream(TRUST_STORE_NAME), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());
                //秘钥初始化
                keyManagerFactory.init(keyStoreOne, SERVER_KEY_STORE_PASSWORD.toCharArray());
                trustManagerFactory.init(keyStoreTwo);
                //初始化SSLContext
                sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
                //获取SSLContext的SocketFactory
                sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(DEFAULT_PORT);
                //是否开启双向验证
                sslServerSocket.setNeedClientAuth(true);
                System.out.println("服务器已开启,等待连接 .....");
                while (true) {
                    Socket accept = sslServerSocket.accept();
                    accept.setKeepAlive(true);
                    System.out.println("客户端 : " + accept.getInetAddress().getHostAddress());
 
                    try (
                            InputStream inputStream = accept.getInputStream();
                            OutputStream outputStream = accept.getOutputStream();
                    ) {
                        byteMsgReader(inputStream, outputStream);
//                        socket.shutdownOutput();        // 长连接则不关闭输出流
//                        socket.shutdownInput();            // 长连接则不关闭输入流
                    } catch (Exception e) {
                        System.out.println(e.toString());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
 
                try {
                    if (sslServerSocket != null) {
                        sslServerSocket.close();
                        System.out.println("服务器关闭!");
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
 
    /**
     * 字节流-解析客户端的消息
     */
    public static void byteMsgReader(InputStream inputStream, OutputStream outputStream) throws Exception {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        FileOutputStream bufferedOutputStream1 = new FileOutputStream(writeToFile1, false);
        FileOutputStream bufferedOutputStream2 = new FileOutputStream(writeToFile2, false);
        byte[] b = new byte[4096];
        int i = 0;
        while ((i = bufferedInputStream.read(b)) > 0) {
            bufferedOutputStream1.write(b, 0, i);
            bufferedOutputStream1.flush();
            if (i < b.length) {       // 根据读取的长度是否满格判断当前文件是否已读取完毕
                bufferedOutputStream1.close();
                break;
            }
        }
        System.out.println("接收完成第一个文件");
        byteMsgWriter(outputStream);
        i = 0;
        byte[] b2 = new byte[4096];
        while ((i = bufferedInputStream.read(b2)) > 0) {
            bufferedOutputStream2.write(b2, 0, i);
            bufferedOutputStream2.flush();
            if (i < b2.length) {       // 根据读取的长度是否满格判断当前文件是否已读取完毕
                bufferedOutputStream2.close();
                break;
            }
        }
        System.out.println("接收完成第二个文件");
        byteMsgWriter(outputStream);
    }
 
    /**
     * 字节流-发送给客户端回执消息
     */
    public static void byteMsgWriter(OutputStream outputStream) throws Exception {
        BufferedOutputStream bufferedOutputStreamBack = new BufferedOutputStream(outputStream);
        bufferedOutputStreamBack.write("服务端已成功接收文件.".getBytes(StandardCharsets.UTF_8));
        bufferedOutputStreamBack.write("\n".getBytes(StandardCharsets.UTF_8));
        bufferedOutputStreamBack.flush();        // 第一次发送消息,长连接实现方式
    }
 
}

6、客户端代码

package com.ssl.client;
 
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.security.KeyStore;
 
public class Client {
 
    public static File readFile = new File("C:\\Users\\Li\\Desktop\\temp\\sqlV2.0.sql");
 
    public static void main(String[] args) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new ClientOne()).start();
    }
 
 
    public static class ClientOne implements Runnable {
 
        private static final String TLS = "TLSv1.2";
        private static final String PROVIDER = "SunX509";
        private static final String STORE_TYPE = "JKS";
        private static final String TRUST_STORE_NAME = "E:\\02-IDEA_Project\\Test01_HttpsClient\\serverTrust_ks.jks";
        private static final String KEY_STORE_NAME = "E:\\02-IDEA_Project\\Test01_HttpsClient\\client_ks.jks";
        private static final String CLIENT_KEY_STORE_PASSWORD = "client_password"; //密码
        private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "client";//密码
 
        @Override
        public void run() {
 
            SSLSocket socket = null;
            try {
                SSLContext sslContext = SSLContext.getInstance(TLS);
                KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER);
                //生成信任证书Manager,默认系统会信任CA机构颁发的证书,自定的证书需要手动的加载
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER);
                KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE);
                KeyStore keyStoreTwo = KeyStore.getInstance(STORE_TYPE);
                //加载client端密钥
                keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), CLIENT_KEY_STORE_PASSWORD.toCharArray());
                //信任证书
                keyStoreTwo.load(new FileInputStream(TRUST_STORE_NAME), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
                keyManagerFactory.init(keyStoreOne, CLIENT_KEY_STORE_PASSWORD.toCharArray());
                trustManagerFactory.init(keyStoreTwo);
 
                //初始化
                sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
 
 
                //此种写法代表客户端信任所有证书
                /*TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }
                }};
                //初始化
                sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, null);*/
 
 
                socket = (SSLSocket) sslContext.getSocketFactory().createSocket("localhost", 8090);
                socket.setKeepAlive(true);
//                socket.setSoTimeout(10000);
                try (
                        InputStream inputStream = socket.getInputStream();
                        OutputStream outputStream = socket.getOutputStream();
                ) {
                    byteMsgWriter(outputStream, inputStream);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } catch (Exception e) {
                e.printStackTrace();
 
                try {
                    if (socket != null) {
                        socket.close();
                        System.out.println("客户端关闭");
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
 
    /**
     * 字节流-发送给服务端
     */
    public static void byteMsgWriter(OutputStream outputStream, InputStream inputStream) throws Exception {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(readFile));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        //mark后读取超过readlimit字节数据,mark标记就会失效
        bufferedInputStream.mark(909600000);
        byte[] b = new byte[4096];
        int i;
        while ((i = bufferedInputStream.read(b)) > 0) {
            bufferedOutputStream.write(b, 0, i);
            bufferedOutputStream.flush();                    // 第一次发送消息,长连接实现方式
        }
 
        bufferedInputStream.reset();
        Thread.sleep(1);                                // 必须加休眠,否则第二个文件流会发生错乱
        characterMsgReader(inputStream);                    // 从服务端接收消息
        while ((i = bufferedInputStream.read(b)) > 0) {
            bufferedOutputStream.write(b, 0, i);
            bufferedOutputStream.flush();                    // 第二次发送消息,长连接实现方式
        }
        characterMsgReader(inputStream);                    // 从服务端接收消息
    }
 
    /**
     * 字符流-解析服务端的回执消息-不间断解析
     */
    public static void characterMsgReader(InputStream inputStream) throws Exception {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println("服务端消息: " + line);
        }
    }
 
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文