美文网首页
Android SSL 双向验证

Android SSL 双向验证

作者: XBYoung | 来源:发表于2018-10-10 17:50 被阅读150次

    前言

    因为业务需求需要连接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

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

    相关文章

      网友评论

          本文标题:Android SSL 双向验证

          本文链接:https://www.haomeiwen.com/subject/ukcmaftx.html