2012-06-21 123 views
6

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

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)。它也可以在iOS设备上使用与Android相同的webkit,这很奇怪。 Opera Mini浏览器也可以正常工作。

Here's the website.

我已经通过在客户端证书密钥库和忽略无效的客户端证书,但没有任何结果尝试的解决方法。但是看起来证书本身不是问题。

我陷入了僵局。任何人都可以提供任何指导我如何才能使这个工作?

回答

2

您是如何访问此网站的?通过Android浏览器?的WebView?或者HttpClient/HTTPSURLConnection?它似乎只响应SSL3,你必须强迫你的客户使用它。

+0

起初,我试图通过HttpClient的访问。在尝试了几项工作后(禁用SSL证书验证,将证书添加到本地密钥库等)后,我确定问题不在于证书本身。然后我尝试通过浏览器访问它,但它也不会在那里工作。我没有尝试过WebView,因为我假设如果浏览器无法连接,WebView也无法连接。 我回去接受了一些答案。接受程度低的原因是因为大部分答案都没有解决我的问题。 –

+0

问题是您的服务器只响应单个SSL版本SSL v3。如果使用HttpClient,则需要创建自己的SocketFactory并手动设置启用的协议版本。类似的东西在这里描述:http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String []) –

+0

嗨尼古拉,经过一些研究,我已经找到你所描述的。服务器不支持TLS,这是应用程序试图握手的内容。我现在正在研究如何强制使用SSLv3,但我没有太多的运气(这比我有限的Java经验习惯稍高一点)。你认为你可以为我提供一个非常基本的代码片段吗? –

4

我找到了解决方案(谢谢你尼古拉指着我在正确的方向)。

问题有两方面:一是它返回了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"; 
    } 
} 
+2

这是正确的,但您的TrustManager会信任任何和所有证书,这不是一个好主意。为什么Android不喜欢服务器证书?如果这是因为它是由不可信的CA颁发的,则可以使用它轻松创建一个信任库,并让您的SocketFactory使用它。 –

+0

就目前而言,这很好......我在开发中根本无法取得进展,因为我无法通过Web服务器进行身份验证,并且100%的应用程序都涉及与服务器的通信。后来我会回来处理创建一个自定义的信任存储,但现在我很高兴只是有工作代码。它让老板和客户不在我的背上。 ;-) –

+2

通过使用这个'TrustManager',你打开自己的MITM攻击。 – Bruno

-2

使用此方法并调用此方法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()); 
} 

}