RestTemplate Https 证书访问错误

发布于:2025-02-10 ⋅ 阅读:(11) ⋅ 点赞:(0)

错误信息

resttemplate I/O error on GET request for “https://21.24.6.6:9443/authn-api/v5/oauth/token”: java.security.cert.CertificateException: No subject alternative names present; nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

这个错误是由于 SSL/TLS 握手失败引起的,具体原因是服务器的 SSL 证书无效或不受信任。错误信息 No subject alternative names present 表示服务器的证书中没有包含与请求的域名匹配的主题备用名称(Subject Alternative Name, SAN)。

以下是解决此问题的几种方法:

1. 检查服务器证书

确保服务器的 SSL 证书是有效的,并且包含正确的主机名(即请求的域名)。可以通过以下步骤检查证书:

  • 使用浏览器访问服务器的 URL(例如 https://21.24.6.6:9443),查看证书的详细信息。

  • 确保证书的 Subject Alternative Name (SAN) 包含请求的域名或 IP 地址。

如果证书无效或不匹配,需要联系服务器管理员更新证书。

2. 忽略 SSL 证书验证(仅用于测试环境)

在生产环境中不建议使用此方法,但在测试环境中可以临时忽略 SSL 证书验证。

使用 RestTemplate 忽略 SSL 验证

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

public class RestTemplateExample {

    public static void main(String[] args) throws Exception {
        // 创建忽略 SSL 验证的 RestTemplate
        RestTemplate restTemplate = createRestTemplateWithIgnoreSSL();

        // 发送请求
        String url = "https://21.24.6.6:9443/authn-api/v5/oauth/token";
        String response = restTemplate.getForObject(url, String.class);
        System.out.println("Response: " + response);
    }

    private static RestTemplate createRestTemplateWithIgnoreSSL() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        // 创建 SSLContext,忽略证书验证
        SSLContext sslContext = new SSLContextBuilder()
                .loadTrustMaterial(null, (certificate, authType) -> true) // 信任所有证书
                .build();

        // 创建 HttpClient
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLContext(sslContext)
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // 忽略主机名验证
                .build();

        // 创建 RestTemplate
        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(requestFactory);
    }
}

3. 使用自定义信任库

如果服务器使用的是自签名证书或私有 CA 签发的证书,可以将证书导入 Java 的信任库(cacerts)或自定义信任库。

步骤:

  1. 导出服务器的证书:

    • 使用浏览器访问服务器的 URL,导出证书(通常为 .crt 或 .pem 格式)。

    • 或者使用 openssl 命令导出证书:

openssl s_client -connect 21.24.6.6:9443 -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM > server.crt
  1. 将证书导入 Java 信任库:
    • 找到 Java 的默认信任库(通常位于$JAVA_HOME/lib/security/cacerts)。

    • 使用 keytool 将证书导入信任库:

keytool -import -trustcacerts -file server.crt -keystore $JAVA_HOME/lib/security/cacerts -alias server_cert
- 默认密码是 changeit。
  1. 配置 RestTemplate 使用自定义信任库:
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.security.KeyStore;

public class RestTemplateExample {

    public static void main(String[] args) throws Exception {
        // 创建使用自定义信任库的 RestTemplate
        RestTemplate restTemplate = createRestTemplateWithCustomTrustStore();

        // 发送请求
        String url = "https://21.24.6.6:9443/authn-api/v5/oauth/token";
        String response = restTemplate.getForObject(url, String.class);
        System.out.println("Response: " + response);
    }

    private static RestTemplate createRestTemplateWithCustomTrustStore() throws Exception {
        // 加载自定义信任库
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream fis = new FileInputStream("path/to/truststore.jks")) {
            trustStore.load(fis, "password".toCharArray());
        }

        // 创建 SSLContext
        SSLContext sslContext = new SSLContextBuilder()
                .loadTrustMaterial(trustStore, null) // 使用自定义信任库
                .build();

        // 创建 HttpClient
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();

        // 创建 RestTemplate
        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(requestFactory);
    }
}

4. 检查服务器配置

如果服务器配置了 SNI(Server Name Indication),确保客户端支持 SNI。某些旧版本的 Java 可能不支持 SNI,可以尝试升级 Java 版本。

5. 使用 WebClient 替代 RestTemplate

如果使用的是 Spring WebFlux,可以考虑使用 WebClient,它提供了更灵活的 SSL 配置选项。
示例:

import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

public class WebClientExample {

    public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException {
        // 创建忽略 SSL 验证的 WebClient
        WebClient webClient = WebClient.builder()
                .baseUrl("https://21.24.6.6:9443")
                .build();

        // 发送请求
        Mono<String> response = webClient.get()
                .uri("/authn-api/v5/oauth/token")
                .retrieve()
                .bodyToMono(String.class);

        response.subscribe(body -> System.out.println("Response: " + body));
    }
}

总结

  • 如果服务器证书无效或不匹配,需要更新证书。
  • 在测试环境中可以临时忽略 SSL 验证,但不建议在生产环境中使用。
  • 对于自签名证书或私有 CA 签发的证书,可以将证书导入 Java 信任库或自定义信任库。
  • 如果问题仍然存在,可以尝试使用 WebClient 替代 RestTemplate。

网站公告

今日签到

点亮在社区的每一天
去签到