Android 4.4.2 SSL 握手中止

Posted

技术标签:

【中文标题】Android 4.4.2 SSL 握手中止【英文标题】:Android 4.4.2 SSL handshake aborted 【发布时间】:2016-04-02 07:00:11 【问题描述】:

代码适用于我的 Genymotion android 4.4.4 模拟器,但不适用于我正在使用的设备 (4.4.2)。

我尝试了很多“信任所有证书”的解决方法,但都无济于事(我认为这不是问题,因为证书是 AOK)。

我想我已经识别了密码(使用我桌面上的 wireshark 跟踪); TLS 0x00 0x1E 这似乎有点少见?

知道如何解决吗?

这是我的代码

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();
String baseURL = "https://mysite.co.uk/api/";
HttpGetHC4 request = new HttpGetHC4(baseURL + "/authenticate?user=abcd&password=1234");
CloseableHttpResponse response = client.execute(request);

还有错误;

 javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: Failure in SSL library, usually a protocol error
 error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
     at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:449)
     at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:340)
     at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:281)
     at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:124)
     at org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:322)
     at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:373)
     at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:225)
     at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
     at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
     at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
     at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:178)
     at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
     at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
     at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.java:239)
     at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.java:189)
     at android.os.AsyncTask.finish(AsyncTask.java:632)
     at android.os.AsyncTask.access$600(AsyncTask.java:177)
     at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
     at android.os.Handler.dispatchMessage(Handler.java:102)
     at android.os.Looper.loop(Looper.java:136)
     at android.app.ActivityThread.main(ActivityThread.java:5017)
     at java.lang.reflect.Method.invokeNative(Native Method)
     at java.lang.reflect.Method.invoke(Method.java:515)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
     at dalvik.system.NativeStart.main(Native Method)
 Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: Failure in SSL library, usually a protocol error
 error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
     at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
     at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:406)
    ... 25 more

编辑

尝试使用不同的技术/库;

        RequestQueue queue = Volley.newRequestQueue(this);
        String url ="https://mysite.co.uk/api/authenticate?user=abcd&password=1234";

        // Request a string response from the provided URL.
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() 
                    @Override
                    public void onResponse(String response) 
                        // Display the first 500 characters of the response string.
                        Log.d("response: ", response);
                    
                , new Response.ErrorListener() 
            @Override
            public void onErrorResponse(VolleyError error) 
                Log.d("response: ", error.toString());
            
        );
        // Add the request to the RequestQueue.
        queue.add(stringRequest);

返回;

D/response:: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ad51be0: Failure in SSL library, usually a protocol error
D/response:: error:140740B5:SSL routines:SSL23_CLIENT_HELLO:no ciphers available (external/openssl/ssl/s23_clnt.c:486 0x684dfce0:0x00000000)

或者使用来自Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error 的 NoSSLv3SocketFactory hack;

        HttpStack stack = new HurlStack(null, new NoSSLv3SocketFactory());
        RequestQueue queue = Volley.newRequestQueue(this, stack);

返回;

D/response:: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ae51d30: Failure in SSL library, usually a protocol error
D/response:: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)

【问题讨论】:

我猜你搜索了很多问题,但你看到了吗?:***.com/questions/29916962/… 我做到了,但是我似乎无法弄清楚如何将 NoSSLv3SocketFactory 应用于 CloseableHttpClient。如果我尝试类似SSLSocketFactory nosslv3 = new NoSSLv3SocketFactory(sslc.getSocketFactory()); CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(nosslv3).build(); 我会得到setSSLSocketFactory (org.apache.http.conn.socket.LayeredConnectionSocketFactory in HttpClientBuilder cannot be applied to javax.net.ssl.SSLSocketFactory 0x00,0x1E 是 TLS_KRB5_WITH_DES_CBC_SHA 并且确实非常罕见而且非常不安全。我怀疑这个密码是否适用于 Android 4.4.4。相反,我会假设服务器名称指示存在问题,但只有在对目标站点了解得更多的情况下才能说得更多。检查SSLLabs 以了解与特定 Android 版本相关的问题或该网站是否需要 SNI。 插入该站点并没有返回任何我可以看到的奇怪内容。似乎支持 TLS 1.0、TLS 1.1 和 TLS 1.2。在握手模拟下,它建议使用 TLS 1.2 w/ciper;安卓 4.4.2 的 TLS_RSA_WITH_RC4_128_SHA? 我现在被引导相信设备不支持站点支持的任何密码。为java找到一种明确声明这一点的方法会很棒吗?在我工作的那一刻,我不得不创建一个 http->https apache 代理 【参考方案1】:

Android 使用不同的协议进行网络操作。

不同安卓版本的默认配置。

我找到了 OkHttpClient 的解决方案。

Protocol    Supported (API Levels)  Enabled by default (API Levels)

SSLv3       1–25                    1–22
TLSv1       1+                      1+
TLSv1.1     16+                     20+
TLSv1.2     16+                     20+

所以我们必须在 Android VERSION >= 16 & VERSION

创建java文件Tls12SocketFactory.java

/**
 * Enables TLS v1.2 when creating SSLSockets.
 * <p/>
 * For some reason, android supports TLS v1.2 from API 16, but enables it by
 * default only from API 20.
 * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
 * @see SSLSocketFactory
 */
public class Tls12SocketFactory extends SSLSocketFactory 
    private static final String[] TLS_V12_ONLY = "TLSv1.2";

    final SSLSocketFactory delegate;

    public Tls12SocketFactory(SSLSocketFactory base) 
        this.delegate = base;
    

    @Override
    public String[] getDefaultCipherSuites() 
        return delegate.getDefaultCipherSuites();
    

    @Override
    public String[] getSupportedCipherSuites() 
        return delegate.getSupportedCipherSuites();
    

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException 
        return patch(delegate.createSocket(s, host, port, autoClose));
    

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException 
        return patch(delegate.createSocket(host, port));
    

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException 
        return patch(delegate.createSocket(host, port, localHost, localPort));
    

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException 
        return patch(delegate.createSocket(host, port));
    

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException 
        return patch(delegate.createSocket(address, port, localAddress, localPort));
    

    private Socket patch(Socket s) 
        if (s instanceof SSLSocket) 
            ((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY);
        
        return s;
    

将此方法放在代码中的某个位置。

public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) 
        if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) 
            try 
                SSLContext sc = SSLContext.getInstance("TLSv1.2");
                sc.init(null, null, null);
                client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));

                ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                        .tlsVersions(TlsVersion.TLS_1_2).build();

                List<ConnectionSpec> specs = new ArrayList<>();
                specs.add(cs);
                specs.add(ConnectionSpec.COMPATIBLE_TLS);
                specs.add(ConnectionSpec.CLEARTEXT);

                client.connectionSpecs(specs);
             catch (Exception exc) 
                Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
            
        

        return client;
    

    public OkHttpClient getNewHttpClient() 
        OkHttpClient.Builder client = new OkHttpClient.Builder().followRedirects(true).followSslRedirects(true)
                .retryOnConnectionFailure(true).cache(null).connectTimeout(5, TimeUnit.SECONDS)
                .writeTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS);

        return enableTls12OnPreLollipop(client).build();
    

