java - 我该如何解决 [收到致命警报 : bad_certificate]?

标签 java spring-boot ssl keytool mutual-authentication

我已经在本地创建了两台服务器,并且我将对它们的通信应用相互身份验证。
我只是不知道问题是什么。我对这种机制缺乏了解,但我对服务器本身也缺乏了解。

  • 创建每个 keystore
  • 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"
    
  • 导出到 X.509 以在 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
    
  • 在信任库中注册彼此的证书。
  • 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
    
  • 服务器的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
    
  • 客户端的应用程序.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
    
  • 客户端的应用程序主函数设置如下。服务器也是
  • @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);
    
    
        }
    
  • 这是客户端调用服务端API到RestTemplate的部分。
  •  String serverUrl = "https://localhost:8443/api/abcd/";
    
                RestTemplate restTemplate = new RestTemplate();
                ResponseEntity<String> response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);
    
  • 我从客户端
  • 收到异常
    
    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)}
    
    )
    
  • 这是服务器的异常
  • 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]
    
    
    我能够在下面看到答案并解决了这个问题。通过我写的方式如下。我希望对遇到同样问题的人有所帮助
        @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);
    
        }
    

    最佳答案

    我认为创建 RestTemplate使用 new 关键字不会将证书发送到服务器。相反,您应该将 SSL 证书包装在 Rest 模板中。请试试这个:

    @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;
    }
    

    关于java - 我该如何解决 [收到致命警报 : bad_certificate]?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69400914/

    相关文章:

    java - MockIntegrationMessage 在缓存上下文时不起作用

    spring - 当事件配置文件为 "test"时使用嵌入式 MongoDB

    php - 通过 SSL 通过 PHP PDO 连接到 MSSQL 服务器

    ssl - 无论如何通过命令行为openssl cert指定basicConstraints

    java - 如果线程之间没有 Java 中的公共(public)数据,如何指定线程之间的通知

    java - 使用 GridBagLayout 使组件跨多行

    java - 如何检查点击 selenium 中的按钮时是否出现 facebook 弹出窗口?

    java - NoSuchMethodError 由于 Guava jar

    ssl - ServletRequest.getAttribute ("javax.servlet.request.X509Certificate") 返回 null

    java - 需要将多维数组的行打印为列