SSL握手过程:
一、自签名密钥和证书
1、生成服务端密钥
2、生成服务端证书
3、生成客户端密钥
4、生成客户端证书
5、将server端证书添加到serverTrust_ks.jks文件中
6、将client端证书添加到clientTrust_ks.jks文件中
(以上生成过程详见:https://blog.csdn.net/weixin_43192102/article/details/122214603)
7、将jks密钥转换为bks格式密钥(因为Android只支持.bks格式的密钥文件)
(转换工具详见:jks文件转bks文件.zip-Java文档类资源-CSDN下载)
8、服务端代码
package com.ssl.socket.server;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManagerFactory;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnServer = findViewById(R.id.btn_server);
btnServer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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 = "X509";
//秘钥类型,java默认是JKS,Android不支持JKS,只能用BKS
// private static final String STORE_TYPE = "JKS";
private static final String STORE_TYPE = "BKS";
//秘钥的路径
private static final String KEY_STORE_NAME = "server_ks.bks";
private static final String TRUST_STORE_NAME = "clientTrust_ks.bks";
//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(MyApplication.getContext().getAssets().open(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray());
keyStoreTwo.load(MyApplication.getContext().getAssets().open(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(false);
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);
} 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 bis = new BufferedInputStream(inputStream);
byte[] buffer = new byte[20];
int length = bis.read(buffer);
System.out.println("Receive: " + new String(buffer, 0, length));
System.out.println("接收完成第一个文件");
byteMsgWriter(outputStream);
bis = new BufferedInputStream(inputStream);
buffer = new byte[20];
length = bis.read(buffer);
System.out.println("Receive: " + new String(buffer, 0, length));
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(); // 第一次发送消息,长连接实现方式
}
}
9、客户端代码
package com.ssl.socket.client;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnClient = findViewById(R.id.btn_client);
btnClient.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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 = "X509";
private static final String STORE_TYPE = "BKS";
private static final String TRUST_STORE_NAME = "serverTrust_ks.bks";
private static final String KEY_STORE_NAME = "client_ks.bks";
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(MyApplication.getContext().getAssets().open(KEY_STORE_NAME), CLIENT_KEY_STORE_PASSWORD.toCharArray());
//信任证书
keyStoreTwo.load(MyApplication.getContext().getAssets().open(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);
socket = (SSLSocket) sslContext.getSocketFactory().createSocket("localhost", 8090);
// socket.startHandshake();
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 {
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write("Hello".getBytes());
bufferedOutputStream.flush(); // 第一次发送消息,长连接实现方式
Thread.sleep(1); // 必须加休眠,否则第二个文件流会发生错乱
characterMsgReader(inputStream); // 从服务端接收消息
bufferedOutputStream.write("World".getBytes());
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);
}
}
}
二、CA签发密钥和证书
1、将CA server端证书添加到serverTrust_ks.jks文件中
命令:keytool -import -keystore serverTrust_ks.jks -storepass client -file server.crt
结果:会生成serverTrust_ks.jks密钥文件
操作:将生成的serverTrust_ks.jks密钥文件配置到客户端
2、将CA签发密钥转换为pfx格式密钥
命令:openssl pkcs12 -export -inkey client.key -in client.crt -out client.pfx
结果:会生成client.pfx证书文件
(注:如果没有OpenSSL命令则先下载OpenSSL包)
3、将pfx格式文件转换为Android支持的bks格式文件
(转换工具详见:jks文件转bks文件.zip-Java文档类资源-CSDN下载)
操作:将生成的client_ks.bks密钥文件配置到客户端
4、客户端完整代码(代码中的业务根据具体需求进行修改)
private static Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnClient = findViewById(R.id.btn_client);
btnClient.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
thread = new Thread(new ClientOne());
thread.start();
}
});
}
public static class ClientOne implements Runnable {
private static final String TLS = "TLS";
private static final String PROVIDER = "X509";
private static final String STORE_TYPE = "BKS";
private static final String TRUST_STORE_NAME = "serverTrust_ks.bks";
private static final String KEY_STORE_NAME = "client_ks.bks";
private static final String CLIENT_KEY_STORE_PASSWORD = "screen"; //密码
private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "client";//密码
private static final int PORT = 28007;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@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(MyApplication.getContext().getAssets().open(KEY_STORE_NAME), CLIENT_KEY_STORE_PASSWORD.toCharArray());
//信任证书
keyStoreTwo.load(MyApplication.getContext().getAssets().open(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);
socket = (SSLSocket) sslContext.getSocketFactory().createSocket(HOST, PORT);
// socket.startHandshake();
socket.setKeepAlive(true);
// socket.setSoTimeout(10000);
//不关闭连接(长连接)
while (true) {
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
byteMsgWriter(outputStream, inputStream);
}
} catch (Exception e) {
e.printStackTrace();
try {
if (socket != null) {
socket.close();
System.out.println("客户端关闭");
IS_OK = true;
}
if (!thread.isInterrupted()) {
thread.interrupt();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
/**
* 字节流-发送给服务端
*/
public static void byteMsgWriter(OutputStream outputStream, InputStream inputStream) throws Exception {
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
if (IS_OK) {
bufferedOutputStream.write(HexUtil.decodeHex("03000201000000004F4B"));
bufferedOutputStream.flush(); // 第一次发送消息,长连接实现方式
IS_OK = false;
Thread.sleep(10); // 必须加休眠,否则第二个文件流会发生错乱
}
characterMsgReader(inputStream, bufferedOutputStream); // 从服务端接收消息
}
/**
* 字符流-解析服务端的回执消息-不间断解析
*/
public static void characterMsgReader(InputStream inputStream, BufferedOutputStream bufferedOutputStream) throws Exception {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] chars = new char[8];
bufferedReader.read(chars, 0, chars.length);
//得到8位请求头的数据包
String packet = HexUtil.encodeHexStr(String.valueOf(chars));
//解析得到命令码
String commandCode = packet.substring(0, 2);
//得到数据长度
int dataLen = Integer.parseInt(packet.substring(2, 6), 16);
switch (commandCode) {
case "00":
//心跳检测
Log.e("MainActivity", "心跳检测");
bufferedOutputStream.write("Yes".getBytes(StandardCharsets.UTF_8));
bufferedOutputStream.flush(); // 第一次发送消息,长连接实现方式
// Thread.sleep(10); // 必须加休眠,否则第二个文件流会发生错乱
break;
case "01":
//业务
System.out.println("业务操作");
break;
case "02":
//断开连接
System.out.println("断开连接");
break;
case "03":
//确认授权
System.out.println("确认授权");
break;
case "F1":
//WIFI
System.out.println("WIFI");
break;
case "F2":
//蓝牙
System.out.println("蓝牙");
break;
}
}