使用okhttp作为网络连接时,
Okhttp现作为Android网络连接框架已经很流行了,我也使用okhttp有三年了。以前https还是使用得少的,访问别人的网站,如果使用了https都是绿色的(在浏览器上看,某购火车票网站的不是),在okhttp里啥问题也没有,最近的项目是公司自己的服务器使用了https,但证书不是购买的,是自己产生的(使用jdk自产的),okhttp默认拒绝访问这自产证书的https的连接。
怎么办?查看okhttp说明,是可以向okhttp里添加证书的。下面是向okhttp添加证书的内容了。证书可以浏览器上导出,导时选择X509,导出结果的xxx.cer。
为了方便各模块添加自己的证书,各模块把需要用到的证书都放在assets里和同一路径的目录下(我放置的目录为assets/certs),Android Studio编译时会把所以模块里的assets/cert统一起来,到时遍历这目录就能读取到所有证书了。
添加证书,可以在okhttp访问网络前,但我放置在Application.OnCreate里,目的为了让okhttp使用时不用传入Context,也不用Context满天飞。
读取证书文件代码如下:
// 添加https证书
try {
String[] certFiles=this.getAssets().list("certs");
if (certFiles != null) {
for (String cert:certFiles) {
InputStream is = getAssets().open("certs/" + cert);
NetConfig.addCertificate(is); // 这里将证书读取出来,,放在配置中byte[]里
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
NetConfig的相关代码:
// 证书数据
private static List<byte[]> CERTIFICATES_DATA = new ArrayList<>();
/**
* 添加https证书
* @param inputStream
*/
public synchronized static void addCertificate(InputStream inputStream) {
Log.i(TAG,"#addCertificate inputStream = " + inputStream);
if (inputStream != null) {
try {
int ava = 0;// 数据当次可读长度
int len = 0;// 数据总长度
ArrayList<byte[]> data = new ArrayList<>();
while ((ava = inputStream.available()) > 0) {
byte[] buffer = new byte[ava];
inputStream.read(buffer);
data.add(buffer);
len += ava;
}
byte[] buff = new byte[len];
int dstPos = 0;
for (byte[] bytes:data) {
int length = bytes.length;
System.arraycopy(bytes, 0, buff, dstPos, length);
dstPos += length;
}
CERTIFICATES_DATA.add(buff);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* https证书
* @return
*/
public static List<byte[]> getCertificatesData() {
return CERTIFICATES_DATA;
}
那么现在到将证书添加到okhttp里去了:
public OkHttpClient createOkhttp() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 添加证书
List<InputStream> certificates = new ArrayList<>();
List<String> certs = NetConfig.getCertificates();
List<byte[]> certs_data = NetConfig.getCertificatesData();
// 将字节数组转为数组输入流
if (certs_data != null && !certs_data.isEmpty()) {
for (byte[] bytes:certs_data) {
certificates.add(new ByteArrayInputStream(bytes));
}
}
SSLSocketFactory sslSocketFactory = getSocketFactory(certificates);
if (sslSocketFactory != null) {
builder.sslSocketFactory(sslSocketFactory);
}
return builder.build();
}
/**
* 添加证书
*
* @param certificates
*/
private static SSLSocketFactory getSocketFactory(List<InputStream> certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
try {
for (int i = 0, size = certificates.size(); i < size; ) {
InputStream certificate = certificates.get(i);
String certificateAlias = Integer.toString(i++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
if (certificate != null)
certificate.close();
}
} catch (IOException e) {
e.printStackTrace();
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init
(
null,
trustManagerFactory.getTrustManagers(),
new SecureRandom()
);
return sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Ok,https就这样了。