NetAndroid开发经验谈Android开发

Https自定义证书引入问题(2)

2018-01-17  本文已影响65人  tmyzh

上一篇介绍接口使用https并且证书是自签的情况下,如何在客户端信任服务器证书,没有看过的请移步

Https自定义证书引入问题

1.Webview加载https问题

1.1 最简单的方式,助各位大佬一秒脱坑

自定义证书的https地址在加载时会进入

onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) 方法,前提是自定义webviewClient类。这时候调用 handler.proceed()即可。也就是忽略验证证书这个过程。

这个方式简单,快捷,但是可能会过不了大厂的安全监测,handler.proceed()这个方法会被监测到,视为https安全检测(本人就是被卡在这里,哭),另外在上架google应用时也会因此被拒,提示使用handler.cancel()方法,但是没有实际作用,因为会加载不出来。

1.2 安全的方式

安全的方式当然是让webview信任服务器证书了。

首先把证书放入项目中

za.cer https证书
private void handlerCer(final SslErrorHandler ssl, String url) {

      OkHttpClient.Builder builder;

       try {

          builder = setCertificates1(new OkHttpClient.Builder(),      context.getAssets().open("zs.cer"));

//builder=checkAction(new OkHttpClient.Builder());

 }catch (Exception e) {
      builder =new OkHttpClient.Builder();
 }

builder.hostnameVerifier(new HostnameVerifier() {

@Override

public boolean verify(String hostname, SSLSession session) {
        //忽略域名检查
        return true;
}

});

Request request =new Request.Builder().url(url).build();

builder.build().newCall(request).enqueue(new okhttp3.Callback() {

@Override

 public void onFailure(okhttp3.Call call, IOException e) {

      ssl.cancel();

}

@Override

 public void onResponse(okhttp3.Call call, okhttp3.Response response)throws IOException {

     ssl.proceed();

 }

});

 }

private OkHttpClient.Builder setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
 try {
     CertificateFactory certificateFactory =    CertificateFactory.getInstance("X.509");
// KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
   KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
   keyStore.load(null);
   int index = 0;
  for (InputStream certificate : certificates) {
       String certificateAlias = Integer.toString(index++);
      keyStore.setCertificateEntry(certificateAlias,       certificateFactory.generateCertificate(certificate));

 try {
      if (certificate != null)
          certificate.close();
 } catch (IOException e) {
 }
 }
 SSLContext sslContext = SSLContext.getInstance("TLS");
 TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
X509TrustManager trustManager = Platform.get().trustManager(sslSocketFactory);
client.sslSocketFactory(sslSocketFactory, trustManager);
} catch (Exception e) {
e.printStackTrace();
 }
 return client;
 }

还是在onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)调用这个方法。
重点这里我们需要验证最后是否有进入最后的onResponse方法,说明我们信任证书后访问网址响应成功,进入onFailures说明我们的证书有问题。注意在响应onReceivedSslError之前asLog信息中可以看到一个报错x509Util:.java.security.cert.CertPathValidatorException: Trust anchor for certification 就是这个让他进入sslError所以不必惊慌。但是这里我们依然少不了被安全检测扫描到proceed()


扫描到的漏洞

但是我们可以大声说我们已经信任了证书。

2 glide遇到使用自签证书的https图片链接

这里我们要用到GlideModule来进行网络请求库的定制,里面要重写两个方法

@Override
 public void applyOptions(Context context, GlideBuilder builder) {
 //通过builder.setXXX进行配置.  缓存配置,图片配置
}

 @Override
public void registerComponents(Context context, Glide glide) {
//通过glide.register进行配置. 对网络上的配置做一些处理
 }

2.1 拷贝’com.github.bumptech.glide:okhttp-integration:1.4.0@aar’里面3个类OkHttpGlideModule ,OkHttpUrlLoader,OkHttpStreamFetcher到项目中
先贴上后两个的代码,直接使用即可
OkHttpStreamFetcher

package com.example.appuser.testother.utils.https;

import com.bumptech.glide.Priority;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Fetches an {@link InputStream} using the okhttp library.
 */
public class OkHttpStreamFetcher implements DataFetcher
{
 private final OkHttpClient client;
 private final GlideUrl url;
private InputStream stream;
 private ResponseBody responseBody;

 public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
 this.client = client;
 this.url = url;
 }

 @Override
 public InputStream loadData(Priority priority) throws Exception
 {
Request.Builder requestBuilder = new Request.Builder()
.url(url.toStringUrl());

 for (Map.Entry headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}

Request request = requestBuilder.build();
 Response response = client.newCall(request).execute();
 responseBody = response.body();
 if (!response.isSuccessful()) {
 throw new IOException("Request failed with code: " + response.code());
}
 long contentLength = responseBody.contentLength();
 stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
return stream;
 }

 @Override
public void cleanup() {
if (stream != null) {
 try {
stream.close();
 } catch (IOException e) {
// Ignored
 }
}
 if (responseBody != null) {
 responseBody.close();
 }
 }

