最近做了个项目要求使用https的双向认证,我第一次做我也很懵逼啊,不会做只能找百度,于是乎百度了一大堆资料。。。
首先呢我们需要两个证书:
1.客户端证书
2.服务端证书
这两个证书都由后台生成之后给我们,当然记得让后台转化为BKS格式的,android独有的。
拿到证书之后就要配置我们的网络环境了。ok直接上代码
我的网络请求使用的是 Retrofit所以要先配置okhttp
Retrofit retrofit;
OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(getSSLCertifcation())//获取SSLSocketFactory .hostnameVerifier(new UnSafeHostnameVerifier())//添加hostName验证器 .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .readTimeout(1, TimeUnit.MINUTES) .addInterceptor(new Interceptor() { @Override public Response intercept(@NonNull Chain chain) throws IOException { Request original = chain.request(); Request request = original.newBuilder() .header("Authorization", HttpConfig.Token) .method(original.method(), original.body()) .build(); return chain.proceed(request); } }).build();
retrofit = new Retrofit.Builder() .baseUrl(api) .client(client) .addConverterFactory(FastJsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
下面是我使用的client的方法类
private final static String CLIENT_PRI_KEY = "client.bks"; //证书名字 private final static String TRUSTSTORE_PUB_KEY = "client_truststore.bks"; //证书名字 private final static String CLIENT_BKS_PASSWORD = "xxxx"; //证书密码 private final static String TRUSTSTORE_BKS_PASSWORD = "xxxx"; //证书密码 private final static String KEYSTORE_TYPE = "BKS"; private final static String PROTOCOL_TYPE = "TLS"; private final static String CERTIFICATE_FORMAT = "X509";
public static SSLSocketFactory getSSLCertifcation() { SSLSocketFactory sslSocketFactory = null; try { // 服务器端需要验证的客户端证书,其实就是客户端的keystore KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE); // 客户端信任的服务器端证书 KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE); //读取证书InputStream ksIn = APP.appOS.getAssets().open(CLIENT_PRI_KEY); InputStream tsIn = APP.appOS.getAssets().open(TRUSTSTORE_PUB_KEY); //加载证书 keyStore.load(ksIn, CLIENT_BKS_PASSWORD.toCharArray()); trustStore.load(tsIn, TRUSTSTORE_BKS_PASSWORD.toCharArray()); ksIn.close(); tsIn.close(); //初始化SSLContext SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_FORMAT); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_FORMAT); trustManagerFactory.init(trustStore); keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); sslSocketFactory = sslContext.getSocketFactory(); } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { e.printStackTrace(); } return sslSocketFactory; }
static class UnSafeHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { Log.e("ssl",hostname); Log.e("ssl",session.toString()); return true; //自行添加判断逻辑,true->Safe,false->unsafe } }
这样我们https双向认证就解决了。
【关于Https的双向认证问题】
推荐阅读
- 网络请求|OkHttp源码分析 (一)
- Android笔记|Retrofit2.5是如何解析在接口类中定义的方法()
- 上网/游戏/看剧太慢了(瞧瞧是不是运营商干的)
- 关于Glide实现https的双向认证