2011-08-19 10 views
14

Mam 2 aplikacje, jedna to serwer Serwlet/Tomcat, a druga to aplikacja na Androida.Android: połączenie HTTPS (SSL) za pomocą łącza HTTP

Chcę użyć HttpURLConnection do wysyłania i odbierania XML między oboma.

Kod:

private String sendPostRequest(String requeststring) { 

    DataInputStream dis = null; 
    StringBuffer messagebuffer = new StringBuffer(); 

    HttpURLConnection urlConnection = null; 

    try { 
     URL url = new URL(this.getServerURL()); 

     urlConnection = (HttpURLConnection) url.openConnection();   

     urlConnection.setDoOutput(true); 

     urlConnection.setRequestMethod("POST"); 

     OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); 

     out.write(requeststring.getBytes()); 

     out.flush(); 

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

     dis = new DataInputStream(in); 

     int ch; 

     long len = urlConnection.getContentLength(); 

     if (len != -1) { 

      for (int i = 0; i < len; i++) 

       if ((ch = dis.read()) != -1) { 

        messagebuffer.append((char) ch); 
       } 
     } else { 

      while ((ch = dis.read()) != -1) 
       messagebuffer.append((char) ch); 
     } 

     dis.close(); 

    } catch (Exception e) { 

     e.printStackTrace(); 

    } finally { 

     urlConnection.disconnect(); 
    } 

    return messagebuffer.toString(); 
} 

Teraz muszę używać SSL do wysyłania plików XML dla bezpieczeństwa.

Najpierw używam Java Keytool do wygenerowania pliku .keystore.

Keytool -keygen -alias tomcat -keyalg RSA 

Potem umieścić kod XML w pliku server.xml Tomcat używać SSL

<Connector 
port="8443" protocol="HTTP/1.1" SSLEnabled="true" 
maxThreads="150" scheme="https" secure="true" 
keystoreFile="c:/Documents and Settings/MyUser/.keystore" 
keystorePass="password" 
clientAuth="false" sslProtocol="TLS" 
/> 

Potem to się zmieni HttpURLConnection dla HttpsURLConnection

