参考:
https://developer.android.com/training/articles/security-ssl
https://developer.android.com/reference/java/net/HttpURLConnection
HTTPS
关于HTTPS的介绍可以参考我的另一篇文章,HTTPS和SSL
Android实现HTTPS
Android通过SSLSocket来实现HTTPS请求,这里先介绍几个重要的API:
SSLSocketFactory
Android 使用的是 Java 的 API。那么 HTTPS 使用的 Socket 必然都是通过SSLSocketFactory 创建的 SSLSocket,当然自己实现了 TLS 协议除外。
默认的 SSLSocketFactory 校验服务器的证书时,会信任设备内置的100多个根证书。所以通过HttpsURLConnection请求https://www.baidu.com/是可以访问的,但是官网中并不建议这样做,官网认为信任具体的证书才是最安全的。
TrustManager
SSL 握手开始后,会校验服务器的证书,有种方法是信任所有证书,当然我们是不建议进行这种操作的,风险太大。
// 信任所有证书,不建议这么操作
private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}, null);
return sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new AssertionError();
}
}
自定义信任策略
为了更好的安全性,我们应该自己指定信任的证书,官网的实现步骤如下:
- 获取到证书
- 创建 Keystore 包含我们的证书
- 创建一个 TrustManager 仅把 Keystore 中的证书 作为信任的锚点
- 用 TrustManager 初始化一个 SSLContext
主要代码:
CertificateFactory cf = null;
try {
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();
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}