我们在做https接口支持时,用到了X509证书。然后针对X509的证书验证,线下环境不做校验,线上环境仅仅做单向的hostname校验。完善的校验应该包括客户端和服务端的证书校验。
package com.xxx.utils;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
//验证客户端证书,不通过则抛出异常
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
//验证服务端证书,不通过则抛出异常
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// FIXME 线上要做HTTPS证书信息验证
boolean isTrust = false;
//如果线下则默认不校验,isOnline自己做控制
if (!isOnline) {
isTrust = true;
LogUtil.d("线下环境,证书不校验。");
} else {
try {
//获得证书
Certificate[] certificates = session.getPeerCertificates();
if (certificates != null && certificates.length > 0) {
Certificate certificate = certificates[0];
//做X509证书信息获取
if (certificate instanceof X509Certificate) {
X509Certificate x509Certificate = (X509Certificate) certificate;
Collection<List<?>> subjectAlternativeNames = x509Certificate
.getSubjectAlternativeNames();
//获得证书信息
String names = Arrays
.deepToString(subjectAlternativeNames.toArray());
//校验host信息
isTrust = names.contains(hostname);
}
}
} catch (SSLPeerUnverifiedException e) {
LogUtil.e("on error when verify SSL." + e.getMessage());
} catch (CertificateParsingException e) {
LogUtil.e("on error when verify SSL." + e.getMessage());
}
}
if (!isTrust) {
LogUtil.d("证书校验不通过,请核查~(hostname=" + hostname + ")");
}
return isTrust;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[] { new HttpsTrustManager() };
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
}
}
然后在网络请求之前调用如下代码:
//添加https支持
HttpsTrustManager.allowAllSSL();