最近跟华为做合作项目,主要是我这边提供sdk集成到华为的app里,对方要求sdk与我方服务器的数据通信必须使用证书绑定,这里简单记录一下流程。
//证书的public key 真实key这里就不放出来了, 可以通过 keytool -printcert -rfc -file xxx.crt得到
private static final String cert = "-----BEGIN CERTIFICATE-----\n" +
"-----END CERTIFICATE-----";
SSLSocketFactory sslSocketFactory = null;
try {
InputStream certStream = new Buffer().writeUtf8(certString).inputStream();
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
char[] password = "".toCharArray(); // Any password will work.
KeyStore keyStore;
try {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = null; // By convention, 'null' creates an empty key store.
keyStore.load(in, password);
} catch (IOException e) {
throw new AssertionError(e);
}
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
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));
}
SSLContext ssContext = SSLContext.getInstance("TLS");
ssContext.init(keyManagerFactory.getKeyManagers(), trustManagers, null);
SslSocketFactory sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
}
OkHttpClient client = new OkHttpClient().newBuilder().sslSocketFactory(sslSocketFactory).hostnameVerifier(STRICT_HOSTNAME_VERIFIER) .build();
主要流程是根据证书创建trustmanager,并以此创建SslSocketFactory对象设置到okhttpclient上,同时要记得配置hostnameVerifier,两者前者负责检验证书,后者负责检验域名。目前测试下来会对所有使用该client的https网络请求做证书验证,如何在同一个client下只筛选特定域名请求暂时不知道如何实现,不过也已经满足功能需求。中间尝试跟了一下证书验证的源码,发现实际实现并不在andorid的jar包里,后续有机会再跟。