使用 SSLCertificateSocketFactory 的 Android HTTPS SNI 支持

Posted

技术标签:

【中文标题】使用 SSLCertificateSocketFactory 的 Android HTTPS SNI 支持【英文标题】:Android HTTPS SNI support using SSLCertificateSocketFactory 【发布时间】:2014-08-15 08:27:25 【问题描述】:

我正在尝试使用 SSLCertificateSocketFactory.setHostname 添加 SNI 支持,使用 wireshark 我看到客户端和启用 SNI 的服务器之间的通信,客户端 HELLO 转到服务器(设置正确的主机名),服务器响应服务器 Hello 并发送证书给客户端,但是在客户端没有发送证书并且通信/握手停止之后,通过 openssl 命令 openssl s_client -connect theclient -servername thehostname -cert thecertificate 一切顺利,握手成功..

我正在使用套接字,并且在 socket.startHandshake 上出现异常: IOException :javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: 找不到证书路径的信任锚。

           final SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);

            socket = (SSLSocket) sslSocketFactory.createSocket(InetAddress.getByName("theserver"), 443);


            socket.setEnabledProtocols(socket.getSupportedProtocols());

            String[] cipherArray = socket.getEnabledCipherSuites();
            for(int i = 0; i<cipherArray.length; i++) 
                Log.e("","Available Cipher:"+cipherArray[i]);
            
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) 
                sslSocketFactory.setHostname(socket, "thehostname");
             else 
                try 
                    java.lang.reflect.Method setHostnameMethod = socket.getClass().getMethod("setHostname", String.class);
                    setHostnameMethod.invoke(socket, "thehostname");
                 catch (Exception e) 
                    Log.w("", "SNI not useable", e);
                
            
            socket.setSoTimeout(20000);
            socket.setUseClientMode(true);
            Log.i(getClass().toString(), "Connected.");
            socket.startHandshake();
            SSLSession session = socket.getSession();
            boolean secured = session.isValid();

这个IOexception SSLHANDSHAKEEXCEPTION对android开发者的解决方法是https://developer.android.com/training/articles/security-ssl.html#CommonProblems:

“从 InputStream 中获取一个特定的 CA,使用它来创建一个 KeyStore,然后使用它来创建和初始化一个 TrustManager。TrustManager 是系统用来验证来自服务器的证书,并且通过从一个具有一个或多个 CA 的 KeyStore — 这些将是该 TrustManager 信任的唯一 CA。

鉴于新的 TrustManager,该示例初始化了一个新的 SSLContext,它提供了一个 SSLSocketFactory,您可以使用它来覆盖 HttpsURLConnection 中的默认 SSLSocketFactory。这样,连接将使用您的 CA 进行证书验证。”

现在 sslcontext 返回 SSLSOCKETFACTORY,我想使用 SSLCertificateSocketFactory(用于 sethostname 方法)做什么.. 我希望这个问题很清楚,如果没有让我知道或随时让我更清楚

【问题讨论】:

我面临着类似的问题。我将关注这里发生的事情。这是我正在使用的一些 sn-ps 代码的 pastebin:pastebin.com/K18PwtLa 【参考方案1】:

在使用 Android 的 HttpsURLConnection(包括 SNI 支持)时,您可以使用 NetCipher 库来获取现代 TLS 配置。 NetCipher 将HttpsURLConnection 实例配置为使用最受支持的 TLS 版本,以及该 TLS 版本的最佳密码套件。首先,将其添加到您的 build.gradle

compile 'info.guardianproject.netcipher:netcipher:2.0.0-alpha1'

或者您可以下载 netcipher.jar 并将其直接包含在您的应用中。然后代替调用:

HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();

这样称呼:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);

如果你想看看它是如何在代码中完成的,请查看TlsOnlySocketFactory

【讨论】:

这是一个很好的解决方案,有效并拯救了我。来自我的 +1

以上是关于使用 SSLCertificateSocketFactory 的 Android HTTPS SNI 支持的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)