Android实现SSL Socket双向认证

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;
        }
    }

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SSL双向认证是指在SSL连接中,不仅客户端要验证服务器的身份,服务器也要验证客户端的身份。Java实现SSL双向认证一般需要以下步骤: 1. 创建KeyStore和TrustStore,分别存储客户端和服务器的证书和私钥。可以使用keytool工具来创建这些文件。 2. 在服务器端,创建SSLContext对象,并为其指定KeyManagerFactory和TrustManagerFactory。这些工厂类可以使用KeyStore和TrustStore来初始化。 3. 在客户端,创建SSLContext对象,并为其指定TrustManagerFactory。这个工厂类可以使用TrustStore来初始化。 4. 在服务器端,创建ServerSocket并开始监听客户端连接。当客户端连接到服务器时,服务器会为客户端创建一个SSLSocket并开始SSL握手。 5. 在客户端,创建SSLSocket并连接到服务器。客户端和服务器会进行SSL握手,包括交换证书和验证身份等步骤。 6. 客户端和服务器建立SSL连接后,可以开始进行安全通信。 下面是一个简单的Java SSL双向认证的示例代码: 服务器端: ```java KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("server.jks"), "password".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(keyStore, "password".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(8443); while (true) { SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); // 处理客户端连接 } ``` 客户端: ```java KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("client.jks"), "password".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket("localhost", 8443); // 发送和接收数据 ``` 需要注意的是,这只是一个简单的示例代码,实际中还需要考虑证书的管理、密码的安全等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值