Android使用https与服务器交互的正确姿势

书史足自悦,安用勤与劬。这篇文章主要讲述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,然后用后者创建和初始化 TrustManagerTrustManager 是系统用于从服务器验证证书的工具,可以使用一个或多个 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,从而使你的应用可与任何人通信。

    推荐阅读