如何编写在它们之间使用相互ssl身份validation的java客户端和服务器应用程序?

我正在构建两个必须使用SSL双向身份validation进行通信的java应用程序,我使用此处的指令来创建客户端和服务器证书。
然后我在服务器应用程序中构建了一个webService,如下所示:

@RequestMapping(value="/testssl", method = RequestMethod.POST, produces="application/json", consumes="application/json") @ResponseBody public String testSSl(@RequestBody String name) { return = successfully tested; } 

我编辑了tomcat server.xml文件以启用SSL,如下所示:

  

并将server.jks放在Tomcat / Jenkins-Tomcat / keystore / server.jks中
然后我使用spring RestTemplate构建客户端应用程序以通过SSL调用此Web服务,如下所示:

 Properties props = new Properties(); props.put("javax.net.ssl.trustStore", "D:\\test\\server.jks"); props.put("javax.net.ssl.trustStoreType", "jks"); props.put("javax.net.ssl.trustStorePassword", "server123"); props.put("javax.net.ssl.keyStore", "D:\\test\\client.jks"); props.put("javax.net.ssl.keyStoreType", "jks"); props.put("javax.net.ssl.keyStorePassword", "client123"); props.put("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); props.put("ws.ssl.loose.disableHostnameVerification", "true"); System.setProperties(props); SSLContext sslcontext = SSLContexts.createDefault(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); CloseableHttpClient httpClient = HttpClientBuilder.create() .setSSLSocketFactory(sslsf).build(); org.springframework.http.client.HttpComponentsClientHttpRequestFactory cc = new HttpComponentsClientHttpRequestFactory(httpClient); RestTemplate rest = new RestTemplate(cc); String resp = rest.postForObject("https://10.141.0.77:8443/Monim/testssl", "monim", String.class); System.err.println(resp); 

但是当我运行这个应用程序时,我unable to find valid certification path to requested targetexception。 我试图再次创建证书但错误仍然存​​在。 当我从firefox浏览器请求url时,我得到An error occurred during a connection to 10.141.0.77:8443. SSL peer cannot verify your certificate. (Error code: ssl_error_bad_cert_alert) An error occurred during a connection to 10.141.0.77:8443. SSL peer cannot verify your certificate. (Error code: ssl_error_bad_cert_alert) An error occurred during a connection to 10.141.0.77:8443. SSL peer cannot verify your certificate. (Error code: ssl_error_bad_cert_alert)当使用chrome访问我得到的url时

 Error code: ERR_BAD_SSL_CLIENT_AUTH_CERT 

这是客户端应用程序的堆栈跟踪

 org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://10.141.0.77:8443/Monim/testssl":sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:503) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:452) at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:302) at com.ebs.csh.sva.handler.SOAPMessage.SendingSoapPostMsg(SOAPMessage.java:135) at com.ebs.csh.sva.handler.SVAHandler.creditSVA(SVAHandler.java:136) at com.ebs.csh.sva.services.SVAService.creditSVA(SVAService.java:73) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.ebs.commons.services.CommonService.processRequest(CommonService.java:276) at com.ebs.csh.commons.services.CSHService.processRequest(CSHService.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy16.processRequest(Unknown Source) at com.ebs.commons.webServices.CommonWebService.processRequest(CommonWebService.java:80) at com.ebs.csh.commons.webServices.CSHWebService.processRequest(CSHWebService.java:63) at com.ebs.csh.sva.webServices.SVAWebService.creditSVA(SVAWebService.java:59) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838) at javax.servlet.http.HttpServlet.service(HttpServlet.java:641) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:306) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:541) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:383) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:166) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:288) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:722) Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1836) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1337) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:154) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868) at sun.security.ssl.Handshaker.process_record(Handshaker.java:804) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:966) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1262) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1289) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1273) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:261) at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:118) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:314) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:357) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:218) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:194) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:85) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57) at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:88) at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46) at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:49) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:488) ... 60 more Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1319) ... 82 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ... 88 more 

如何解决此错误。 谢谢

花了很长时间才完成这项工作后,解决方法如下:
首先在使用System.put("key", "value")设置属性时System.put("key", "value")我注意到经过多次尝试后它没有设置它不会出现在ssl日志中KeyStore值,也尝试将属性设置为参数tomcat配置使用-Djavax.net.ssl.keyStore=""这也-Djavax.net.ssl.keyStore=""
所以我从InputStream读取KeyStore ,如下所示:

  KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream io = new FileInputStream("path/to/jks/file.jks"); try{ keystore.load(io, "pass".toCharArray()); } finally { io.close(); } 

并使用相同的先前代码来读取TrustStore如:

  KeyStore trustStore= KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream stream = new FileInputStream("path/to/truststore/file.jks"); try{ keystore.load(stream, "trusted".toCharArray()); } finally { stream.close(); } 

之后定义SSLContext

 SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keystore, "keyPassword".toCharArray()) .loadTrustMaterial(truststore, new TrustSelfSignedStrategy()) .build(); 

loadKeyMaterial用于加载keyStore它将privateKey "keyPassword"的密码作为参数,所以你必须使用密码保护私钥,当我尝试使用未加密的“加密”的普通私钥时,我得到exception,说: java.security.UnrecoverableKeyException: Cannot recover key ,默认情况下,这与密钥库密码相同,除非您更改密码。
然后创建SSLConnectionSocketFactory并将其作为参数传递给HttpClient ..这对我有用:)

对于任何想要实现双向互认的人来说,以下代码对我来说非常合适。

  FileInputStream keystoreInputStream = null; FileInputStream truststoreInputStream = null; try { KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystoreInputStream = new FileInputStream("C:\\Users\\Aman\\STSProj\\AppSecurity\\clientkeystore.p12"); keystore.load(keystoreInputStream, "client".toCharArray()); keystoreInputStream.close(); KeyStore trustore = KeyStore.getInstance(KeyStore.getDefaultType()); truststoreInputStream = new FileInputStream("C:\\Users\\Aman\\STSProj\\AppSecurity\\clienttruststore.p12"); trustore.load(truststoreInputStream, "client".toCharArray()); SSLContext sslcontext = SSLContexts.custom().useProtocol("TLS") .loadKeyMaterial(keystore, "client".toCharArray()) .loadTrustMaterial(trustore, null).build(); HostnameVerifier hostnameverifier = null; SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslcontext, null, null, hostnameverifier); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build(); HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); RestTemplate restTemp = new RestTemplate(requestFactory); String greetings = restTemp .getForObject("https://localhost:8443/SecureAppServer/test/security/hello/aman123", String.class); System.out.println("Received greetings from secured server ---> " + greetings); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (keystoreInputStream != null) { try { keystoreInputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (truststoreInputStream != null) { try { truststoreInputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }