java 如何将RestTemplate与证书身份验证一起使用?

kcugc4gi  于 2023-04-28  发布在  Java
关注(0)|答案(1)|浏览(356)

我有一个端点需要SSL身份验证。我能够成功地在该端点上发布请求:
curl --location --request POST 'https://someurl.click' --header 'some headers' --cert my_cert.pem
我需要创建一个Sping Boot 应用程序,它使用RestTemplate的证书向该端点发布请求。我读到PEM证书是无效的,我需要使用p12或JKS。
所以我把我的证书转换为p12:
openssl pkcs12 -export -in my_cert.pem -out my_cert.p12
并在我的Bean中使用它作为信任存储,正如几个参考文献所建议的那样(其中trustStore指向转换后的p12证书):

@Value("${trust-store}")
    private Resource trustStore;

    @Value("${trust-store-password}")
    private String trustStorePassword;

    @Bean
    public RestTemplate getRestTemplate(RestTemplateBuilder builder) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        SSLContext sslContext = new SSLContextBuilder()
        .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray())
        .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);

    HttpClient httpClient = HttpClients.custom()
        .setSSLSocketFactory(socketFactory)
        .build();

    return builder
        .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
        .build();
    }

当使用此RestTemplate时,我有SSL握手错误。
验证码:

String response = restTemplate.postForObject(myEndpoint, request, String.class);

输出:

2021-08-04 12:06:41.926 ERROR 129727 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://myurl.click": readHandshakeRecord; nested exception is javax.net.ssl.SSLException: readHandshakeRecord] with root cause

java.net.SocketException: Broken pipe (Write failed)
        at java.base/java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:na]
        at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110) ~[na:na]
        at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150) ~[na:na]
        at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeChangeCipherSpec(SSLSocketOutputRecord.java:221) ~[na:na]
        at java.base/sun.security.ssl.OutputRecord.changeWriteCiphers(OutputRecord.java:162) ~[na:na]
        at java.base/sun.security.ssl.ChangeCipherSpec$T10ChangeCipherSpecProducer.produce(ChangeCipherSpec.java:118) ~[na:na]
        at java.base/sun.security.ssl.Finished$T12FinishedProducer.onProduceFinished(Finished.java:395) ~[na:na]
        at java.base/sun.security.ssl.Finished$T12FinishedProducer.produce(Finished.java:379) ~[na:na]...

任何建议都非常感谢。

1l5u6lss

1l5u6lss1#

我可以通过使用JKS密钥库而不是p12证书来使它工作。
首先,我使用私钥以及私钥和公钥作为输入来生成P12证书:

openssl pkcs12 -export -inkey <private_key>.pem -in <all_keys>.pem -name new_certificate -out certificate.p12

最后,我使用keytool将P12证书转换为JKS密钥库:

keytool -importkeystore -srckeystore certificate.p12 -srcstoretype pkcs12 -destkeystore certificate.jks

然后,我终于可以使用SSLContext将其加载到RestTemplate示例中:

@Value("${trust-store}")
private String trustStore;
@Value("${trust-store-password}")
private String trustStorePassword;

@Bean
public RestTemplate getRestTemplate(RestTemplateBuilder builder) throws IOException, CertificateException,
                                                                        NoSuchAlgorithmException, KeyStoreException,
                                                                        KeyManagementException, UnrecoverableKeyException {
    Resource trustStoreCertificate = new ClassPathResource(trustStore);
    File certificate = new File("combined.jks");
    FileUtils.copyInputStreamToFile(trustStoreCertificate.getInputStream(), certificate);
    SSLContext sslContext = new SSLContextBuilder()
        .loadKeyMaterial(certificate , trustStorePassword.toCharArray(), trustStorePassword.toCharArray()).build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
    
    HttpClient httpClient = HttpClients.custom()
        .setSSLSocketFactory(socketFactory)
        .build();
    
    certificate.delete();
    
    return builder
        .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
        .build();
}

相关问题