书史足自悦,安用勤与劬。这篇文章主要讲述Android使用https与服务器交互的正确姿势相关的知识,希望能为你提供帮助。
HTTPS 使用 SSL 在客户端和服务器之间进行加密通信,错误地使用 SSL ,将会导致其它人能够拦截网络上的应用数据。
使用一个包含公钥及与其匹配的私钥的证书配置服务器,作为 SSL 客户端与服务器握手的一部分,服务器将通过使用公钥加密签署其证书来证明自己具有私钥。
主机平台一般包含其信任的知名 CA 的列表。从 android 4.2 开始,Android 包含在每个版本中更新的 100 多个 CA。CA 具有一个证书和一个私钥,为服务器发放证书时,CA 使用其私钥签署服务器证书。然后,客户端可以验证该服务器是否具有平台已知的 CA 发放的证书。
如果拥有一个知名 CA 发放证书的服务器,那么可以用以下代码直接发起 HTTPS 请求
URL url = new URL("
https://www.cnblogs.com"
);
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
对!就是这么简单。 Android 会对验证证书和主机名做处理,你不用考虑这些细节。
如果验证服务器证书出现
SSLHandshakeException
异常,那么原因可能是颁发服务器证书的 CA 是 Android 系统未知的,或者是自签署的服务器证书。为了解决证书验证失败的问题,我们可以使用自定义的
TrustManager
使 HttpsURLConnection
信任特定的 CA 。// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("
X.509"
);
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("
load-der.crt"
));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("
ca="
+ ((X509Certificate) ca).getSubjectDN());
} 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("
TLS"
);
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
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);
从
InputStream
获取一个特定的 CA,用该 CA 创建 KeyStore
,然后用后者创建和初始化 TrustManager
。TrustManager
是系统用于从服务器验证证书的工具,可以使用一个或多个 CA 从 KeyStore
创建,而创建的 TrustManager
将仅信任这些 CA。【Android使用https与服务器交互的正确姿势】很多网站和博客介绍一种非常糟糕的解决方案来通过验证
SSLContext sslContext = SSLContext.getInstance("
TLS"
);
sslContext.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 {}@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, null);
URLConnection urlConnection = url.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
使用一个没有任何作用的
TrustManager
。这样做等同于没加密通信,因为任何人都可以在公共 WLAN 热点下,使用伪装成服务器的代理发送数据,通过 DNS 欺骗攻击用户。然后,攻击者可以记录密码和其他个人数据。此方法之所以有效是因为攻击者可以生成一个证书,且没有可以真正验证证书是否来自值得信任的来源的 TrustManager
,从而使你的应用可与任何人通信。推荐阅读
- Mapper与Reducer浅析
- Android-活动(Activity)Intent
- Android JNI 学习(JNI 数据类型和数据结构)
- Android移动端自动化测试从入门到实战(Java篇)
- 学习Android过程中遇到的问题及解决方法——电话监听
- Android JNI 学习(JNI 设计概述)
- “今日校园” App 用户体验分析
- 将Xcode升级到10.0以上版本,Appium启动报错的问题
- RXAndroidBle 记录网址