@Override
 public String getId() {
 return url.getCacheKey();
}

@Override
public void cancel() {
// TODO: call cancel on the client when this method is called on a background thread. See #257
 }
}

OkHttpUrlLoader

package com.example.appuser.testother.utils.https;

import android.content.Context;

import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GenericLoaderFactory;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;

import java.io.InputStream;

import okhttp3.OkHttpClient;


public class OkHttpUrlLoader implements ModelLoader
{
 /**
 * The default factory for {@link OkHttpUrlLoader}s.
  */
 public static class Factory implements ModelLoaderFactory
 {
 private static volatile OkHttpClient internalClient;
 private OkHttpClient client;

private static OkHttpClient getInternalClient() {
 if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
 internalClient = new OkHttpClient();
}
}
 }
 return internalClient;
 }

 /**
  * Constructor for a new Factory that runs requests using a static singleton client.
  */
 public Factory() {
 this(getInternalClient());
 }

 /**
  * Constructor for a new Factory that runs requests using given client.
  */
public Factory(OkHttpClient client) {
this.client = client;
}

 @Override
 public ModelLoader build(Context context, GenericLoaderFactory factories) {
return new OkHttpUrlLoader(client);
 }

@Override
 public void teardown() {
 // Do nothing, this instance doesn't own the client.
 }
}

private final OkHttpClient client;

public OkHttpUrlLoader(OkHttpClient client) {
 this.client = client;
}

 @Override
 public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) {
 return new OkHttpStreamFetcher(client, model);
 }
}

OkHttpGlideModule

package com.alpha58.okhttps.https;

import android.content.Context;

import com.alpha58.okhttps.utils.HTTPSUtils;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.GlideModule;

import java.io.InputStream;

/**
 * A {@link GlideModule} implementation to replace Glide's default
 * {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with an OkHttp based
 * {@link com.bumptech.glide.load.model.ModelLoader}.
 *
 * </p><p>
 * If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
  in by manifest merger. For other build systems or for more more information, see
 *  {@link GlideModule}.
 * </p>
 */
public class OkHttpGlideModule implements GlideModule {
@Override
 public void applyOptions(Context context, GlideBuilder builder) {
 // Do nothing.
 }

;@Override
 public void registerComponents(Context context, Glide glide) {
//注意:new HTTPSUtils(context).getInstance()为已经通过认证的okhttpclient
 glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(new HTTPSUtils(context).getInstance()));
}
}

稍后具体讲解registerComponents里面的实现方法
2.2 删除build.gradle里面的依赖
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
2.3 在Manifest.xml配置拷贝过来的OkHttpGlideModule。

   <meta-data
            android:name="com.example.appuser.testother.utils.https.OkHttpGlideModule "
            android:value="GlideModule"/>

走完三步即可,glide加载图片的方式还是照旧。
接着我们好好说说如何对glide进行配置让他能展示
方案一:信任所有证书

 /**
  * 信任所有https证书
  */
 private SSLContext overlockCard()
 {
 final TrustManager[] trustAllCerts = 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() {
X509Certificate[] x509Certificates = new X509Certificate[0];
 return x509Certificates;
 }
 }};
 try
 {
SSLContext sslContext = SSLContext.getInstance("SSL");
 sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
 return sslContext;
}
 catch (Exception e){
 return null;
 }
 }
@Override
 public void registerComponents(Context context, Glide glide)
 {
 OkHttpClient.Builder clientBuilder=new OkHttpClient.Builder();

OkHttpClient.Builder builder = new OkHttpClient.Builder()
.sslSocketFactory(overlockCard().getSocketFactory())
 .hostnameVerifier(new HostnameVerifier() {
 @Override
public boolean verify(String hostname, SSLSession session)
 {
 //忽略域名检验
 return true;
 }
 }); 
 glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(clientBuilder.build()));
}

方案二:信任服务器的证书

 @Override
public void registerComponents(Context context, Glide glide)
 {
OkHttpClient.Builder clientBuilder=new OkHttpClient.Builder();
 InputStream certificate=getAsserts.open("zs.cer");
 try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
 keyStore.load(null);
 int index = 0;
keyStore.setCertificateEntry("1", certificateFactory.generateCertificate(certificate));  
 try {
 if (certificate != null)
certificate.close();
 } catch (IOException e) {
}
 }
SSLContext sslContext = SSLContext.getInstance("TLS");
 TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
 SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
X509TrustManager trustManager = Platform.get().trustManager(sslSocketFactory);
clientBuilder.sslSocketFactory(sslSocketFactory, trustManager);
clientBuilder.hostnameVerifier(new HostnameVerifier() {
 @Override
 public boolean verify(String hostname, SSLSession session) {
 return true;
 }
});
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(clientBuilder.build()));
 }

这一篇到此为止,希望各位多多点评,下一篇会详细写一下在使用https 自签证书时如何双向验证。

上一篇下一篇

猜你喜欢

热点阅读