2011-08-19 19 views
14

Ho 2 app, una è un server Servlet/Tomcat e l'altra è un'app Android.Android: connessione HTTPS (SSL) utilizzando HttpsURLConnection

Desidero utilizzare HttpURLConnection per inviare e ricevere XML tra entrambi.

Codice:

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

Ora, ho bisogno di utilizzare SSL per inviare le XMLs per la sicurezza.

Per prima cosa, utilizzo Java Keytool per generare il file .keystore.

Keytool -keygen -alias tomcat -keyalg RSA 

poi ho messo il codice XML file server.xml di Tomcat per utilizzare 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" 
/> 

Poi, lo cambio il HttpURLConnection per 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 

e aggiungere un trustAllHosts metodo per fidarsi di ogni server (non controllare per qualsiasi certificato)

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

Queste modifiche hanno funzionato molto bene, ma non voglio fidarmi di ogni server. Voglio usare il mio file di archivio chiavi per convalidare la connessione e usare SSL nel modo giusto. Ho letto molto su internet e ho fatto un sacco di test, ma non riesco a capire cosa devo fare e come farlo.

Qualcuno può aiutarmi?

La ringrazio molto

Ci dispiace per il mio povero inglese

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

Beh, ci sto ancora lavorando. Ho fatto un nuovo metodo per impostare il KeyStore, InputStream, ecc

Il metodo è simile al seguente:

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

Prima ho avuto un sacco di problemi con la chiave e il certificato, ma ora si sta lavorando (Penso di sì)

Il mio problema adesso è un'eccezione TimeOut. Non so perché viene generato. Penso che sia qualcosa con la scrittura dei dati, ma non posso ancora risolverlo.

Qualche idea?

+0

Si sta utilizzando un certificato firmato da un'autorità di certificazione noto o stai usando un certificato auto-firmato? –

risposta

6

È necessario creare un file di archivio sicuro per il certificato autofirmato come descritto here. Usalo sul lato client per connetterti con il tuo server. Non importa se usi JKS o un altro formato, suppongo JKS per ora.

Per realizzare ciò che avete in mente avete bisogno di un diverso TrustManager, ovviamente. Puoi utilizzare TrustManagerFactory e alimentare le sue impostazioni di fiducia con il tuo nuovo negozio di fiducia.

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

Usa tms per init vostro SSLContext, invece, per le nuove impostazioni di attendibilità da utilizzare per la connessione SSL/TLS.

Inoltre, è necessario assicurarsi che la parte CN del certificato TLS del server sia uguale al nome di dominio completo (FQDN) del server, ad es. se l'URL della base del server è "https://www.example.com", il codice CN del certificato deve essere "www.example.com". Questo è necessario per la verifica del nome host, una funzionalità che impedisce attacchi man-in-the-middle. Puoi disabilitarlo, ma solo quando lo usi la tua connessione sarà veramente sicura.

+0

Penso che abbia bisogno di configurare correttamente il truststore del client Android, non quello del server. –

+0

Hai assolutamente ragione - cambierò questo, grazie! – emboss

+0

Grazie per l'aiuto. Cercherò di capire tutto questo e fare alcuni test. Grazie mille! –

0

Crea il tuo negozio di fiducia, memorizza come bene e usalo inizializza questo SocketFactory. Quindi usa la fabbrica invece della tua "fiducia in tutti".

+0

Grazie, hai qualche esempio? –

+0

Non un esempio completo, ma una volta caricato il trust store (che contiene il certificato del server), basta passarlo al costruttore insieme alla password. Quindi è possibile passare l'istanza 'SocketFactory' risultante a' HttpsURLConnection.setDefaultSSLSocketFactory() '. –

Problemi correlati