Android Https Web服务通信(SSL / TLS 1.2)

时间:2015-11-06 13:10:48

标签: java android ssl https tls1.2

在我的Android应用程序中,我必须与https网络服务进行通信并阅读回复。

我已通过 TLS 1.2 通知配置 SSL 的服务器。

我使用以下示例代码连接服务(https get request),但只有运行 Android 5.0 或更高版本的设备才能成功通信并阅读响应.... / p>

该版本(Android 5.0)以下的所有其他设备在尝试建立连接时无法进行通信并抛出IOException ...

    HttpResponse response = null;
    try 
    {        
        HttpClient client = new DefaultHttpClient();

        HttpGet request = new HttpGet();
        request.setURI(new URI("https://domain.co.uk/services/pay.aspx?param1=val1&param2=val2"));

        response = client.execute(request);

        HttpEntity entity = response.getEntity();
        String responseString = EntityUtils.toString(entity); 
        String decodedResStr = URLDecoder.decode(responseString, "UTF-8");  

        Log.v("AppState", "Response: " + decodedResStr);
    }  
    catch (Exception e) 
    {
        e.printStackTrace();
        Log.v("AppState", "Exception: " + e.getMessage() )
    }

    // HttpURLConnection urlConnection = null;
    HttpsURLConnection urlConnection = null;

    try
    {
        URL url = new URL("https://domain.co.uk/services/pay.aspx?param1=val1&param2=val2");
        //urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection = (HttpsURLConnection) url.openConnection();

        InputStream in = new BufferedInputStream(urlConnection.getInputStream());

    }
    catch (Exception e)
    {
        e.printStackTrace();
        Log.v("AppState", "Exception: " + e.getMessage() )
    }
    finally
    {
        urlConnection.disconnect();
    }

问题1

想知道我在这里做错了什么(如果我的代码中缺少任何附加参数,支持旧版本的Androids而不是5.0以支持TLS 1.2 Web服务通信)?

问题2

我只是用谷歌搜索,发现了DOCUMENT

在那里它表示ANDROID中与TLS 1.2通信的最低支持浏览器是" GOOGLE Android 5.0 OS浏览器"。那么在尝试通过代码(Apps)进行连接时是否也适用相同的限制?

如果是这样,如果想与这个Web服务(支持TLS 1.2 Web服务的最低版本的android)进行通信,那么我应该支持的最低Android版本是什么?

示例异常堆栈跟踪如下

Android 2.2模拟器

    11-06 12:51:01.885: W/System.err(352): java.io.IOException: SSL handshake failure: I/O error during system call, Unknown error: 0
    11-06 12:51:01.895: W/System.err(352):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.nativeconnect(Native Method)
    11-06 12:51:01.895: W/System.err(352):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:316)
    11-06 12:51:01.895: W/System.err(352):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.getSecureSocket(HttpConnection.java:168)
    11-06 12:51:01.905: W/System.err(352):  at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:399)
    11-06 12:51:01.915: W/System.err(352):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1152)
    11-06 12:51:01.915: W/System.err(352):  at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:253)
    11-06 12:51:01.915: W/System.err(352):  at com.serviceapp.WSHelperHttpURLConnection.executeAndroid(WSHelperHttpURLConnection.java:93)
    11-06 12:51:01.915: W/System.err(352):  at com.serviceapp.HttpPage$1$1.run(HttpPage.java:69)
    11-06 12:51:01.915: W/System.err(352):  at java.lang.Thread.run(Thread.java:1096)

Android 3.0模拟器

11-06 12:56:22.917: W/System.err(447): javax.net.ssl.SSLException: Connection closed by peer
11-06 12:56:22.927: W/System.err(447):  at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
11-06 12:56:22.927: W/System.err(447):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:485)
11-06 12:56:22.927: W/System.err(447):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:323)
11-06 12:56:22.927: W/System.err(447):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.setupSecureSocket(HttpConnection.java:167)
11-06 12:56:22.937: W/System.err(447):  at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:479)
11-06 12:56:22.937: W/System.err(447):  at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeConnection(HttpsURLConnectionImpl.java:428)
11-06 12:56:22.937: W/System.err(447):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.retrieveResponse(HttpURLConnectionImpl.java:1038)
11-06 12:56:22.937: W/System.err(447):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:523)
11-06 12:56:22.937: W/System.err(447):  at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:283)
11-06 12:56:22.947: W/System.err(447):  at com.serviceapp.WSHelperHttpURLConnection.executeAndroid(WSHelperHttpURLConnection.java:93)
11-06 12:56:22.947: W/System.err(447):  at com.serviceapp.HttpPage$1$1.run(HttpPage.java:69)
11-06 12:56:22.947: W/System.err(447):  at java.lang.Thread.run(Thread.java:1020)

EDITS

使用Robert的MySSLSocketFactory类实现Android 4.4.2和Android 5.1.1设备时,这是完整的堆栈跟踪。

