Android 实现SLL Socket 双向认证(原有格式为PEM(私钥.key和证书.pem))

为了实现TLS socket 消息认证。
Server需要:
1KeyStore: 其中保存服务端的私钥
2Trust KeyStore:其中保存客户端的授权证书
同样,Client需要:
1KeyStore:其中保存客户端的私钥
2Trust KeyStore:其中保存服务端的授权证书

 

但是Android 确实只能识别BKS 格式的证书,如果为其他格式的证书需要转换,

其中用到的转换工具为Portecle(免费):

官方下载地址为:https://sourceforge.net/projects/portecle/

百度网盘下载地址:链接: https://pan.baidu.com/s/1r4viK9X6wvnqYXrOzaVhZQ 提取码: kahh

一、证书格式转换

1.Portecle的使用步骤如下:

  1. 打开Portecle工具后,选择的File- - >New Keystore- - > BKS 的。
  2. 创建好bks格式的Keystore文件后,点击 Tools- - > Import Key Pair 即可导入密钥对,先选择好.pem文件,随后会提示选择 .key文件
  3. 配置好密码,或者不设置也行,
  4. 导出该创建的文件,以.bks结尾的文件

      如果在步骤2中无法完成导入.pem证书文件 和选取.key私钥文件,那就需要将.pem证书文件 和.key私钥文件合并要一个密钥库中,如jks或pkcs12(.pfx文件)中。

2.将PEM格式转为jks或pfx

方式一:通过openssl和keytool转换

    openssl可以到官网下载,openssl暂时没有windows版,大伙可以在此处下载Win32OpenSSLwindows版本
    下载完记得配置环境变量,同时也记得配置jdk的环境变量,因为后面需要用到keytool命令
    现在假设你拿到了客户端证书client.crt和client.key(或者client.pem和client.key)
    通过以下命令生成pfx文件,期间会让输入密码,输入你想要的密码即可,假如这里输入的是123456

    openssl pkcs12 -export -out client.pfx -inkey client.key -in client.pem
    或者
    openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt

    执行成功则可以看到client.pfx文件,

   如果需要得到jks格式的证书可以接着再执行如下命令

    keytool -importkeystore -srckeystore client.pfx -destkeystore client.jks -srcstoretype PKCS12 -deststoretype JKS

    执行成功即可看到生成了client.jks,拿到生成的jks就可以通过Portecle工具转换成bks了。

  注:Openssl 使用操作可以参考:https://blog.csdn.net/liao20081228/article/details/77159039

方式二:在线转换

你可以到这里进行格式转换:https://myssl.com/cert_convert.html,在线转换真的超方便。

二、双向认证(客户端实现)

       添加了SSLHelper.java工具类用来生成SSLSocketFactory对象

package com.yf.ssl.client;

import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

/**
 * @description: <功能简述>
 * @Author: YunFeng
 * @CreateTime: 2020-2-13
 */
public class SSLHelper {

    private static final String TAG = "SSLHelper";
    private final static String CLIENT_PRI_KEY = "client_bks.bks";
    private final static String TRUSTSTORE_PUB_KEY = "service_bks.bks";
    private final static String CLIENT_BKS_PASSWORD = "123456";
    private final static String TRUSTSTORE_BKS_PASSWORD = "123456";
    private final static String KEYSTORE_TYPE = "BKS";
    private final static String PROTOCOL_TYPE = "TLS";
    private final static String CERTIFICATE_STANDARD = "X.509";
    private static AssetManager mAssetManager = null;

    public static SSLSocketFactory getSSLCertifcation(Context context) {
        if(context == null){
            Log.w(TAG, "getSSLCertifcation () context is null");
            return null;
        }
        if (mAssetManager == null) {
            mAssetManager = context.getAssets();
        }
        Log.d(TAG, "getSSLCertifcation () ");
        try {
            SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            KeyStore keyClient = KeyStore.getInstance(KEYSTORE_TYPE);
            InputStream in_client_bks = mAssetManager.open(CLIENT_PRI_KEY);
            keyClient.load(in_client_bks, null);//该证书未设置密码 以下类似
            try {
                keyManagerFactory.init(keyClient, null);
            } catch (UnrecoverableKeyException e) {
                Log.e(TAG, "getSSLCertifcation() UnrecoverableKeyException:" + e.toString());
            }


            KeyStore tClient = KeyStore.getInstance(KEYSTORE_TYPE);
            InputStream trustClient = mAssetManager.open(TRUSTSTORE_PUB_KEY);
            tClient.load(trustClient, null);

//            另一种加载方式,不需要是bks的格式,但是仅仅可以加载trust
//            InputStream trustIn = mAssetManager.open("service_cert.pem");
//            CertificateFactory cf = CertificateFactory.getInstance(CERTIFICATE_STANDARD);
//            Certificate clientC = cf.generateCertificate(trustIn);
//            tClient.load(null,null);
//            tClient.setCertificateEntry("clientT",clientC);

            // 创建一个 TrustManager 仅把 Keystore 中的证书 作为信任的锚点
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(tClient);

            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

//              配置所有证书的 认证管理器  未使用
//            javax.net.ssl.TrustManager[] trustAllCerts = {new TrustAllTrustManager()};
//            sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }catch (Exception e){
            Log.e(TAG, "getSSLCertifcation() Exception:" + e.toString());
        }
        return  null;
    }
}

然后即可以得到SSLSocket

SSLSocket sslSocket = (SSLSocket) SSLHelper.getSSLCertifcation(context).createSocket(IP, PORT)

由于该项目中用到的是OKSocket框架,其实这里更简单:

  ConnectionInfo mInfo = new ConnectionInfo(ip, port);
        SSLSocketFactory sSLSocketFactory= SSLHelper.getSSLCertifcation(context);
        if(sSLSocketFactory == null){
            Log.w(TAG,"没有加载到证书");
        }
        OkSocketSSLConfig mOkSocketSSLConfig =new OkSocketSSLConfig.Builder()
                  .setProtocol("TLS")
                  .setCustomSSLFactory(sSLSocketFactory)//配置上SSLSocketFactory即可
                  .build();
        OkSocketOptions mOkOptions = new OkSocketOptions.Builder()
        .setReconnectionManager(new NoneReconnect())
                  .setConnectTimeoutSecond(5)//超时时间
                  .setCallbackThreadModeToken(new OkSocketOptions.ThreadModeToken() {
                      @Override
                      public void handleCallbackEvent(ActionDispatcher.ActionRunnable runnable) {
                          handler.post(runnable);
                      }
                  }).setSSLConfig(mOkSocketSSLConfig)//配置OkSocketSSLConfig即可
                  .build();
        IConnectionManager mManager = OkSocket.open(mInfo).option(mOkOptions);

三、双向认证(服务点实现)

服务端就不详细介绍,有兴趣的可以参考他人的文章,写的非常好:https://blog.csdn.net/albb_/article/details/90637338

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值