说明
okhttp是一个用于实现http/https访问的客户端,该文章用来说明如何实现支持https及支持双向认证。
引入
gradle
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
mvaen
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>
代码
//OkhttpUtils.java
import java.io.IOException;
import java.util.UUID;
import javax.net.ssl.HostnameVerifier;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
//平台接口访问工具
public class OkhttpUtils {
public static boolean isHttps = true;
public static boolean safetySwitch = true;
public static boolean certificateIsCA = true;
//自签名证书关闭验证
public static HostnameVerifier DO_NOT_VERIFY = (hostname, session) -> true;
//获取访问前缀
public static String getHttpOrHttps(){
return isHttps ? "https://" : "http://";
}
//测试https双向访问
public void httpsTest(){
OkHttpClient.Builder clientbuilder = new OkHttpClient.Builder();
if (safetySwitch) clientbuilder.sslSocketFactory(SSLUtils.getCertificates(SSLUtils.getBks(),SSLUtils.getCer()));
if (certificateIsCA) clientbuilder.hostnameVerifier(DO_NOT_VERIFY);
OkHttpClient client = clientbuilder.build();
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("key","value");
MultipartBody requestBody = builder.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + UUID.randomUUID())
.url(getHttpOrHttps() + "hostname.com/https")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) {
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
//SSLUtils.java
import java.io.*;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class SSLUtils {
///证书请求文件bks,用于双向认证
public static InputStream getBks(){
File file = new File("client_private.bks");
try {
InputStream i = new FileInputStream(file);
return i;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
//证书cer
public static List<InputStream> getCer(){
try {
File file = new File("server_public.cer");
InputStream i = new FileInputStream(file);
List<InputStream> publicHers = new ArrayList<>();
publicHers.add(i);
return publicHers;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//获取ssl双向认证的SSLSocketFactory
public static SSLSocketFactory getCertificates(InputStream privateMe,List<InputStream> publicHers){
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
//远程
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : publicHers)
{
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
certificate.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//本地
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(privateMe, "123456".toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, "123456".toCharArray());
//如何不启用双向认证,则init第一个参数传null即可。
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}