11-06 14:26:46.962: W/System.err(14700): java.lang.IllegalArgumentException: protocol TLS1.2 is not supported
11-06 14:26:46.985: W/System.err(14700):    at com.android.org.conscrypt.NativeCrypto.checkEnabledProtocols(NativeCrypto.java:879)
11-06 14:26:46.985: W/System.err(14700):    at com.android.org.conscrypt.OpenSSLSocketImpl.setEnabledProtocols(OpenSSLSocketImpl.java:807)
11-06 14:26:46.985: W/System.err(14700):    at com.serviceapp.MySSLSocketFactory.createSocket(WSURlCon.java:99)
11-06 14:26:46.986: W/System.err(14700):    at com.serviceapp.MySSLSocketFactory.createSocket(WSURlCon.java:1)
11-06 14:26:46.986: W/System.err(14700):    at com.android.okhttp.Connection.upgradeToTls(Connection.java:131)
11-06 14:26:46.986: W/System.err(14700):    at com.android.okhttp.Connection.connect(Connection.java:107)
11-06 14:26:46.986: W/System.err(14700):    at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
11-06 14:26:46.987: W/System.err(14700):    at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
11-06 14:26:46.988: W/System.err(14700):    at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
11-06 14:26:46.988: W/System.err(14700):    at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
11-06 14:26:46.990: W/System.err(14700):    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
11-06 14:26:46.990: W/System.err(14700):    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:179)
11-06 14:26:46.991: W/System.err(14700):    at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:246)
11-06 14:26:46.991: W/System.err(14700):    at com.serviceapp.WSURlCon.executeAndroid(WSURlCon.java:33)
11-06 14:26:46.992: W/System.err(14700):    at com.serviceapp.HttpPage$1$1.run(HttpPage.java:74)
11-06 14:26:46.992: W/System.err(14700):    at java.lang.Thread.run(Thread.java:848)

2 个答案:

答案 0 :(得分:9)

根据Android Developer文档,在API级别为20+(Android 4.4可穿戴设备)的设备上可以使用并启用TLS 1.2:

http://developer.android.com/reference/javax/net/ssl/SSLEngine.html

我假设您的测试设备没有使用该API级别,因此您得到的结果是只有5.0个设备可以连接。

我个人的经验是,有些4.4设备支持TLS 1.2但是没有启用。您可以尝试通过调用已使用的SSLSocket上的setEnabledProtocols(new String[]{"TLSv1.2"})来启用它。

这样做的优雅解决方案是使用代理模式实现自己的SSLSocketFactory:

public class MySSLSocketFactory extends SSLSocketFactory {

    SSLSocketFactory sslSocketFactory;

    public MySSLSocketFactory(SSLSocketFactory sslSocketFactory) {
        super();
        this.sslSocketFactory = sslSocketFactory;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return sslSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return sslSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(s, host, port, autoClose);
        socket.setEnabledProtocols(new String[] { "TLSv1.2" });
        return socket;
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(host, port);
        socket.setEnabledProtocols(new String[] { "TLSv1.2" });
        return socket;
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException,
            UnknownHostException {
        SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(host, port, localHost, localPort);
        socket.setEnabledProtocols(new String[] { "TLSv1.2" });
        return socket;
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(host, port);
        socket.setEnabledProtocols(new String[] { "TLSv1.2" });
        return socket;
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
            throws IOException {
        SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(address, port, localAddress, localPort);
        socket.setEnabledProtocols(new String[] { "TLSv1.2" });
        return socket;
    }

你可以这样使用它:

    ...
    urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(new MySSLSocketFactory(urlConnection.getSSLSocketFactory()));
    ...

答案 1 :(得分:0)

以下是罗伯特(Robert)回答的科特林版本,它解决了我的问题:

import java.net.InetAddress
import java.net.Socket
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory

class MySSLSocketFactory(socket_factory :SSLSocketFactory):
      SSLSocketFactory()
{
    val sslSocketFactory :SSLSocketFactory

    init
    {
        this.sslSocketFactory = socket_factory
    }

    override fun getDefaultCipherSuites(): Array<String>
    {
        return this.sslSocketFactory.getDefaultCipherSuites()
    }

    override fun createSocket(socket: Socket?, host: String?, port: Int, autoclose: Boolean): Socket
    {
        val socket: SSLSocket = this.sslSocketFactory.createSocket(socket, host, port, autoclose) as SSLSocket
        socket.setEnabledProtocols(arrayOf("TLSv1.2"))
        return socket
    }

    override fun createSocket(host: String?, port: Int): Socket
    {
        val socket: SSLSocket = this.sslSocketFactory.createSocket(host, port) as SSLSocket
        socket.setEnabledProtocols(arrayOf("TLSv1.2"))
        return socket
    }

    override fun createSocket(host: String?, port: Int, local_host: InetAddress?, local_port: Int): Socket {
        val socket = sslSocketFactory.createSocket(host, port, local_host, local_port) as SSLSocket
        socket.enabledProtocols = arrayOf("TLSv1.2")
        return socket
    }

    override fun createSocket(host: InetAddress?, port: Int): Socket
    {
        val socket = sslSocketFactory.createSocket(host, port) as SSLSocket
        socket.enabledProtocols = arrayOf("TLSv1.2")
        return socket
    }

    override fun createSocket(address: InetAddress?, port: Int, local_address: InetAddress?, local_port: Int): Socket
    {
        val socket = sslSocketFactory.createSocket(address, port, local_address, local_port) as SSLSocket
        socket.enabledProtocols = arrayOf("TLSv1.2")
        return socket
    }

    override fun getSupportedCipherSuites(): Array<String>
    {
        return this.sslSocketFactory.getSupportedCipherSuites()
    }
}