我的Android應用程序使用DefaultHttpClient API向外部HTTP服務器發送請求。我無法切換到HTTPS。如何提示用戶接受或拒絕HTTPS證書?
我的服務器使用自簽名證書,默認情況下顯然不受信任。
當應用程序連接到服務器時,我想向服務器顯示服務器證書的指紋,並讓他接受或拒絕證書。
這與DefaultHttpClient可能嗎?如果是的話,我可以看到一個代碼片段顯示它是如何完成的。
我已經看到很多假設的解決方案來解決這個問題和類似的問題,但都沒有爲我工作。
我的Android應用程序使用DefaultHttpClient API向外部HTTP服務器發送請求。我無法切換到HTTPS。如何提示用戶接受或拒絕HTTPS證書?
我的服務器使用自簽名證書,默認情況下顯然不受信任。
當應用程序連接到服務器時,我想向服務器顯示服務器證書的指紋,並讓他接受或拒絕證書。
這與DefaultHttpClient可能嗎?如果是的話,我可以看到一個代碼片段顯示它是如何完成的。
我已經看到很多假設的解決方案來解決這個問題和類似的問題,但都沒有爲我工作。
我終於找到了一個工作解決方案。這裏的片段:
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);
我嘗試了出來,它的工作!
DefaultHTTPClient,
是不可能的,因爲信任匹配發生在較低級別,SSL堆棧(JSSE或OpenSSL或任何Android使用)中。
最簡單和最便宜的解決方案是根本不使用自簽名證書。使用CA簽名的證書。你已經花費了更多的成本,而且你花的時間會更多,這可能是值得的。把錢交給CA.
你是說「這是不可能的」,因爲你知道這是事實,還是僅僅因爲你不知道答案?我在問,因爲我已經知道如何讓DefaultHttpClient使用應用程序的自定義密鑰庫,其中信任匹配也發生在較低級別,所以您提供的原因並不能完全說服我。此外,我的應用程序旨在與用戶的個人DynDNS啓用的Raspberry Pi服務器結合使用,因此不可能事先知道使用了哪個證書。這就是爲什麼我希望用戶能夠通過指紋接受或拒絕。 – user805623 2014-10-01 08:37:55
「我已經看到很多假設的解決方案來解決這個和類似的問題,但沒有人爲我工作」 - 列出它們並解釋你遇到的具體問題。 – CommonsWare 2014-09-30 23:49:57
我下次嘗試失敗,我會在這裏發佈具體問題。事情是我昨晚嘗試了幾種不同的方法,但都沒有爲我工作,在我厭倦之後,我沒有費心去記錄每個假設的解決方案。 – user805623 2014-10-01 08:46:04