Android合规问题引起的https证书校验
Posted 汤米粥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android合规问题引起的https证书校验相关的知识,希望对你有一定的参考价值。
//1、设置httpsURLConnection.setSSLSocketFactory
public static HttpURLConnection getHttpURLConnection(URL url) throws IOException
HttpURLConnection conn = null;
if (url.toString().startsWith("https"))
final HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setHostnameVerifier(HOSTNAME_VERIFIER);
try
httpsURLConnection.setSSLSocketFactory(getSSLSocketFactory(null));
catch (NoSuchAlgorithmException | KeyManagementException e)
e.printStackTrace();
conn = httpsURLConnection;
else
conn = (HttpURLConnection) url.openConnection();
return conn;
/**
* '
* 暂时解决合规问题
* <p>
* 使用服务器端证书,只信任指定证书。
* 但是由于服务器端证书3个月一变,如果过了12.10后为了防止访问不了,信任所有证书。
*
* @param context
* @return
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public static SSLSocketFactory getSSLSocketFactory(Context context) throws NoSuchAlgorithmException, KeyManagementException
X509TrustManager xtm;
if (System.currentTimeMillis() < 1670662401000l && context != null)
if (SSLConfigUtils.sslKey == null)
xtm = createTrustCustomTrustManager(getInputStreamFromAsset(context));
// Toast.makeText(context,"证书校验1",0).show();
else
xtm = createTrustCustomTrustManager(new ByteArrayInputStream(SSLConfigUtils.sslKey.getBytes()));
// Log.e("xxx-我的证书",SSLConfigUtils.sslKey);
// Toast.makeText(context,"证书校验2",0).show();
else
xtm = getX509TrustManager();
// Toast.makeText(context,"证书不校验",0).show();
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]xtm, new SecureRandom());
return sslContext.getSocketFactory();
/**
* 创建只信任指定证书的TrustManager
*
* @param inputStream:证书输入流
* @return
*/
private static X509TrustManager createTrustCustomTrustManager(InputStream inputStream)
try
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
Certificate certificate = certificateFactory.generateCertificate(inputStream);
//将证书放入keystore中
String certificateAlias = "ca";
keyStore.setCertificateEntry(certificateAlias, certificate);
if (inputStream != null)
inputStream.close();
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));
return (X509TrustManager) trustManagers[0];
catch (Exception e)
e.printStackTrace();
return null;
/**
* 读取放在assets里的证书(服务器端配置的证书公钥)
*
* @param context
* @return
*/
private static InputStream getInputStreamFromAsset(Context context)
InputStream inputStream = null;
try
inputStream = context.getAssets().open("ssl.pem");
catch (IOException e)
e.printStackTrace();
return inputStream;
/**
* 创建信任所有证书的TrustManager
*/
@NonNull
public static X509TrustManager getX509TrustManager()
return new X509TrustManager()
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
@Override
public X509Certificate[] getAcceptedIssuers()
return new X509Certificate[];
;
import android.util.Log;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class SSLConfigUtils
private InputStream inputStream;
public static String sslKey;
public static InputStream doGetSSL()
BufferedReader reader = null;
String bookJsonString = null;
InputStream inputStream = null;
Log.e("xxx", "获取证书");
try
//1.HttpURLConnection建立连接
HttpURLConnection httpURLConnection = null;
String url = "https://xxx.com/xxx/ssl.pem";
URL requestUrl = null;
try
requestUrl = new URL(url);
httpURLConnection = (HttpURLConnection) requestUrl.openConnection();//打开连接
httpURLConnection.setRequestMethod("GET");//两种方法GET/POST
httpURLConnection.setConnectTimeout(5000);//设置超时连接时间
httpURLConnection.connect();
//2.InputStream获取二进制流
inputStream = httpURLConnection.getInputStream();
//3.InputStreamReader将二进制流进行包装成BufferedReader
reader = new BufferedReader(new InputStreamReader(inputStream));
//4.从BufferedReader中读取String字符串,用StringBulider接收
StringBuilder bulider = new StringBuilder();
String line;
while ((line = reader.readLine()) != null)
bulider.append(line);
bulider.append("\\n");
//5.StringBulider将字符串进行拼接
bookJsonString = bulider.toString();
// Log.e("xxx", bookJsonString);
sslKey = bookJsonString;
catch (Exception e)
e.printStackTrace();
catch (Exception e)
e.printStackTrace();
return inputStream;
现在来讲解一下上述代码解决了一个什么事情 :
1.首先网信办、应用市场要求不能用明文传输,所以使用了https传输。
2.使用https发现网络请求不了,因为https要求证书通过验证,为了接口能正常访问,于是设置了X509TrustManager 信任所有证书。
事情到这儿好像是解决了,但相关部门又提出来了,X509TrustManager这种信任所有证书存在安全隐患,不合规。使用charles抓包亲测(将charles证书导出安装到手机上)的确能抓取到https传输的明文信息。
3.于是改成只信任指定ssl证书。具体做法是:在app端上assets目录里存放服务器端给的ssl证书 *.pem(公钥),然后通过getInputStreamFromAsset的方式读取,接着调用 xtm = createTrustCustomTrustManager(getInputStreamFromAsset(context));设置只信任服务器端给的指定ssl证书。通过添加上述操作之后,再使用charles抓包(将charles证书导出安装到手机上),发现抓取不到网络请求。到此第2步的问题解决。
某天发现app访问不了网络,原来是服务器端的ssl证书是在国内某机构申请的免费证书,3个月就会更新一次。服务器端证书更新了,但存放在app端的还是旧的证书,故会出现访问不了网络的问题。于是出现了接下来的操作。
4.服务器端证书发生变化时及时放到网络上,通过接口(见上面代码SSLConfigUtils )下载到本地,然后在接口请求中设置信任下载的证书。
虽然这样把证书放到网络上不安全,但能解决相关部门的检查,就这样吧。 针对这种服务器端https证书不定期更新的问题,你们有什么好的解决办法吗?
以上是关于Android合规问题引起的https证书校验的主要内容,如果未能解决你的问题,请参考以下文章