HttpClient 中自定义SSL

当下面两种情况的SSL,用HttpClient建立连接时,需要使用自定义SSL:

 

  • Ability to accept self-signed or untrusted SSL certificates. This is highlighted by an SSLException with the message Unrecognized SSL handshake (or similar) being thrown when a connection attempt is made.
  • You want to use a third party SSL library instead of Sun's default implementation.

常见代码如下:

 

PostMethod post = new PostMethod(masupServiceURL);
Protocol myhttps = new Protocol("https",
                new MySecureProtocolSocketFactory(), 443);

Protocol.registerProtocol("https", myhttps);

HttpClient client = new HttpClient();

 

上面的代码中MySecureProtocolSocketFactory的作用就是当SSL证书不受信任(如证书过期),HttpClient可以自动接受证书。当然这种用法有一定的安全隐患,因此当使用这种方法时,还是得先考虑需求。

<think>我们正在解决如何自定义Spring Boot中的SSL证书验证。用户之前遇到了SSLHandshakeException,因为证书的SAN不包含IP地址。现在用户想要在Spring Boot中配置自定义SSL证书验证,可能包括信任特定的证书或绕过某些检查(如主机名验证)。 参考引用: 引用[1]提到了在Spring Boot中配置SSL证书实现HTTPS,以及如何与Nginx联合使用。其中涉及到在application.yml中配置SSL证书: server.ssl.key-store: classpath:cert.pfx server.ssl.key-store-password: 123 server.ssl.keyStoreType: PKCS12 引用[2]和[3]主要讲述了如何申请免费SSL证书以及配置到Spring Boot中。 但是,用户现在需要的是“自定义SSL证书验证”,而不仅仅是配置内置的SSL证书。自定义验证可能包括: 1. 信任自签名证书 2. 忽略主机名验证(比如用户之前遇到的IP地址不匹配问题) 3. 使用自定义的信任库或特定的证书验证逻辑 在Spring Boot中,我们可以通过自定义RestTemplate或WebClient来实现自定义SSL验证。对于Spring Boot 2.x,通常使用WebClient(响应式)或RestTemplate(同步)进行HTTP调用。这里我们以RestTemplate为例。 步骤: 1. 创建自定义SSLContext,其中可以设置信任管理器(TrustManager)来忽略证书验证(不安全,仅用于测试)或添加特定的证书。 2. 使用自定义SSLContext创建HttpClient。 3. 将此HttpClient配置到RestTemplate中。 注意:在生产环境中,应避免完全禁用证书验证,而应该只信任特定的证书。 下面提供两种常见场景的配置方法: 场景一:忽略所有SSL证书验证(仅用于开发测试) 这种方法会接受任何证书(包括自签名和无效的),因此不安全。 场景二:使用自定义信任库(包含特定的自签名证书) 这种方法只信任我们指定的证书,相对安全。 下面分别实现: 场景一:忽略所有证书验证(不安全) ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import javax.net.ssl.*; import java.net.HttpURLConnection; import java.security.cert.X509Certificate; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() throws Exception { // 创建一个忽略SSL验证的SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, new java.security.SecureRandom()); // 创建自定义的请求工厂 SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory() { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { super.prepareConnection(connection, httpMethod); if (connection instanceof HttpsURLConnection) { ((HttpsURLConnection) connection).setSSLSocketFactory(sslContext.getSocketFactory()); ((HttpsURLConnection) connection).setHostnameVerifier((hostname, session) -> true); } } }; return new RestTemplate(requestFactory); } } ``` 场景二:使用自定义信任库(推荐) 假设我们在resources下有一个自定义的信任库文件(mytruststore.jks),其中包含我们信任的证书。 步骤: 1. 配置一个SSLContext,使用自定义的信任库。 2. 创建RestTemplate时使用这个SSLContext。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import javax.net.ssl.*; import java.io.InputStream; import java.net.HttpURLConnection; import java.security.KeyStore; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() throws Exception { // 加载信任库 KeyStore trustStore = KeyStore.getInstance("JKS"); InputStream trustStoreStream = new ClassPathResource("mytruststore.jks").getInputStream(); trustStore.load(trustStoreStream, "truststore-password".toCharArray()); // 创建TrustManagerFactory,使用自定义信任库 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); // 创建SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); // 创建自定义的请求工厂 SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory() { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { super.prepareConnection(connection, httpMethod); if (connection instanceof HttpsURLConnection) { ((HttpsURLConnection) connection).setSSLSocketFactory(sslContext.getSocketFactory()); // 可以自定义主机名验证,如果需要严格验证主机名,则应使用默认的验证器 ((HttpsURLConnection) connection).setHostnameVerifier((hostname, session) -> { // 在这里实现自定义的主机名验证逻辑,比如允许特定的主机名 // 例如:如果hostname等于某个值,则返回true,否则false return true; // 这里忽略主机名验证,仅用于示例。实际应谨慎使用! }); } } }; return new RestTemplate(requestFactory); } } ``` 注意:在场景二中,我们仍然可以自定义主机名验证。如果希望严格验证主机名,可以删除setHostnameVerifier那一行(使用默认验证),或者实现自己的逻辑。 另外,如果Spring Boot应用本身作为HTTPS服务器,用户想要自定义客户端连接时的证书验证,那么上述配置是针对客户端的。如果是服务器端的SSL验证(即验证客户端证书),则需要配置双向SSL(mutual SSL),这需要不同的配置。 根据用户之前的问题(SSLHandshakeException),主要是客户端连接服务器时发生的,因此上述配置是针对RestTemplate作为客户端的情况。 如果用户需要配置服务器端的SSL(即Spring Boot应用提供HTTPS服务并验证客户端证书),则需要在application.properties中配置: server.ssl.client-auth=need # 要求客户端提供证书 server.ssl.trust-store: classpath:truststore.jks server.ssl.trust-store-password: password 这样,服务器将要求客户端提供证书,并使用trust-store中的证书来验证客户端。 总结: - 对于客户端(比如使用RestTemplate调用外部HTTPS服务),可以通过自定义RestTemplate来定制SSL验证。 - 对于服务器端,可以通过配置server.ssl相关属性来实现单向或双向SSL。 最后,生成相关问题: 1. 如何在Spring Boot中配置双向SSL(mutual SSL)? 2. 如何在Spring Boot中使用WebClient进行自定义SSL验证? 3. 如何将自签名证书添加到Java信任库(cacerts)? 4. 如何生成用于Spring Boot的JKS或PKCS12格式的证书? 注意:在回答中,我们引用了前面提到的配置方法,但并没有直接使用引用[1][2][3]中的内容,因为引用主要讲的是如何配置内置的SSL证书,而用户需要的是自定义验证。不过,在自定义信任库的场景中,我们提到了将信任库文件放在resources下,这与引用[3]中放置证书文件的方式类似,可以看作是一种参考。 因此,在回答末尾,我们可以添加引用标识,但要注意我们的解决方案并没有直接引用,所以可以不标注。但如果用户需要了解如何配置内置HTTPS,可以参考引用[1][3]。 </think>### Spring Boot 中自定义 SSL 证书验证的配置方法 在 Spring Boot 中自定义 SSL 证书验证主要涉及两种场景:**客户端验证**(调用外部 HTTPS 服务)和**服务端验证**(作为 HTTPS 服务器)。以下是详细配置方案: --- #### 一、客户端证书验证(HTTP 调用场景) 当使用 `RestTemplate` 或 `WebClient` 调用外部 HTTPS 服务时,需要自定义证书验证逻辑: ```java @Configuration public class SSLConfig { @Bean public RestTemplate restTemplate() throws Exception { // 1. 创建自定义信任管理器 TrustManager[] trustManagers = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) {} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { // 自定义验证逻辑:例如检查特定证书指纹 if (!"A1:B2:C3".equals(calculateThumbprint(chain[0]))) { throw new CertificateException("证书指纹不匹配"); } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }; // 2. 创建SSL上下文 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, new SecureRandom()); // 3. 配置RestTemplate SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setOutputStreaming(false); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClients.custom() .setSSLContext(sslContext) .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // 可选: 禁用主机名验证 .build() ); return new RestTemplate(factory); } private String calculateThumbprint(X509Certificate cert) { // 实现证书指纹计算逻辑 (如 SHA-256) } } ``` --- #### 二、服务端证书验证(HTTPS 服务器) 作为 HTTPS 服务端时,配置双向认证和自定义验证: 1. **application.yml 基础配置** ```yaml server: port: 8443 ssl: key-store: classpath:server.jks key-store-password: changeit key-alias: my-server trust-store: classpath:truststore.jks # 信任的客户端CA trust-store-password: changeit client-auth: need # 要求客户端证书 ``` 2. **自定义证书验证器** ```java @Configuration public class SSLServerConfig { @Bean public TomcatServletWebServerFactory servletContainer() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.addConnectorCustomizers(connector -> { connector.setProperty("SSLEnabled", "true"); ((AbstractHttp11Protocol<?>) connector.getProtocolHandler()) .setSSLVerifyClient(SSLVerifyClient.REQUIRED); connector.addUpgradeProtocol(new Http2Protocol()); }); return factory; } @Bean public TomcatContextCustomizer customCertValidator() { return context -> { context.setUseHttpOnly(true); context.addParameter("certificateVerifier", "com.example.CustomCertificateVerifier"); }; } } ``` 3. **实现证书验证类** ```java public class CustomCertificateVerifier implements CertificateVerifier { @Override public boolean verify(X509Certificate[] certChain, String remoteIp) { // 自定义验证逻辑(如检查IP白名单) return "192.168.1.100".equals(remoteIp) && "CN=Trusted Client".equals(certChain[0].getSubjectX500Principal().getName()); } } ``` --- #### 三、关键配置说明 | **组件** | **作用** | **推荐方案** | |---------------------|-------------------------------------------------------------------------|----------------------------------------| | `X509TrustManager` | 客户端证书验证入口点 | 重写 `checkServerTrusted()` 方法 | | `SSLContext` | 管理 TLS/SSL 协议实现 | 使用自定义 `TrustManager` 初始化 | | `client-auth` | 服务端配置客户端证书要求 | `need`(必须)/`want`(可选) | | `CertificateVerifier`| 服务端自定义证书验证逻辑 | 实现 IP/证书主题等自定义规则 | --- #### 四、证书管理最佳实践 1. **生成含 IP SAN 的证书** ```bash openssl req -newkey rsa:2048 -nodes -keyout server.key \ -x509 -days 365 -out server.crt \ -addext "subjectAltName = IP:192.168.1.100" ``` 2. **转换 JKS 格式** ```bash keytool -importcert -file server.crt -keystore truststore.jks -alias server-cert ``` 3. **验证证书信息** ```bash keytool -list -v -keystore server.jks ``` > 注:生产环境应使用权威 CA 证书,自签名证书仅限测试环境使用[^1][^3]。 --- ### 典型应用场景 1. **金融系统**:验证特定 CA 颁发的客户端证书 2. **物联网设备**:通过 IP SAN 绑定设备证书 3. **内部系统**:自定义证书指纹白名单
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值