如何使用带有证书认证的 RestTemplate?
How to use RestTemplate with certificate authentication?
我有一个端点需要 SSL 身份验证。我能够成功 post 在该端点上的请求:
curl --location --request POST 'https://someurl.click' --header 'some headers' --cert my_cert.pem
我需要创建一个 Spring 引导应用程序,它使用带有 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]...
非常感谢任何建议。
我能够通过使用 JKS 密钥库而不是 p12 证书使其工作。
首先,我使用私钥以及私钥和 public 密钥作为输入来生成 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();
}
我有一个端点需要 SSL 身份验证。我能够成功 post 在该端点上的请求:
curl --location --request POST 'https://someurl.click' --header 'some headers' --cert my_cert.pem
我需要创建一个 Spring 引导应用程序,它使用带有 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]...
非常感谢任何建议。
我能够通过使用 JKS 密钥库而不是 p12 证书使其工作。
首先,我使用私钥以及私钥和 public 密钥作为输入来生成 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();
}