如何为Apache HttpClient配置ca.pem、key.pem和cert.pem证书
配置HttpClient加载PEM格式证书访问服务
要让HttpClient正确使用ca.pem、key.pem和cert.pem三个PEM文件,需要修改现有代码,正确加载证书和私钥到SSL上下文,而非简单信任所有证书。以下是具体实现方案:
核心步骤
- 加载CA证书到信任库,用于验证服务端证书合法性
- 加载客户端证书和私钥到密钥库,用于服务端验证客户端身份
- 基于信任库和密钥库构建合规的SSLContext
- 配置HttpClient使用该SSLContext,保留原有拦截器等业务配置
修改后的完整代码
import org.apache.http.HttpRequestInterceptor; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.ssl.SSLContextBuilder; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import java.io.FileInputStream; import java.io.FileReader; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; public class HttpClientCertConfig { public HttpComponentsClientHttpRequestFactory createCertEnabledRequestFactory() throws Exception { // 初始化证书工厂 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); // 加载CA证书并构建信任库 X509Certificate caCert = (X509Certificate) certFactory.generateCertificate( new FileInputStream(getClass().getResource("/certs/ca.pem").getPath())); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null); trustStore.setCertificateEntry("ca-trust-cert", caCert); // 加载客户端证书 X509Certificate clientCert = (X509Certificate) certFactory.generateCertificate( new FileInputStream(getClass().getResource("/certs/cert.pem").getPath())); // 加载客户端私钥(依赖BouncyCastle解析PEM格式私钥) PEMParser pemParser = new PEMParser(new FileReader(getClass().getResource("/certs/key.pem").getPath())); Object keyObj = pemParser.readObject(); JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter(); PrivateKey clientPrivateKey = keyConverter.getPrivateKey((org.bouncycastle.asn1.pkcs.PrivateKeyInfo) keyObj); // 构建客户端密钥库 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); // 若私钥有密码,替换第二个参数为实际密码字符串 keyStore.setKeyEntry("client-auth-key", clientPrivateKey, "".toCharArray(), new X509Certificate[]{clientCert}); // 构建带证书验证的SSLContext SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial(trustStore, null) // 仅信任指定CA证书 .loadKeyMaterial(keyStore, "".toCharArray()) // 加载客户端身份证书 .build(); // 配置HttpClient CloseableHttpClient httpClient = HttpClients.custom() .setSSLContext(sslContext) // 生产环境建议移除该配置,启用默认域名验证逻辑,避免安全风险 .setSSLHostnameVerifier(new NoopHostnameVerifier()) .addInterceptorFirst((HttpRequestInterceptor) (httpRequest, httpContext) -> { httpRequest.setHeader("Content-Type", "application/xml"); }) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); return requestFactory; } }
依赖说明
解析PEM格式私钥需引入BouncyCastle依赖(Maven示例):
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.70</version> </dependency>
关键注意事项
- 私钥若设置了密码,需将代码中的空字符串
""替换为实际密码 - 生产环境必须移除
NoopHostnameVerifier,启用默认的域名验证,防止中间人攻击 - 确保三个PEM文件放置在类路径下的
certs目录,或根据实际路径调整资源读取逻辑
内容的提问来源于stack exchange,提问作者Robert Wilson
相关产品推荐
相关产品推荐

