【问题标题】:SSL Issue with AndroidAndroid 的 SSL 问题
【发布时间】:2012-06-23 20:39:06
【问题描述】:

我的 Android 上有一个非常具体的 SSL 问题。如果我尝试通过代码访问特定网站,则会收到以下错误:

SSL handshake failure: Failure in SSL library, usually a protocol error
error:140773F2:SSL routines:SSL23_GET_SERVER_HELLO: sslv3 alert unexpected message (external/openssl/ssl/s23_cInt.c:500 0xaf076228:0x00000000)

无论构建如何,我都会得到这个...我已经在 API 级别 1.5、1.6、2.2 和 4.0 上尝试过,每次都得到相同的结果。

经过一些故障排除后,我尝试通过浏览器访问该网站,但出现以下错误:

Data connectivity problem
A secure connection could not be established.

事情是这样的……该网站在 Windows 浏览器上打开得很好(在 Firefox、IE 和 Chrome 上测试)。它在使用与 Android 相同的 webkit 的 iOS 设备上也可以正常打开,这很奇怪。该网站在 Opera Mini 浏览器上也可以正常运行。

Here's the website.

我尝试了通过将客户端证书添加到密钥库并忽略无效客户端证书的变通方法,但没有任何结果。但是,证书本身似乎不是问题。

我陷入了僵局。任何人都可以就我如何使它工作提供任何指导吗?

