当前位置: 首页 > 知识库问答 >
问题:

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

章睿
2023-03-14

在我的Android应用程序中,我必须与https web服务通信并读取响应。

我已经通知服务器使用TLS 1.2配置SSL。

我正在使用以下示例代码与服务连接(https get Request),但只有运行Android 5.0或更高版本的设备才能成功通信和读取响应......

该版本(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();
    }

问题一

想知道我在这里是否做错了什么(如果我的代码中缺少任何其他参数,这些参数将支持5.0以上的旧版本的Android以支持TLS 1.2 Web服务通信)?

问题2

我刚在谷歌上找到了这份文件。

那里说ANDROID中与TLS 1.2通信的最低支持浏览器是“GOOGLE Android 5.0 OS Browser”。那么尝试通过代码(应用程序)连接时是否适用相同的限制?

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

示例异常堆栈跟踪如下

Android2.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)

Android3.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)

编辑

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

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个答案

龙景澄
2023-03-14

以下是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()
    }
}
严玉泽
2023-03-14

根据Android开发者留档TLS 1.2是可用的,并启用API级别20(Android 4.4可穿戴)的设备:

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

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

我的个人经验是,一些4.4设备支持TLS 1.2,但未启用它。您可以尝试通过在使用的SSLSocket上调用setEnabledProtocol(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()));
    ...
 类似资料:
  • 我对spring boot和SSL还很陌生。我已经开发了一个SSL rest web服务器,它具有服务器密钥存储库、私钥和服务器CA,并且能够正确地处理相互X.509证书身份验证。我已经测试过,它可以与Postman和Python客户机一起工作HTTPS请求,并且所有工作都正常,服务器和客户机都可以成功地交换和验证对方的证书。 但我的情况有点不同, 我已经创建并连接了一个Postgresql数据库

  • 我有一个关于SSLConnection的问题,在java中,我为客户端编写了下面的代码(这个应用程序必须连接到服务器,我有支持ssl的服务器),但我得到了这样的错误“javax.net.ssl.sslException:Connection Hays Shutdown:javax.net.ssl.sslhandShakeException:sun.security.validator.valida

  • 问题内容: 我正在尝试通过SSL连接使用socket.io设置服务器到服务器的链接。这是我的例子: 在没有SSL的情况下运行时,代码工作正常。我怀疑这可能是我的自签名证书未被接受,但是我不知道如何使客户接受它。 请告诉我如何:1.接受自签名SSL证书。或2.以其他方式帮助我完成这项工作。 提前致谢。 问题答案: 经过更多搜索后,将其添加到客户端中使其可以工作: require(’https’)。g

  • 我实际上在研究微服务,我面临一个问题。 上下文 我正在开发两个微服务: 用户管理,基于spring,使用MySQL数据库 计划管理,基于ASP.NET与SQL Server数据库。此服务的唯一访问点是列出一些RESTFULendpoint的API,如 计费管理,基于MongoDB的node.js。 问题 > 我该怎么做才能只允许通过用户服务访问规划信息,而不耦合这两个服务?知道以后可以从其他地方访

  • 在Openshift中,我有两个需要彼此通信的服务,我想知道您是否知道一种方法来做到这一点,而不公开这两个微服务。 示例: 服务A需要在服务B上请求一个endpoint,为此,我正在使用DNS,但要做到这一点,我需要公开服务B。 有什么方法可以在不公开服务B的情况下完成此场景? 要公开服务A,我运行命令:“oc expose Service/servicea”

  • 在 Ark 服务机制 中,我们详细介绍了如何引用和发布插件服务,主要是解决 Plugin 和 Biz 的通信问题;为了解决 Biz 之间的通信问题,SOFAArk 引入了 SOFABoot 提供的 SofaService/SofaReference 编程界面;下面介绍其使用方法。 引入依赖 引入 runtime-sofa-boot-plugin 依赖,如果应用基于 Spring Boot 1.x