虽然网上ok适配https 的文章一大堆。但是我在用
retrofit2 +Rxjava +Ok 的时候,由于IOS 那边需要适配https,也为了防止资讯页面的网页被运营商劫持。就开始做https 的适配。
发现网络上很多文章,经过一天的实践,发现我的不是请求不到数据,就是闪退,闪退的原因是项目集成了nuwa 热修复。app初始化的时候就请求服务器端的jar。在application类初始化之前请求没有
Content对象没获取到证书,好了 这里 废话不多说,请欣赏核心代码。
private static OkHttpClient myClient; static SSLSocketFactory sslSocketFactory = null; static X509TrustManager trustManager; public static OkHttpClient getOkHttpClient() { if (myClient == null) { try { SSLContext sslContext = trustManagerForCertificates(ttrustedCertificatesInputStream()); //SSLContext.getInstance("TLS"); // setCard(ttrustedCertificatesInputStream()); sslSocketFactory = sslContext.getSocketFactory(); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); } HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); Interceptor mTokenInterceptor = new Interceptor() {//网络请求拦截器 这里是在所有网络请求的header里加上apikey @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request authorised = originalRequest.newBuilder().addHeader("head", "head").build(); return chain.proceed(authorised); } }; myClient = new OkHttpClient.Builder().addInterceptor(interceptor).retryOnConnectionFailure(true).connectTimeout(15, TimeUnit.SECONDS).sslSocketFactory(sslSocketFactory).hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }).addNetworkInterceptor(mTokenInterceptor).build(); } return myClient; } public static OkHttpClient getOkHttpClient(Context cc) { if (myClient == null) { SSLContext sslContext = null; //SSLContext.getInstance("TLS"); try { sslContext = trustManagerForCertificates(ttrustedCertificatesInputStream(cc)); sslSocketFactory = sslContext.getSocketFactory(); } catch (GeneralSecurityException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // setCard(ttrustedCertificatesInputStream(cc)); HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); Interceptor mTokenInterceptor = new Interceptor() {//网络请求拦截器 这里是在所有网络请求的header里加上apikey @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request();Request authorised = originalRequest.newBuilder().addHeader("head", "head").build();//这儿的head为了上传头像的时候加的 return chain.proceed(authorised); } }; myClient = new OkHttpClient.Builder().retryOnConnectionFailure(true).addInterceptor(interceptor).addNetworkInterceptor(mTokenInterceptor).sslSocketFactory(sslSocketFactory).hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }).connectTimeout(15, TimeUnit.SECONDS).build(); } return myClient; } private static InputStream ttrustedCertificatesInputStream(Context cc) { InputStream inputStream;//这儿写工厂设计 根据url 打包不同的证书。是为了打包方便 if (Config.APP_URL2.equals("你的线上服url")) { inputStream = cc.getResources().openRawResource(R.raw.线上服证书); } else if (Config.APP_URL2.equals("你的线上服url")) { inputStream = cc.getResources().openRawResource(R.raw.测试服证书); } else { inputStream = cc.getResources().openRawResource(R.raw.线上服证书); }//这儿是最容易出问题的地方。网上好多地方直接 返回cc.getResources().openRawResource(R.raw.线上服证书)。我直接 这样做请求不到数据,而且只有一个certificate 对象 不知道各位有没有出现。所以在外面套了一个bufferread 来读取 return new BufferedInputStream(inputStream); // return new BufferedInputStream(cc.getResources().openRawResource(R.raw.线上服证书)); // return trustedCertificatesInputStream(); } private static InputStream ttrustedCertificatesInputStream() { InputStream inputStream; if (Config.APP_URL2.equals("线上服url")) { inputStream = MyApplication.mContext.getResources().openRawResource(R.raw.线上服证书); } else if (Config.APP_URL2.equals("测试服url")) { inputStream = MyApplication.mContext.getResources().openRawResource(R.raw.测试服证书); } else { inputStream = MyApplication.mContext.getResources().openRawResource(R.raw.线上服证书); } return new BufferedInputStream(inputStream); // return trustedCertificatesInputStream(); } private static SSLContext trustManagerForCertificates(InputStream in) throws GeneralSecurityException, IOException { // CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");// 这儿也是最容易出问题的,网上的大部分文章缺少后面的参数,。我把后面的 BC去掉,直接异常闪退。不知道各位有没有遇到。 CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC"); Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in); if (certificates.isEmpty()) { throw new IllegalArgumentException("expected non-empty set of trusted certificates"); } char[] password = CLIENT_KET_PASSWORD.toCharArray(); // Any password will work. KeyStore keyStore = newEmptyKeyStore(password); 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("SSL"); ssContext.init(keyManagerFactory.getKeyManagers(), trustManagers, null); return ssContext; } private static KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = null; keyStore.load(in, password); return keyStore; } catch (IOException e) { throw new AssertionError(e); } }
写两个是 因为 热修复 之前的 引导页需要获取 服务器的热修复jar 包,所以重写了一个方法。。。。谢谢观看。。。