I've created two servers locally, and I'm going to apply a mutual authentication to their communication. I just don't know what the problem is. I lack understanding of this mechanism, but I also lack understanding of the server itself. Please understand that I am a beginner developer who has no knowledge of SSL and the web.
- Create each key store
keytool -genkey -alias julie-server-key -keyalg RSA -keypass 11111111 -storepass 11111111 -keystore julie/src/main/resources/julie-server-key.jks -validity 365 -keysize 2048 -dname "CN=localhost,OU=edge,O=edge,L=edge,S=edge,C=KR"
keytool -genkey -alias client-key -keyalg RSA -keypass 11111111 -storepass 11111111 -keystore transmitter/src/main/resources/client-key.jks -validity 365 -keysize 2048 -dname "CN=localhost,OU=edge,O=edge,L=edge,S=edge,C=KR"
- Export to X.509 to register certificate in Truststore.
keytool -export -alias julie-server-key -keystore julie/src/main/resources/julie-server-key.jks -file server_X509.cer -storepass 11111111
keytool -export -alias client-key -keystore transmitter/src/main/resources/client-key.jks -file client_X509.cer -storepass 11111111
- Register each other's certificates in Truststore.
keytool -import -alias client-key -keystore julie/src/main/resources/server.truststore -file transmitter/src/main/resources/client_X509.cer -storepass 11111111
keytool -import -alias julie-server-key -keystore transmitter/src/main/resources/client.truststore -file julie/src/main/resources/server_X509.cer -storepass 11111111
- server's application.properties
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=/Users/julie/IdeaProjects/cloud/julie/src/main/resources/julie-server-key.jks
server.ssl.key-store-password=11111111
server.ssl.key-store-type=jks
server.ssl.key-alias=julie-server-key
erver.ssl.trust-store=/Users/julie/IdeaProjects/cloud/julie/src/main/resources/server.truststore
server.ssl.trust-store-password=11111111
server.ssl.trust-store-type=jks
server.ssl.client-auth=need
- client's application.properties
server.port=8080
server.ssl.key-store=/Users/julie/IdeaProjects/cloud/transmitter/src/main/resources/client-key.jks
server.ssl.key-store-password=11111111
server.ssl.trust-store=/Users/julie/IdeaProjects/cloud/transmitter/src/main/resources/client.truststore
server.ssl.trust-store-password=11111111
- The application main function of the client was set as follows. server as well
@SpringBootApplication
public class TransmitterApplication {
private static String keyStorePath;
private static String keyStorePassword;
private static String trustKeyStorePath;
private static String trustKeyStorePassword;
@Value("${server.ssl.key-store}")
public void setKeyStorePath(String keyStorePath) {
TransmitterApplication.keyStorePath = keyStorePath;
}
@Value("${server.ssl.key-store-password}")
public void setKeyStorePassword(String keyStorePassword) {
TransmitterApplication.keyStorePassword = keyStorePassword;
}
@Value("${server.ssl.trust-store}")
public void setTrustKeyStorePath(String trustKeyStorePath) {
TransmitterApplication.trustKeyStorePath = trustKeyStorePath;
}
@Value("${server.ssl.trust-store-password}")
public void setTrustKeyStorePassword(String trustKeyStorePassword) {
TransmitterApplication.trustKeyStorePassword = trustKeyStorePassword;
}
public static void main(String[] args) {
SpringApplication.run(TransmitterApplication.class, args);
System.setProperty("javax.net.debug", "all");
System.setProperty("javax.net.ssl.keyStore", keyStorePath);
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
System.setProperty("javax.net.ssl.trustStore", trustKeyStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", trustKeyStorePassword);
}
- This is the part where the client calls the server's API to RestTemplate.
String serverUrl = "https://localhost:8443/api/abcd/";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);
- I got a exception from client
javax.net.ssl|DEBUG|21|scheduling-1|2021-10-01 13:32:25.456 KST|Alert.java:238|Received alert message (
"Alert": {
"level" : "fatal",
"description": "bad_certificate"
}
)
javax.net.ssl|ERROR|21|scheduling-1|2021-10-01 13:32:25.457 KST|TransportContext.java:341|Fatal (BAD_CERTIFICATE): Received fatal alert: bad_certificate (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:336)
at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:185)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1429)
at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1396)
at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:985)
at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:754)
at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1615)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1520)
at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:334)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:64)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:807)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468)
at com.penta.transmitter.scheduler.SendingScheduler.sendToEdge(SendingScheduler.java:110)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)}
)
javax.net.ssl|ALL|21|scheduling-1|2021-10-01 13:32:25.457 KST|SSLSessionImpl.java:784|Invalidated session: Session(1633062745301|TLS_AES_128_GCM_SHA256)
javax.net.ssl|DEBUG|21|scheduling-1|2021-10-01 13:32:25.457 KST|SSLSocketImpl.java:1656|close the underlying socket
javax.net.ssl|DEBUG|21|scheduling-1|2021-10-01 13:32:25.457 KST|SSLSocketImpl.java:1675|close the SSL connection (initiative)
javax.net.ssl|WARNING|21|scheduling-1|2021-10-01 13:32:25.458 KST|SSLSocketImpl.java:1573|handling exception (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:336)
at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:185)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1429)
at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1396)
at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:985)
at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:754)
at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1615)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1520)
at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:334)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:64)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:807)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468)
at com.penta.transmitter.scheduler.SendingScheduler.sendToEdge(SendingScheduler.java:110)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)}
)
- and this is server's exception
2021-10-01 13:32:25.328 DEBUG 26886 --- [nio-8443-exec-5] o.a.tomcat.util.net.SecureNioChannel : Handshake failed during wrap
javax.net.ssl.SSLHandshakeException: Empty client certificate chain
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[na:na]
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:336) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:292) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:283) ~[na:na]
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1194) ~[na:na]
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1181) ~[na:na]
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) ~[na:na]
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) ~[na:na]
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074) ~[na:na]
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061) ~[na:na]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008) ~[na:na]
at org.apache.tomcat.util.net.SecureNioChannel.tasks(SecureNioChannel.java:429) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.net.SecureNioChannel.handshakeUnwrap(SecureNioChannel.java:493) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.net.SecureNioChannel.handshake(SecureNioChannel.java:217) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1702) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.53.jar:9.0.53]
at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
I was able to see answer below and fix this problem.By the way I wrote as follows.I hope to be helpful to people who are experiencing the same problem
@Bean
public RestTemplate restTemplate() throws Exception {
SSLContext sslContext = new SSLContextBuilder()
.loadKeyMaterial(keyStorePath, keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.loadTrustMaterial(trustKeyStorePath, trustKeyStorePassword.toCharArray())
.build();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom()
.setSSLHostnameVerifier((hostname, session)->true)
.setSSLSocketFactory(socketFactory)
.build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
CodePudding user response:
I think creating RestTemplate
with new keyword will not send the certificate to the server. Instead, you should wrap the SSL certificate in the Rest template. Please try this:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
char[] password = "password".toCharArray();
SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(keyStore("classpath:cert.jks", password), password)
.loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
HttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
return builder
.requestFactory(new HttpComponentsClientHttpRequestFactory(client))
.build();
}
private KeyStore keyStore(String file, char[] password) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
File key = ResourceUtils.getFile(file);
try (InputStream in = new FileInputStream(key)) {
keyStore.load(in, password);
}
return keyStore;
}