2012-06-21 31 views
6

Mam bardzo specyficzny problem z SSL na moim Androidzie. Jeśli staram się odwiedzić daną stronę internetową za pomocą kodu, pojawia się następujący błąd:Problem z SSL z Androidem

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) 

uzyskać to niezależnie od budowy ... Próbowałem go na poziomie API 1,5, 1,6, 2,2, i 4,0 i za każdym razem otrzymali ten sam wynik.

Po pewnym rozwiązywania problemów Próbowałem znaleźć na stronie internetowej za pośrednictwem przeglądarki i mam następujący błąd:

Data connectivity problem 
A secure connection could not be established. 

Oto rzeczy, choć ... strona otwiera się dobrze w przeglądarkach systemu Windows (testowane na Firefox , IE i Chrome). Otwiera się również dobrze na urządzeniach z iOS, które używają tego samego zestawu webowego, co Android, co jest dziwne. Strona działa również bezproblemowo w przeglądarce Opera Mini.

Here's the website.

Próbowałam obejść przez dodanie certyfikatu klienta do magazynu kluczy i ignorowanie nieprawidłowych certyfikatów klienta, bez żadnych rezultatów. Jednak wydaje się, że sam certyfikat nie jest problemem.

Jestem w impasie. Czy ktokolwiek może udzielić wskazówek, jak mogę to osiągnąć?

Odpowiedz

2

W jaki sposób uzyskujesz dostęp do tej witryny? Przez przeglądarkę Android? WebView? Czy HttpClient/HTTPSURLConnection? Wygląda na to, że odpowiada tylko na SSL3, musisz zmusić swojego klienta, aby z niego korzystał.

+0

Początkowo próbowałem do niego dostęp za pośrednictwem HttpClient. Po wypróbowaniu kilku rzeczy, aby to zadziałało (wyłączenie weryfikacji certyfikatu SSL, dodanie certyfikatu do mojego lokalnego magazynu kluczy itp.) Stwierdziłem, że problem nie dotyczy samego certyfikatu. Następnie spróbowałem uzyskać do niego dostęp za pośrednictwem przeglądarki, ale też tam nie działało. Nie próbowałem WebView, ponieważ zakładam, że jeśli przeglądarka nie może się połączyć, WebView też nie może. Wróciłem i przyjąłem kilka odpowiedzi. Powodem niskiej akceptacji jest to, że większość odpowiedzi nie rozwiązała moich problemów. –

+0

Problem polega na tym, że twój serwer odpowiada tylko na jedną wersję SSL, SSL v3. Jeśli używasz HttpClient, musisz utworzyć własną SocketFactory i ręcznie ustawić włączoną wersję protokołu. Coś podobnego jest opisane tutaj: http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols (java.lang.String []) –

+0

Witam Nikolay, po pewnych badaniach znalazłem dokładnie co opisałeś. Serwer NIE obsługuje TLS, z którym to aplikacja próbuje się uzgadniać. Badam teraz, jak wymusić SSLv3, ale nie mam dużo szczęścia (jest to nieco bardziej zaawansowane, niż przywykłem do mojej ograniczonej obsługi Java). Czy uważasz, że możesz podać mi bardzo prosty fragment kodu? –

4

Znalazłem rozwiązanie (dziękuję Nikolay za skierowanie mnie we właściwym kierunku).

Problem był dwojaki ... Jeden, zwrócił certyfikat witryny, który nie podobał się Androidowi, a po drugie, miał włączony tylko SSLv3 (a nie TLS).

Oto moje rozwiązanie. Najpierw miałem utworzyć klasy fabrycznej gniazdo niestandardowe:

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; 
} 

}

II, miałem ten zwyczaj httpclient zdefiniowany w moim kodu:

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

trzecie, zadzwoniłem zwyczaj HttpClient i przeanalizował wyniki:

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

To prawda, ale Twój TrustManager zaufa wszystkim certyfikatom, co nie jest dobrym pomysłem. Dlaczego Android nie "polubił" certyfikatu serwera? Jeśli tak jest, ponieważ został wydany przez niezaufany urząd certyfikacji, możesz z łatwością utworzyć za jego pośrednictwem magazyn zaufanych certyfikatów i korzystać z niego przez SocketFactory. –

+0

Na razie jest w porządku ... Nie mogłem w ogóle się rozwijać, ponieważ nie mogłem uwierzytelnić się na serwerze WWW, a 100% aplikacji wymaga komunikacji z serwerem. Później wrócę i zajmę się tworzeniem niestandardowego magazynu zaufania, ale na razie cieszę się, że mam działający kod. Utrzymuje bossów i klientów z dala od moich pleców. ;-) –

+2

Używając tego 'TrustManager', otwierasz się na ataki MITM. – Bruno

-2

Użyj tej i wywołaj tę metodę HttpsTrustManager.allowAllSSL()

Rozwiązuje to problem i działa dobrze dla mnie.

public class HttpsTrustManager realizuje 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()); 
} 

}