private String sendPostRequest(String requeststring) { 

    DataInputStream dis = null; 
    StringBuffer messagebuffer = new StringBuffer(); 

    HttpURLConnection urlConnection = null; 

    //Conexion por HTTPS 
    HttpsURLConnection urlHttpsConnection = null; 

    try { 
     URL url = new URL(this.getServerURL()); 

     //urlConnection = (HttpURLConnection) url.openConnection();   

     //Si necesito usar HTTPS 
     if (url.getProtocol().toLowerCase().equals("https")) { 

      trustAllHosts(); 

      //Creo la Conexion 
      urlHttpsConnection = (HttpsURLConnection) url.openConnection(); 

      //Seteo la verificacion para que NO verifique nada!! 
      urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY); 

      //Asigno a la otra variable para usar simpre la mism 
      urlConnection = urlHttpsConnection; 

     } else { 

      urlConnection = (HttpURLConnection) url.openConnection(); 
     } 

//Do the same like up 

i dodać trustAllHosts metoda zaufania do każdego serwera (nie sprawdzaj żadnego certyfikatu)

private static void trustAllHosts() { 

    X509TrustManager easyTrustManager = new X509TrustManager() { 

     public void checkClientTrusted(
       X509Certificate[] chain, 
       String authType) throws CertificateException { 
      // Oh, I am easy! 
     } 

     public void checkServerTrusted(
       X509Certificate[] chain, 
       String authType) throws CertificateException { 
      // Oh, I am easy! 
     } 

     public X509Certificate[] getAcceptedIssuers() { 
      return null; 
     } 

    }; 

    // Create a trust manager that does not validate certificate chains 
    TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; 

    // Install the all-trusting trust manager 
    try { 
     SSLContext sc = SSLContext.getInstance("TLS"); 

     sc.init(null, trustAllCerts, new java.security.SecureRandom()); 

     HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 

    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
} 

Te zmiany działały bardzo dobrze, ale nie chcę Zaufać każdemu serwerowi. Chcę użyć pliku magazynu kluczy do sprawdzenia poprawności połączenia i prawidłowego korzystania z protokołu SSL. Dużo czytam w Internecie i wykonuję wiele testów, ale nie mogę zrozumieć, co muszę zrobić i jak to zrobić.

Czy ktoś może mi pomóc?

Dziękuję bardzo

Przepraszam za mój słaby angielski

------------------------- UPDATE 2011/08/24 ------------------------------------------------ -

Cóż, wciąż nad tym pracuję. Zrobiłem nową metodę, aby ustawić KeyStore, InputStream itp

Metoda wygląda następująco:

private static void trustIFNetServer() { 

    try { 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 

     KeyStore ks = KeyStore.getInstance("BKS"); 

     InputStream in = context.getResources().openRawResource(R.raw.mykeystore); 

     String keyPassword = "password"; 

     ks.load(in, keyPassword.toCharArray()); 

     in.close(); 

     tmf.init(ks); 

     TrustManager[] tms = tmf.getTrustManagers();  

     SSLContext sc = SSLContext.getInstance("TLS"); 

    sc.init(null, tms, new java.security.SecureRandom()); 

    } catch (Exception e) { 
    e.printStackTrace(); 
    } 
} 

Najpierw miałem wiele problemów z kluczem i świadectwie, ale teraz pracuje (Tak myślę)

Moim problemem jest teraz wyjątek TimeOut. Nie wiem, dlaczego jest generowany. Myślę, że to coś z zapisaniem danych, ale nie mogę jeszcze rozwiązać.

Każdy pomysł?

+0

Używasz certyfikat podpisany przez znanego ośrodka certyfikacji lub używasz certyfikatu z podpisem własnym? –

Odpowiedz

6

Należy utworzyć plik magazynu zaufanych certyfikatów z podpisem własnym, zgodnie z opisem here. Użyj go po stronie klienta, aby połączyć się z serwerem. Nie ma znaczenia, czy używasz JKS czy innego formatu, na razie założę JKS.

Aby osiągnąć to, co masz na myśli, potrzebujesz oczywiście innego TrustManager. Możesz użyć TrustManagerFactory i podać ustawienia zaufania do nowo utworzonego magazynu zaufanych certyfikatów.

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); 
KeyStore ks = KeyStore.getInstance("JKS"); 
FileInputStream in = new FileInputStream("<path to your key store>"); 
ks.load(in, "password".toCharArray()); 
in.close(); 
tmf.init(ks); 
TrustManager[] tms = tmf.getTrustManagers(); 

Zastosowanie tms init swój SSLContext zamiast na nowe ustawienia powiernicze mają być stosowane do połączenia SSL/TLS.

Należy także upewnić się, że część certyfikatu TLS serwera jest równa nazwie FQDN (w pełni kwalifikowanej nazwie domeny) serwera, np. FTDN. jeśli Twoim podstawowym adresem URL serwera jest "https://www.example.com", to certyfikatem CN powinna być "www.example.com". Jest to potrzebne do weryfikacji nazwy hosta, funkcji, która zapobiega atakom typu "człowiek w środku". Możesz wyłączyć to, ale tylko wtedy, gdy używasz tego połączenia, będzie naprawdę bezpieczne.

+0

Myślę, że musi on poprawnie skonfigurować magazyn zaufanych klientów androida, a nie serwer. –

+0

Masz absolutną rację - zmienię to, dzięki! – emboss

+0

Dzięki za pomoc. Spróbuję to wszystko zrozumieć i zrobić kilka testów. Dziękuję Ci bardzo! –

0

Utwórz swój magazyn zaufania, przechowuj go jako zasób i użyj go, aby zainicjować tę SocketFactory. Następnie użyj fabryki zamiast własnego "zaufaj wszystkim".

+0

Dzięki, czy masz przykład? –

+0

Nie jest to pełny przykład, ale po załadowaniu magazynu zaufania (zawierającego certyfikat serwera) wystarczy przekazać go do konstruktora wraz z hasłem. Następnie możesz przekazać wynikową instancję 'SocketFactory' do' HttpsURLConnection.setDefaultSSLSocketFactory() '. –