Android SSL 双向验证

2018-10-10  本文已影响150人  XBYoung

前言

因为业务需求需要连接MQTT双向验证,测试提供了client.pem 、client.key 、root.pem(包含中间证书和根证书)三个文件,在ssl证书根证书位数为1024时使用之前的博客方法没有问题,后面换为2048位时解析证书文件异常,不得不使用新的方法完成双向验证。

报错:java.security.cert.CertificateException: com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: java.lang.RuntimeException: error:0c0000a2:ASN.1 encoding routines:OPENSSL_internal:NOT_ENOUGH_DATA

思路:

因为解析错误,这里尝试更改证书格式,使用cer格式校验虽然不会报以上错误,但是验证失败:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

到这里已经放弃通过修改证书格式来验证了,转而尝试生成client.p12(客户端证书,用于请求的时候给服务器来验证身份之用)和client.truststore(客户端证书库,用于验证服务器端身份,防止钓鱼)这两个文件来完成验证。

问题:

网上很多文章介绍如何转各种格式证书来生成以上两个文件,尝试转换之后各种问题相当头痛,这里直接采用最简单的方法来生成客户端证书和客户端证书库,避免各种眼花缭乱的转换。

准备:

1.提供四个文件 client.pem(客户端证书) 、client.key(客户端私钥) 、server.pem(服务端证书)、server.key(服务端私钥),没有的话手动生成文件,安装openssl

2.生成client.p12(备用)、server.p12

openssl pkcs12 -export -out client.p12 -in client.pem -inkey client.key

openssl pkcs12 -export -out server..p12 -in server..pem -inkey server..key

需要输入密码,记住在调用时使用 KEY_STORE_PASSWORD

3.生成server.cer

openssl pkcs12 -in server.p12 -out server.cer

4.生成client.truststore

keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

需要密码,记住在调用时使用 KEY_STORE_TRUST_PASSWORD

目前为止需要的两个文件已经备好,存入assets目录下

调用:

public class SSLHelper {

private static final String KEY_STORE_TYPE_BKS ="bks";

    private static final String KEY_STORE_TYPE_P12 ="PKCS12";

    public static final String KEY_STORE_CLIENT_PATH ="client.p12";//P12文件

    private static final String KEY_STORE_TRUST_PATH ="client.truststore";//truststore文件

    public static final String KEY_STORE_PASSWORD ="";//P12文件密码

    private static final String KEY_STORE_TRUST_PASSWORD ="123456";//truststore文件密码

    public static SSLSocketFactory getSSLSocketFactory(Context context) {

SSLSocketFactory factory =null;

        try {

// 服务器端需要验证的客户端证书

            KeyStore keyStore =KeyStore.getInstance(KEY_STORE_TYPE_P12);

            // 客户端信任的服务器端证书

            KeyStore trustStore =KeyStore.getInstance(KEY_STORE_TYPE_BKS);

            InputStream ksIn = context.getResources().getAssets()

.open(KEY_STORE_CLIENT_PATH);

            InputStream tsIn = context.getResources().getAssets()

.open(KEY_STORE_TRUST_PATH);

            try {

keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());

                trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());

            }catch (Exception e) {

e.printStackTrace();

            }finally {

try {

ksIn.close();

                }catch (Exception e) {

e.printStackTrace();

                }

try {

tsIn.close();

                }catch (Exception e) {

e.printStackTrace();

                }

}

//信任管理器

            TrustManagerFactory trustManagerFactory =TrustManagerFactory.getInstance(

TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(trustStore);

            //密钥管理器

            KeyManagerFactory keyManagerFactory =KeyManagerFactory.getInstance("X509");

            keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());

            //初始化SSLContext

            SSLContext sslContext =SSLContext.getInstance("TLS");

            sslContext.init(keyManagerFactory.getKeyManagers(),

                    trustManagerFactory.getTrustManagers(), null);

            factory =  sslContext.getSocketFactory();

        }catch (NoSuchAlgorithmException e) {

e.printStackTrace();

        }catch (KeyManagementException e) {

e.printStackTrace();

        }catch (KeyStoreException e) {

e.printStackTrace();

        }catch (IOException e) {

e.printStackTrace();

        }catch (UnrecoverableKeyException e) {

e.printStackTrace();

        }

return factory;

    }

}

在MQTT设置双向验证时调用以下方法,传入MqttConnectOptions对象,retrofit等完成https双向验证,网上有很多例子

private void doubleTrust(MqttConnectOptions options) {

try {

options.setSocketFactory(SSLHelper.getSSLSocketFactory(context.get()));

    }catch (Exception e) {

e.printStackTrace();

        LogUtils.e(TAG,e.getMessage().toString());

    }

}

注意:

密码不正确或者证书错误可能无法完成验证,报以下错误

MqttException (0) - javax.net.ssl.SSLHandshakeException: java.lang.RuntimeException: Failed to load certificates from KeyStore

结语:

参考:https://www.aliyun.com/jiaocheng/19720.html

           https://www.jianshu.com/p/eef28062a2ea

           相关知识并不精通,提供方法为大家做个参考,如有错误或者好的想法,请多多指点。

上一篇下一篇

猜你喜欢

热点阅读