在Java开发场景中,当服务端要求客户端提供证书进行双向认证时,使用OkHttp发起POST请求需要额外配置SSL上下文和密钥材料,才能正常完成通信。下面将从证书准备到请求实现逐步说明具体操作。

前置准备
首先需要准备以下材料:
- 客户端证书文件,通常为PKCS12格式(.p12或.pfx后缀)
- 客户端证书对应的密码
- 服务端信任的根证书(可选,若服务端证书是可信CA签发则可省略)
- OkHttp依赖,Maven项目中添加如下依赖:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.11.0</version>
</dependency>
核心配置步骤
1. 加载客户端证书
首先将PKCS12格式的证书加载到密钥管理工厂中,代码如下:
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyManagerFactory;
import javax.net.ssl.KeyManager;
public class CertConfig {
// 加载客户端证书,返回KeyManager数组
public static KeyManager[] getClientKeyManagers(InputStream p12Stream, String password) throws Exception {
// 初始化PKCS12类型的密钥库
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(p12Stream, password.toCharArray());
// 初始化密钥管理工厂
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
}
}
2. 配置SSL上下文
如果需要信任自定义根证书,还需要配置信任管理器,若使用默认信任规则可直接使用默认信任管理器:
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.io.InputStream;
public class SSLContextConfig {
// 获取配置了客户端证书和信任规则的SSL上下文
public static SSLContext getSSLContext(InputStream p12Stream, String p12Password, InputStream trustCertStream) throws Exception {
// 获取客户端密钥管理器
KeyManager[] keyManagers = CertConfig.getClientKeyManagers(p12Stream, p12Password);
TrustManager[] trustManagers = null;
if (trustCertStream != null) {
// 加载信任的根证书
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate trustCert = certificateFactory.generateCertificate(trustCertStream);
KeyStore trustKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustKeyStore.load(null, null);
trustKeyStore.setCertificateEntry("trustCert", trustCert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustKeyStore);
trustManagers = trustManagerFactory.getTrustManagers();
}
// 初始化SSL上下文
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, new java.security.SecureRandom());
return sslContext;
}
}
3. 构建OkHttp客户端
将配置好的SSL上下文设置到OkHttp客户端中:
import okhttp3.OkHttpClient;
import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
public class OkHttpClientBuilder {
public static OkHttpClient buildClient(InputStream p12Stream, String p12Password, InputStream trustCertStream) throws Exception {
SSLContext sslContext = SSLContextConfig.getSSLContext(p12Stream, p12Password, trustCertStream);
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (javax.net.ssl.X509TrustManager) SSLContextConfig.getSSLContext(p12Stream, p12Password, trustCertStream).getTrustManagers()[0])
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
}
}
发起POST请求示例
完成客户端配置后,就可以发起带客户端证书认证的POST请求,示例代码如下:
import okhttp3.*;
import java.io.FileInputStream;
import java.io.IOException;
public class PostRequestExample {
public static void main(String[] args) {
String p12Path = "/path/to/client.p12";
String p12Password = "client123";
String trustCertPath = "/path/to/root.crt";
String requestUrl = "https://api.ipipp.com/secure/post";
String requestBody = "{"name":"test","age":20}";
try (FileInputStream p12Stream = new FileInputStream(p12Path);
FileInputStream trustStream = new FileInputStream(trustCertPath)) {
// 构建OkHttp客户端
OkHttpClient client = OkHttpClientBuilder.buildClient(p12Stream, p12Password, trustStream);
// 构建POST请求体,这里使用JSON格式
RequestBody body = RequestBody.create(
requestBody,
MediaType.parse("application/json; charset=utf-8")
);
// 构建请求
Request request = new Request.Builder()
.url(requestUrl)
.post(body)
.addHeader("Content-Type", "application/json")
.build();
// 执行请求并获取响应
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful() && response.body() != null) {
System.out.println("请求成功,响应内容:" + response.body().string());
} else {
System.out.println("请求失败,状态码:" + response.code());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意事项
- 证书路径需要替换为实际的本地路径,或者使用类路径加载证书文件
- 如果服务端使用可信CA签发的证书,不需要传入信任证书流,SSL上下文初始化时信任管理器传null即可
- PKCS12证书密码错误会导致密钥库加载失败,需要确认密码正确性
- 生产环境中建议将证书密码等敏感信息放到配置文件或环境变量中,不要硬编码在代码里
- OkHttp版本需要3.0以上,低版本部分API存在差异