【问题讨论】:

    标签: android ssl openssl


    【解决方案1】:

    您是如何访问本网站的?通过安卓浏览器?网络视图?还是 HttpClient/HTTPSURLConnection?它似乎只响应 SSL3,您必须强制您的客户端使用它。

    【讨论】:

    • 最初我尝试通过 HttpClient 访问它。在尝试了几件事以使其正常工作(禁用 SSL 证书验证、将证书添加到我的本地密钥库等)之后,我确定问题不在于证书本身。然后我尝试通过浏览器访问它,但它也无法在那里工作。我没有尝试过 WebView,因为我假设如果浏览器无法连接,WebView 也不能。我回去接受了一些答案。接受度低的原因是大部分答案都没有解决我的问题。
    • 问题是你的服务器只响应单一的 SSL 版本,SSL v3。如果使用 HttpClient,您需要创建自己的 SocketFactory 并手动设置启用的协议版本。这里描述了类似的东西:developer.android.com/reference/javax/net/ssl/…
    • 您好 Nikolay,经过一番研究,我找到了您所描述的内容。服务器不支持 TLS,这是应用程序试图与之握手的。我现在正在研究如何强制 SSLv3,但我运气不佳(这比我有限的 Java 经验习惯的要先进一些)。你觉得你能给我提供一个非常基本的代码sn-p吗?
    • @NikolayElenkov 我面临与上述相同的问题,就我而言,我正在尝试从 Android Webview 访问该网站。如何禁用 SSLv3 协议或在 Webview 中手动设置协议版本?请告诉我
    【解决方案2】:

    我找到了解决方案(感谢 Nikolay 为我指明了正确的方向)。

    问题有两个方面......第一,它返回了一个 Android 不喜欢的站点证书,第二,它只启用了 SSLv3(而不是 TLS)。

    这是我的解决方案。首先我必须创建一个自定义套接字工厂类:

    public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("SSLv3");
    
    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);
    
        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
    
        sslContext.init(null, new TrustManager[] { tm }, null);
    }
    
    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        S.setEnabledProtocols(new String[] {"SSLv3"});
        return S;
    }
    
    @Override
    public Socket createSocket() throws IOException {
        SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket();
        S.setEnabledProtocols(new String[] {"SSLv3"});
        return S;
    }
    

    }

    其次,我在我的代码中定义了这个自定义的 HttpClient:

    public HttpClient getNewHttpClient() {
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
    
            MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    
            HttpParams params = new BasicHttpParams();
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
    
            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            registry.register(new Scheme("https", sf, 443));
    
            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
    
            return new DefaultHttpClient(ccm, params);
        } catch (Exception e) {
            return new DefaultHttpClient();
        }
    }
    

    第三,我调用了自定义的HttpClient并解析了结果:

    public String test(String URIString) {
        HttpClient httpClient = getNewHttpClient();
        String result = "";
        URI uri;
        try {
            uri = new URI(URIString);
        } catch (URISyntaxException e1) {
            return "ERROR";
        }
        HttpHost host = new HttpHost(uri.getHost(), 443, uri.getScheme());
        HttpPost httppost = new HttpPost(uri.getPath());
        try {
            HttpResponse response = httpClient.execute(host, httppost);
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(
                        response.getEntity().getContent()
                    )
                );
                String line = null;
                while ((line = reader.readLine()) != null){
                  result += line + "\n";
                }
    
                return result;
        } catch (ClientProtocolException e) {
            return "ERROR";
        } catch (IOException e) {
            return "ERROR";
        }
    }
    

    【讨论】:

    • 这是正确的,但您的 TrustManager 将信任任何和所有证书,这不是一个好主意。为什么 Android 不“喜欢”服务器证书?如果是因为它是由不受信任的 CA 颁发的,您可以轻松地使用它创建一个信任存储,并让您的 SocketFactory 使用它。
    • 目前还好...我无法在开发中取得进展,因为我无法通过 Web 服务器进行身份验证,并且 100% 的应用程序都涉及与服务器的通信。稍后我会回来处理创建自定义信任存储,但现在我很高兴拥有工作代码。它让老板和客户远离我。 ;-)
    • 通过使用此TrustManager,您将面临 MITM 攻击。
    • 我的服务器设置为仅接受 TLS 1.1 及更高版本。 SSLSocket 中的 setEnabledProtocols 函数适用于 HttpsUrlConnection 和自定义 HttpClient。为 WebView 客户端设置 SSL 协议怎么样? Webview 客户端抛出 onReceivedError 回调,描述为“无法执行 SSL 握手”。
    • 01-29 15:58:00.073 5486 5525 W chromium_net: external/chromium/net/http/http_stream_factory_impl_job.cc:865: [0129/155800:WARNING:http_stream_factory_impl_job.cc(865)] 下降回到 SSLv3,因为主机不支持 TLS:10.209.126.125:443 01-29 15:58:00.083 5486 5525 E chromium_net: external/chromium/net/socket/ssl_client_socket_openssl.cc:792: [0129/155800:ERROR:ssl_client_socket_openssl. cc(792)] 握手失败;返回 0,SSL 错误代码 5,net_error -107
    【解决方案3】:

    使用这个并调用这个方法 HttpsTrustManager.allowAllSSL()

    它将解决问题并且对我来说工作正常。

    公共类 HttpsTrustManager 实现 X509TrustManager {

    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
    
    @Override 
    public void checkClientTrusted( 
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {
    
    } 
    
    @Override 
    public void checkServerTrusted( 
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {
    
    } 
    
    public boolean isClientTrusted(X509Certificate[] chain) {
        return true; 
    } 
    
    public boolean isServerTrusted(X509Certificate[] chain) { 
        return true; 
    } 
    
    @Override 
    public X509Certificate[] getAcceptedIssuers() {
        return _AcceptedIssuers; 
    } 
    
    public static void allowAllSSL() { 
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    
            @Override 
            public boolean verify(String arg0, SSLSession arg1) {
                return true; 
            } 
    
        }); 
    
        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[]{new HttpsTrustManager()};
        } 
    
        try { 
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } 
    
        HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());
    } 
    

    }

    【讨论】:

      【解决方案4】:

      经过两天的努力,这个最终解决方案对我有用-

          private OkHttpClient.Builder getUnsafeOkHttpClient() {
          try {
              // Create a trust manager that does not validate certificate chains
              final TrustManager[] trustAllCerts = new X509TrustManager[]{new X509TrustManager() {
                  public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                  }
      
                  public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                  }
                  public X509Certificate[] getAcceptedIssuers() {
                      return new java.security.cert.X509Certificate[]{};
                  }
              }};
      
              // Install the all-trusting trust manager
              final SSLContext sslContext = SSLContext.getInstance("SSL");
              KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
              KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
              kmf.init(keyStore, "password".toCharArray());
              sslContext.init(kmf.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
              final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
              OkHttpClient.Builder builder = new OkHttpClient.Builder();
              builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
              builder.hostnameVerifier(new HostnameVerifier() {
                  @Override
                  public boolean verify(String hostname, SSLSession session) {
                      return true;
                  }
              });
              ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
                      .allEnabledTlsVersions()
                      .allEnabledCipherSuites()
                      .build();
              ConnectionSpec spec1 = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT)
                      .build();
      
              builder.connectionSpecs(Arrays.asList(spec,spec1));
      
              return builder;
          } catch (Exception e) {
              Logger.e("[DownloadService][getUnsafeOkHttpClient] :", false);
             return new OkHttpClient.Builder();
      
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-08-20
        • 2011-09-24
        • 1970-01-01
        • 1970-01-01
        • 2011-02-18
        • 1970-01-01
        • 2013-07-23
        相关资源
        最近更新 更多