本文目录:
6 应用实例
6.1 信任所有证书
- 跳过系统检验,不再使用系统默认的 SSLSocketFactory
- 自定义 TrustManager,信任所有证书
X509TrustManager 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];
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
Request request = new Request.Builder()
.url("https://kyfw.12306.cn/otn/")
.build();
Call call = client.newCall(request);
Response response = call.execute();
Logger.d("response " + response.code());
response.close();
6.2 信任自签名证书
还是以 12306 来进行测试,先从官网上下载证书 srca.cer
- 将自签名证书,比如 12306 的 srca.cer,保存到 assets
- 读取自签名证书集合,保存到 KeyStore 中
- 使用 KeyStore 构建 X509TrustManager
- 使用 X509TrustManager 初始化 SSLContext
- 使用 SSLContext 创建 SSLSocketFactory
// 获取自签名证书集合,由证书工厂管理
InputStream inputStream = HttpsActivity.this.getAssets().open("srca.cer");
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends java.security.cert.Certificate> certificates = certificateFactory.generateCertificates(inputStream);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
// 将证书保存到 KeyStore 中
char[] password = "password".toCharArray();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = String.valueOf(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
// 使用包含自签名证书的 KeyStore 构建一个 X509TrustManager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
// 使用 X509TrustManager 初始化 SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManagers[0]}, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0])
.build();
Request request = new Request.Builder()
.url("https://kyfw.12306.cn/otn/")
.build();
Call call = client.newCall(request);
Response response = call.execute();
Logger.d("response " + response.code());
response.close();
6.3 自定义TLS连接规格
比如使用三个安全级别很高的密码套件,并且限制 TLS 版本为 1_2
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(spec))
.build();
该连接规格的配置是否能够生效,还需要和 SSLSocket 的支持情况取交集,SSLSocket 不支持也就用不了
所以这三个密码套件只能在 Android 5.0 以上的机子生效了
6.4 使用证书锁定
比如锁定了指定 publicobject.com 的证书
pin 的取值为,先对证书公钥信息使用 SHA-256 或者 SHA-1 取哈希,然后进行 Base64 编码,再加上 sha256 或者 sha1 的前缀
这样 publicobject.com 只能使用指定公钥的证书了,安全性进一步提高,但灵活性降低
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
.add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
.add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();