Android通信安全之HTTPS
目錄
Android通信安全之HTTPS
Https
起因
問題描述
自定義X509TrustManager
自定義HostnameVerifier
修復(fù)方案
解決方案一
解決方案2
?文章來源地址http://www.zghlxwxcb.cn/news/detail-751350.html
?
?
本文章向大家介紹Android通信安全之HTTPS,主要內(nèi)容包括Https、起因、問題描述、自定義HostnameVerifier、修復(fù)方案、解決方案2、基本概念、基礎(chǔ)應(yīng)用、原理機(jī)制和需要注意的事項(xiàng)等,并結(jié)合實(shí)例形式分析了其使用技巧,希望通過本文能幫助到大家理解應(yīng)用這部分內(nèi)容。
?
?
?
Https
HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全為目標(biāo)的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎(chǔ)是SSL,因此加密的詳細(xì)內(nèi)容就需要SSL。 它是一個(gè)URI scheme(抽象標(biāo)識符體系),句法類同http:體系。用于安全的HTTP數(shù)據(jù)傳輸。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默認(rèn)端口及一個(gè)加密/身份驗(yàn)證層(在HTTP與TCP之間)。這個(gè)系統(tǒng)的最初研發(fā)由網(wǎng)景公司(Netscape)進(jìn)行,并內(nèi)置于其瀏覽器Netscape Navigator中,提供了身份驗(yàn)證與加密通訊方法。現(xiàn)在它被廣泛用于萬維網(wǎng)上安全敏感的通訊,例如交易支付方面。(注:本段來自百度百科)
?
?
問題描述
對于數(shù)字證書相關(guān)概念、Android 里 https 通信代碼就不再復(fù)述了,直接講問題。缺少相應(yīng)的安全校驗(yàn)很容易導(dǎo)致中間人攻擊,而漏洞的形式主要有以下3種:
自定義X509TrustManager
在使用HttpsURLConnection發(fā)起 HTTPS 請求的時(shí)候,提供了一個(gè)自定義的X509TrustManager,未實(shí)現(xiàn)安全校驗(yàn)邏輯,下面片段就是當(dāng)時(shí)新浪微博 sdk 內(nèi)部的代碼片段。如果不提供自定義X509TrustManager,代碼運(yùn)行起來可能會(huì)報(bào)異常(原因下文解釋),初學(xué)者就很容易在不明真相的情況下提供了一個(gè)自定義的X509TrustManager,卻忘記正確地實(shí)現(xiàn)相應(yīng)的方法。本文重點(diǎn)介紹這種場景的處理方式。這里引用部分相關(guān)代碼:
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing,接受任意客戶端證書
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing,接受任意服務(wù)端證書
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
自定義HostnameVerifier
在握手期間,如果 URL 的主機(jī)名和服務(wù)器的標(biāo)識主機(jī)名不匹配,則驗(yàn)證機(jī)制可以回調(diào)此接口的實(shí)現(xiàn)程序來確定是否應(yīng)該允許此連接。如果回調(diào)內(nèi)實(shí)現(xiàn)不恰當(dāng),默認(rèn)接受所有域名,則有安全風(fēng)險(xiǎn)。
HostnameVerifier hnv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// Always return true,接受任意域名服務(wù)器
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hnv);
如上,如果不做任何的教研就是有風(fēng)險(xiǎn)的。
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
修復(fù)方案
分而治之,針對不同的漏洞點(diǎn)分別描述,這里就講的修復(fù)方案主要是針對非瀏覽器App,非瀏覽器 App 的服務(wù)端通信對象比較固定,一般都是自家服務(wù)器,可以做很多特定場景的定制化校驗(yàn)。如果是瀏覽器 App,校驗(yàn)策略就有更通用一些。前面說到,當(dāng)發(fā)起 HTTPS 請求時(shí),可能拋起一個(gè)異常,以上面說到的代碼來看:
try {
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
private void copyInputStreamToOutputStream(InputStream in, PrintStream out) throws IOException {
byte[] buffer = new byte[1024];
int c = 0;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
}
它會(huì)拋出一個(gè)SSLHandshakeException的異常。這里截取部分異常。
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
....//省略n多錯(cuò)誤
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:318)
... 10 more
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
... 16 more
解決方案一
不論是權(quán)威機(jī)構(gòu)頒發(fā)的證書還是自簽名的,打包一份到 app 內(nèi)部,比如存放在 asset 里。通過這份內(nèi)置的證書初始化一個(gè)KeyStore,然后用這個(gè)KeyStore去引導(dǎo)生成的TrustManager來提供驗(yàn)證,具體代碼如下:
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// uwca.crt 打包在 asset 中,該證書可以從https://itconnect.uw.edu/security/securing-computer/install/safari-os-x/下載
InputStream caInput = new BufferedInputStream(getAssets().open("uwca.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
Log.i("Longer", "ca=" + ((X509Certificate) ca).getSubjectDN());
Log.i("Longer", "key=" + ((X509Certificate) ca).getPublicKey();
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLSv1","AndroidOpenSSL");
context.init(null, tmf.getTrustManagers(), null);
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
這樣訪問非“UW Services CA Test Page”就會(huì)報(bào)SSLHandshakeException。也就是說對于特定證書生成的TrustManager,只能驗(yàn)證與特定服務(wù)器建立安全鏈接,這樣就提高了安全性。
解決方案2
同方案1,打包一份到證書到 app 內(nèi)部,但不通過KeyStore去引導(dǎo)生成的TrustManager,而是干脆直接自定義一個(gè)TrustManager,自己實(shí)現(xiàn)校驗(yàn)邏輯; 校驗(yàn)邏輯主要包括: ?服務(wù)器證書是否過期 ?證書簽名是否合法
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// uwca.crt 打包在 asset 中,該證書可以從https://itconnect.uw.edu/security/securing-computer/install/safari-os-x/下載
InputStream caInput = new BufferedInputStream(getAssets().open("uwca.crt"));
final Certificate ca;
try {
ca = cf.generateCertificate(caInput);
Log.i("Longer", "ca=" + ((X509Certificate) ca).getSubjectDN());
Log.i("Longer", "key=" + ((X509Certificate) ca).getPublicKey());
} finally {
caInput.close();
}
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLSv1","AndroidOpenSSL");
context.init(null, new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
for (X509Certificate cert : chain) {
// Make sure that it hasn't expired.
cert.checkValidity();
// Verify the certificate's public key chain.
try {
cert.verify(((X509Certificate) ca).getPublicKey());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}, null);
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
同樣上述代碼只能訪問 certs.cac.washington.edu 相關(guān)域名地址,如果訪問 其他網(wǎng)址 ,則會(huì)在cert.verify(((X509Certificate) ca).getPublicKey());處拋異常,導(dǎo)致連接失敗。
自定義HostnameVerifier,建立匹配規(guī)則;業(yè)務(wù)復(fù)雜的話,還可以結(jié)合配置中心、白名單、黑名單、正則匹配等多級別動(dòng)態(tài)校驗(yàn);總體來說邏輯還是比較簡單的,反正只要正確地實(shí)現(xiàn)那個(gè)方法。
HostnameVerifier hnv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
//示例
if("yourhostname".equals(hostname)){
return true;
} else {
HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(hostname, session);
}
}
};
主機(jī)名驗(yàn)證策略改成嚴(yán)格模式:
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
這樣就有效的避免了Hook。文章來源:http://www.zghlxwxcb.cn/news/detail-751350.html
?
到了這里,關(guān)于Android通信安全之HTTPS的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!