如下创建 OkHttp 实例:

private OkHttpClient getNewHttpClient() 
    OkHttpClient.Builder client = new OkHttpClient.Builder()
            .followRedirects(true)
            .followSslRedirects(true)
            .retryOnConnectionFailure(true)
            .cache(null)
            .connectTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS);

    return enableTls12OnPreLollipop(client).build();

像这样使用客户端对象:

OkHttpClient client = getNewHttpClient();
Request.Builder requestBuilder = new Request.Builder();
URL url = new URL("YOUR_URL_LINK");
Request request = requestBuilder.url(url).build();
Call call = client.newCall(request);
Response response = call.execute();

THANKS

【讨论】:

【参考方案2】:

试试这个:

public class ClientSSLSocketFactory 

    public static SSLSocketFactory getSocketFactory(Context context) 
        try 
            X509TrustManager tm = new X509TrustManager() 
                public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException 
                

                public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException 
                

                public X509Certificate[] getAcceptedIssuers() 
                    return new X509Certificate[0];
                
            ;
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[]tm, new SecureRandom());

            SSLSocketFactory ssf = SSLCertificateSocketFactory.getDefault(10000, new SSLSessionCache(context));
            return ssf;
         catch (Exception ex) 
            Log.e("ssl", "Error during the getSocketFactory");
            ex.printStackTrace();
            return null;
        
    

在创建队列时:

sRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, ClientSSLSocketFactory.getSocketFactory()));

希望对你有帮助。

【讨论】:

【参考方案3】:

我认为问题在于基于此类字符串的 SSL v3,这表明它正在尝试使用 SSL v3: SSL23_GET_SERVER_HELLO:sslv3

SSL v3 被认为是不安全的,因此在大多数现代软件上都被禁用(即使在您原本期望它可以工作的地方,许多公司也只是简单地将其移除)。我还从堆栈跟踪中看到代码使用了 apache http 客户端: org.apache.http.impl.client

所以你必须以某种方式阻止 apache http 客户端使用 ssl v3。 Apache http 客户端与标准 Java SSL/TLS 库分开存在。

还有一个没有收到答案的 *** 问题: How do disable SSLv3 in Apache HttpClient

这个链接看起来最有希望: https://discretemkt.wordpress.com/2014/11/16/commons-httpclient-can-disable-sslv3/

在那里,关键线是

Protocol.registerProtocol(scheme, customHttps);

该调用似乎允许您绕过现有的 ssl 工厂。如果您先运行该代码,它可能会工作,假设 apache http 客户端版本是兼容的。

另请注意,如今 TLS 1.0 也被认为是不安全的。 TLS 1.2 和 1.3 以及现在的标准。

【讨论】:

以上是关于Android 4.4.2 SSL 握手中止的主要内容,如果未能解决你的问题,请参考以下文章

javax.net.ssl.SSLProtocolException: SSL 握手中止: ssl=0x7fa2258640: SSL 库失败,通常是协议错误

SSL握手中止:ssl = 0x55ad47e080:系统调用期间的I / O错误,对等方重置连接

IOException:javax.net.ssl.SSLHandshakeException:SSL 握手中止:ssl=0xafdb8e00:系统调用期间的 I/O 错误,对等方重置连接

com.android.volley.NoConnectionError:javax.net.ssl.SSLHandshakeException:javax.net.ssl.SSLProtocolEx

javax.net.ssl.SSLHandshakeException:在 Android 7.0 中握手失败

Android SSL 握手错误