2014-09-30 55 views
0

我的Android應用程序使用DefaultHttpClient API向外部HTTP服務器發送請求。我無法切換到HTTPS。如何提示用戶接受或拒絕HTTPS證書?

我的服務器使用自簽名證書,默認情況下顯然不受信任。

當應用程序連接到服務器時,我想向服務器顯示服務器證書的指紋,並讓他接受或拒絕證書。

這與DefaultHttpClient可能嗎?如果是的話,我可以看到一個代碼片段顯示它是如何完成的。

我已經看到很多假設的解決方案來解決這個問題和類似的問題,但都沒有爲我工作。

+3

「我已經看到很多假設的解決方案來解決這個和類似的問題,但沒有人爲我工作」 - 列出它們並解釋你遇到的具體問題。 – CommonsWare 2014-09-30 23:49:57

+0

我下次嘗試失敗,我會在這裏發佈具體問題。事情是我昨晚嘗試了幾種不同的方法,但都沒有爲我工作,在我厭倦之後,我沒有費心去記錄每個假設的解決方案。 – user805623 2014-10-01 08:46:04

回答

0

我終於找到了一個工作解決方案。這裏的片段:

public class MySocketFactory implements SocketFactory, LayeredSocketFactory { 
private SSLContext mSslContext = null; 

{ 
    try { 
     mSslContext = SSLContext.getInstance("TLS"); 

     TrustManager trustManager = new X509TrustManager() { 

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

      @Override 
      public void checkServerTrusted(X509Certificate[] chain, String authType) 
        throws CertificateException { 
       // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
       // NOTE : This is where we can calculate the certificate's fingerprint, 
       // show it to the user and throw an exception in case he doesn't like it 
      } 

      @Override 
      public void checkClientTrusted(X509Certificate[] chain, String authType) 
        throws CertificateException { 
      } 
     }; 

     mSslContext.init(null, new TrustManager[]{ trustManager }, new SecureRandom()); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

@Override 
public Socket connectSocket(Socket socket, String host, int port, 
     InetAddress localAddress, int localPort, HttpParams params) 
       throws IOException, UnknownHostException, ConnectTimeoutException { 

    int connTimeout = HttpConnectionParams.getConnectionTimeout(params); 
    int soTimeout = HttpConnectionParams.getSoTimeout(params); 

    SocketAddress socketAddress = new InetSocketAddress(localAddress, localPort); 
    socket.bind(socketAddress); 
    socket.connect(new InetSocketAddress(host, port), connTimeout); 
    socket.setSoTimeout(soTimeout); 

    return socket; 
} 

@Override 
public Socket createSocket() throws IOException { 
    return mSslContext.getSocketFactory().createSocket(); 
} 

@Override 
public boolean isSecure(Socket sock) throws IllegalArgumentException { 
    return true; 
} 



@Override 
public Socket createSocket(Socket socket, String host, int port, 
     boolean autoClose) throws IOException, UnknownHostException { 

    return mSslContext.getSocketFactory().createSocket(socket, host, port, autoClose); 
} 

}

然後你就可以實例化DefaultHttpClient像這樣:

HttpParams httpParameters = new BasicHttpParams(); 
    HttpConnectionParams.setConnectionTimeout(httpParameters, 5000); 
    DefaultHttpClient result = new DefaultHttpClient(httpParameters); 


    MySocketFactory mySocketFactory = new MySocketFactory(); 
    Scheme scheme = new Scheme("https", mySocketFactory, port); 

    result.getConnectionManager().getSchemeRegistry().register(scheme); 

我嘗試了出來,它的工作!

0

DefaultHTTPClient,是不可能的,因爲信任匹配發生在較低級別,SSL堆棧(JSSE或OpenSSL或任何Android使用)中。

最簡單和最便宜的解決方案是根本不使用自簽名證書。使用CA簽名的證書。你已經花費了更多的成本,而且你花的時間會更多,這可能是值得的。把錢交給CA.

+0

你是說「這是不可能的」,因爲你知道這是事實,還是僅僅因爲你不知道答案?我在問,因爲我已經知道如何讓DefaultHttpClient使用應用程序的自定義密鑰庫,其中信任匹配也發生在較低級別,所以您提供的原因並不能完全說服我。此外,我的應用程序旨在與用戶的個人DynDNS啓用的Raspberry Pi服務器結合使用,因此不可能事先知道使用了哪個證書。這就是爲什麼我希望用戶能夠通過指紋接受或拒絕。 – user805623 2014-10-01 08:37:55

相關問題