Java SSL通信Demo
一、数字证书创建
在SSL通信协议中,服务端必须有一个数字证书,当客户端连接到服务端时,会得到这个证书,然后客户端会判断这个证书是否是可信的,如果是,则交换信道加密密钥,进行通信。如果不信任这个证书,则连接失败。
1、keytool
JDK自带keytool工具命令案列:
keytool
-genkey
-alias demo
-keyalg RSA
-keysize 1024
-validity 365
-keystore ./ks
-dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn"
-storepass ks
-keypass 123456
参数 | 参数含义 |
-genkey | 生成秘钥 |
-alias | 别名 |
-keyalg | 秘钥算法 |
-keysize | 秘钥长度 |
-validity | 有效期 |
-keystore | 生成秘钥库的存储路径和名称 |
-keypass | 秘钥口令 |
-storepass | 秘钥库口令 |
-dname | CN:姓名;OU:组织单位名称;O:组织名称;L:省/市/自治区名称;C:国家/地区代码 |
2、数字证书创建
(1)服务端
keytool -genkey -alias ssl-demo-server -keyalg RSA -keysize 1024 -validity 365 -keystore ./server -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123456
(2)客户端
keytool -genkey -alias ssl-demo-client -keyalg RSA -keysize 1024 -validity 365 -keystore ./client -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 123456
(3) 将服务端的证书导出来,并导入到客户端的仓库
[1] 服务端证书导出
keytool -export -alias ssl-demo-server -keystore ./server -file server.cer
[2] 将服务端导出证书导入到客户端仓库
keytool -export -alias ssl-demo-server -keystore ./server -file server.cer
[3]客户端证书导出
keytool -export -alias ssl-demo-client -keystore ./client -file client.cer
[4] 将客户端导出证书导入到服务端仓库
keytool -import -trustcacerts -alias ssl-demo-client -file ./client.cer -keystore E:/store/server/server
二、SSL单向握手通信
SSL单向握手通信。即:客户端验证服务端的证书,服务端不验证客户端的证书。
服务端设置:
/**
* 设置是否需要验证客户端
* true:需要验证客户端
* false:不需要验证客户端
*/
sslServerSocket.setNeedClientAuth(false);
1、服务端(不需要验证客户端)
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
public class SSLServer {
/**
* 服务器端证书位置
*/
private static String SERVER_KEY_STORE = "E:\\store\\server\\server";
/**
* 服务器端证书密码
*/
private static String SERVER_KEY_STORE_PASSWORD = "123456";
/**
* 套接字
*/
private static Socket socket;
public static void main(String[] args) throws Exception {
SSLServerSocket sslServerSocket = createSSLServerSocket();
while (true) {
/**
* 服务端获取连接
*/
socket = sslServerSocket.accept();
messageHandle();
}
}
/**
* 客户端信息处理
*/
public static void messageHandle() {
/**
* 字节输入流
*/
InputStream inputStream = null;
/**
* 字符输入流
*/
InputStreamReader inputStreamReader = null;
/**
* 缓冲区
*/
BufferedReader bufferedReader = null;
/**
* 字节输出流
*/
OutputStream outputStream = null;
/**
* 文本输出流
*/
PrintWriter printWriter = null;
try {
/**
* 获取连接字节输入流
*/
inputStream = socket.getInputStream();
/**
* 将输字节输入流转换为字符输入流
*/
inputStreamReader = new InputStreamReader(inputStream);
/**
* 将字符输入流的数据写到缓冲区
*/
bufferedReader = new BufferedReader(inputStreamReader);
/**
* 获取连接字节输出流
*/
outputStream = socket.getOutputStream();
/**
* 将字节输出流转化为文本输出流
*/
printWriter = new PrintWriter(outputStream);
/**
* 读取客户端发送来的数据
*/
String data = bufferedReader.readLine();
System.out.println("客户端消息:" + data);
/**
* 向客户端返回消息
*/
printWriter.println("你好,客户端,我已经接收到你发送的消息!");
/**
* 刷新流
*/
printWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建SSL协议服务端Socket
*
* @return
* @throws Exception
*/
public static SSLServerSocket createSSLServerSocket() throws Exception {
/**
* SSL请求信托证书仓库注册
*/
System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);
/**
* 获取指定类型(jceks)的密钥存储库实例
*/
KeyStore keyStore = KeyStore.getInstance("jceks");
/**
* 读取服务端证书
*/
FileInputStream fileInputStream = new FileInputStream(SERVER_KEY_STORE);
/**
* 加载服务端证书到密钥存储库
*/
keyStore.load(fileInputStream, null);
/**
* 获取密钥管理仓库实例
*/
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
/**
* 初始化密钥管理仓库
*/
keyManagerFactory.init(keyStore, SERVER_KEY_STORE_PASSWORD.toCharArray());
/**
* 获取协议为“TLS”的SSL上下文实例
*/
SSLContext sslContext = SSLContext.getInstance("TLS");
/**
* 获取密钥管理仓库中所有的密钥管理器
*/
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
/**
* 初始化SSL上下文
*/
sslContext.init(keyManagers, null, null);
/**
* 通过SSL上下文获取ServerSocket工厂
*/
ServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory();
/**
* 创建一个服务端,端口为8848
*/
ServerSocket serverSocket = serverSocketFactory.createServerSocket(8848);
/**
* 服务端向下转换为SSL协议的服务端
*/
SSLServerSocket sslServerSocket = (SSLServerSocket) serverSocket;
/**
* 设置是否需要客户端验证
* true:需要验证客户端
* false:不需要验证客户端
*/
sslServerSocket.setNeedClientAuth(false);
/**
* 返回
*/
return sslServerSocket;
}
}
2、客户端
import java.io.*;
import java.net.Socket;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
public class SSLClient {
/**
* 客户端证书地址
*/
private static String CLIENT_KEY_STORE = "E:\\store\\client\\client";
public static void main(String[] args) throws Exception {
/**
* SSL请求信托证书仓库注册
*/
System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
/**
* 获取Socket工厂
*/
SocketFactory socketFactory = SSLSocketFactory.getDefault();
/**
* 创建Socket
*/
Socket socket = socketFactory.createSocket("localhost", 8848);
/**
* 获取连接输出流
*/
OutputStream outputStream = socket.getOutputStream();
/**
* 将字节输出流转化为文本输出流
*/
PrintWriter writer = new PrintWriter(outputStream);
/**
* 获取连接字节输入流
*/
InputStream inputStream = socket.getInputStream();
/**
* 将输字节输入流转换为字符输入流
*/
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
/**
* 将字符输入流的数据写到缓冲区
*/
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
/**
* 向服务器发送信息
*/
writer.println("你好,服务器(单向)");
/**
* 刷新流
*/
writer.flush();
/**
* 获取服务器返回的信息
*/
String data = bufferedReader.readLine();
System.out.println(data);
/**
* 关闭连接
*/
socket.close();
}
}
3、运行测试
三、SSL双向握手通信
1、服务端
/**
* 修改此处参数即可
* 设置是否需要客户端验证
* true:需要验证客户端
* false:不需要验证客户端
*/
sslServerSocket.setNeedClientAuth(true);
2、客户端
import java.io.*;
import java.net.Socket;
import java.security.KeyStore;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
public class BothwaySSLClient {
/**
* 客户端证书地址
*/
private static String CLIENT_KEY_STORE = "E:\\store\\client\\client";
/**
* 客户端证书密码
*/
private static String CLIENT_KEY_STORE_PASSWORD = "123456";
public static void main(String[] args) throws Exception {
/**
* SSL请求信托证书仓库注册
*/
System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
/**
* 获取连接
*/
Socket socket = createSocket();
/**
* 获取连接输出流
*/
OutputStream outputStream = socket.getOutputStream();
/**
* 将字节输出流转化为文本输出流
*/
PrintWriter writer = new PrintWriter(outputStream);
/**
* 获取连接字节输入流
*/
InputStream inputStream = socket.getInputStream();
/**
* 将输字节输入流转换为字符输入流
*/
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
/**
* 将字符输入流的数据写到缓冲区
*/
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
/**
* 向服务器发送信息
*/
writer.println("你好,服务器(双向)");
/**
* 刷新流
*/
writer.flush();
/**
* 获取服务器返回的信息
*/
String data = bufferedReader.readLine();
System.out.println(data);
/**
* 关闭连接
*/
socket.close();
}
public static Socket createSocket() throws Exception {
/**
* 获取指定类型(jceks)的密钥存储库实例
*/
KeyStore keyStore = KeyStore.getInstance("jceks");
/**
* 读取服务端证书
*/
FileInputStream fileInputStream = new FileInputStream(CLIENT_KEY_STORE);
/**
* 加载服务端证书到密钥存储库
*/
keyStore.load(fileInputStream, null);
/**
* 获取密钥管理仓库实例
*/
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
/**
* 初始化密钥管理仓库
*/
keyManagerFactory.init(keyStore, CLIENT_KEY_STORE_PASSWORD.toCharArray());
/**
* 获取协议为“TLS”的SSL上下文实例
*/
SSLContext sslContext = SSLContext.getInstance("TLS");
/**
* 获取密钥管理仓库中所有的密钥管理器
*/
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
/**
* 初始化SSL上下文
*/
sslContext.init(keyManagers, null, null);
/**
* 通过SSL上下文获取Socket工厂
*/
SocketFactory factory = sslContext.getSocketFactory();
/**
* 创建Socket(连接服务器)
*/
Socket socket = factory.createSocket("localhost", 8848);
/**
* 返回
*/
return socket;
}
}
3、运行测试