2011-08-19 128 views
14

我有2個應用程序,一個是Servlet/Tomcat服務器,另一個是Android應用程序。Android:使用HttpsURLConnection的HTTPS(SSL)連接

我想使用HttpURLConnection在兩者之間發送和接收XML。

代碼:

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

現在,我需要使用SSL來送個XML安全。

首先,我使用Java Keytool生成.keystore文件。

Keytool -keygen -alias tomcat -keyalg RSA 

然後我把XML代碼在Tomcat的server.xml文件中使用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" 
/> 

然後,我改變它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 

的HttpURLConnection類,並添加trustAllHosts方法信任每臺服務器(不檢查任何證書)

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

這些改變非常好,但我不想信任每臺服務器。我想用我的密鑰庫文件來驗證連接並以正確的方式使用SSL。 我在網上閱讀了很多,並做了很多測試,但我不明白我必須做什麼以及如何去做。

有人可以幫我嗎?

非常感謝您

對不起,我的英文不好

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

嗯,我仍在爲此工作。我做了一個新的方法來設置密鑰存儲的InputStream等

的方法是這樣的:

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

首先,我有很多與密鑰和證書的問題,但現在它是工作(我認爲是)

我現在的問題是TimeOut異常。我不知道它爲什麼產生。我認爲這是寫數據的東西,但我還不能解決。

任何想法?

+0

是否使用由知名認證機構簽署的證書或您使用自簽名的證書? –

回答

6

您需要爲here所述的自簽名證書創建一個信任存儲文件。 在客戶端使用它來連接您的服務器。如果你使用JKS或其他格式並不重要,我現在將假設JKS。

爲了實現你的想法,顯然你需要不同的TrustManager。您可以使用TrustManagerFactory並將其信任設置與新創建的信任庫一起提供。

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

使用tms來初始化你SSLContext而不是爲用於您的SSL/TLS連接新的信任設置。

此外,您應該確保服務器TLS證書的CN部分等於您服務器的FQDN(完全限定的域名),例如,如果您的服務器的基本URL是'https://www.example.com',那麼證書的CN應該是'www.example.com'。這是主機名驗證所必需的,該功能可防止中間人攻擊。您可以禁用此功能,但只有在使用此連接時纔會非常安全。

+0

我認爲他需要正確配置android客戶端的truststore,而不是服務器。 –

+0

你是對的 - 我會改變這一點,謝謝! – emboss

+0

感謝您的幫助。我會盡力瞭解所有這些,並進行一些測試。非常感謝你! –

0

創建您的信任存儲,作爲資產存儲並使用它初始化此SocketFactory。然後用工廠代替你自己的'相信每個人'。

+0

謝謝,你有一些例子嗎? –

+0

不是一個完整的例子,但是一旦你加載了你的信任存儲(其中包含服務器證書),只需將它與密碼一起傳遞給構造函數即可。然後,您可以將生成的'SocketFactory'實例傳遞給'HttpsURLConnection.setDefaultSSLSocketFactory()'。 –