java客户端验证https连接(忽略证书验证和证书验证两种方式)
首先根据如下操作生成证书,配置springboot https,生成一个简单的https web服务
https://www.cnblogs.com/qq931399960/p/11889349.html
验证客户端pom依赖
</dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.10</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.12</version> </dependency>
httpclient和httpcore版本要对应,否则可能会出现异常
验证方式包括跳过证书验证,也即是添加信任,就像浏览器访问自签名https服务时,页面会给出提示“您的链接不是私密连接”,点击了高级,继续前往即是对该服务添加了信任,可以继续访问该网站服务,另外一种方式就是通过服务器证书来验证,下面就直接上代码
跳过证书验证方式
package com.demo.bootdemo; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class SkipVerifyRestTemplateBuilder { private Logger logger = LoggerFactory.getLogger(SkipVerifyRestTemplateBuilder.class); // 初始化ssl resttemplate @Bean("skipVerifyRestTemplate") public RestTemplate skipVerifyRestTemplate() { RestTemplate rest = new RestTemplate(); SSLConnectionSocketFactory buildSSLSocketFactory = null; try { buildSSLSocketFactory = this.buildSSLSocketFactory(); } catch (Exception e) { logger.error("", e); } HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory( HttpClients.custom().setSSLSocketFactory(buildSSLSocketFactory).build()); factory.setConnectionRequestTimeout(1000); factory.setConnectTimeout(1000); rest.setRequestFactory(factory); return rest; } private SSLConnectionSocketFactory buildSSLSocketFactory() throws Exception { SSLContext sslContext = SSLContext.getInstance("SSL"); // 设置信任证书(绕过TrustStore验证) sslContext.init(null, new TrustManager[] { new AuthX509TrustManager() }, null); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1" }, null, new HostnameVerifier() { // hostname,默认返回true,不验证hostname @Override public boolean verify(String urlHostName, SSLSession session) { return true; } }); return sslConnectionSocketFactory; } private class AuthX509TrustManager implements TrustManager, X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkServerTrusted(X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } } }
第二种跳过证书验证方式
package com.demo.bootdemo; import java.io.IOException; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.springframework.context.annotation.Bean; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class SecondSkipVerifyRestTemplateBuilder { @Bean("secondSkipRestTemplate") public RestTemplate verifyCaRestTemplate() { RestTemplate rest = new RestTemplate(); SSLConnectionSocketFactory ssLSocketFactory = null; try { ssLSocketFactory = sslFactory("PKCS12", "abc123"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory( HttpClients.custom().setSSLSocketFactory(ssLSocketFactory).build()); // 设置传递数据超时时长 httpRequestFactory.setReadTimeout(1000); rest.setRequestFactory(httpRequestFactory); // 如果返回的数据非json则可能需要添加对应httpmessageconverter // Jaxb2RootElementHttpMessageConverter converter = new // Jaxb2RootElementHttpMessageConverter(); // // List<MediaType> mediaTypeList = new ArrayList<>(); // mediaTypeList.addAll(converter.getSupportedMediaTypes()); // mediaTypeList.add(MediaType.TEXT_HTML); // converter.setSupportedMediaTypes(mediaTypeList); // // List<HttpMessageConverter<?>> list = new ArrayList<>(); // list.add(converter); // rest.setMessageConverters(list); return rest; } public SSLConnectionSocketFactory sslFactory(String keyStoreType, String keyPassword) { SSLConnectionSocketFactory sslConnectionSocketFactory = null; try { SSLContext sslcontext = SSLContexts.custom() // //忽略掉对服务器端证书的校验 .loadTrustMaterial(new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); } catch (Exception e) { e.printStackTrace(); } return sslConnectionSocketFactory; } }
根据证书验证
package com.demo.bootdemo; import java.io.IOException; import java.security.KeyStore; import java.util.ArrayList; import java.util.List; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.core.io.Resource; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class VerifyCaRestTemplateBuilder { private Logger logger = LoggerFactory.getLogger(VerifyCaRestTemplateBuilder.class); @Value("classpath:cert.p12") private Resource certFile; @Bean("verifyCaRestTemplate") public RestTemplate verifyCaRestTemplate() { RestTemplate rest = new RestTemplate(); SSLConnectionSocketFactory ssLSocketFactory = null; try { ssLSocketFactory = sslFactory("PKCS12", "abc123"); } catch (Exception e) { logger.error("", e); } HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory( HttpClients.custom().setSSLSocketFactory(ssLSocketFactory).build()); // 设置传递数据超时时长 httpRequestFactory.setReadTimeout(1000); rest.setRequestFactory(httpRequestFactory); // 如果返回的数据非json则可能需要添加对应httpmessageconverter // Jaxb2RootElementHttpMessageConverter converter = new // Jaxb2RootElementHttpMessageConverter(); // // List<MediaType> mediaTypeList = new ArrayList<>(); // mediaTypeList.addAll(converter.getSupportedMediaTypes()); // mediaTypeList.add(MediaType.TEXT_HTML); // converter.setSupportedMediaTypes(mediaTypeList); // // List<HttpMessageConverter<?>> list = new ArrayList<>(); // list.add(converter); // rest.setMessageConverters(list); return rest; } public SSLConnectionSocketFactory sslFactory(String keyStoreType, String keyPassword) { SSLConnectionSocketFactory sslConnectionSocketFactory = null; try { KeyStore keyStore = null; try { keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(certFile.getInputStream(), keyPassword.toCharArray()); } catch (IOException e) { logger.error("", e); } HostnameVerifier hv = new HostnameVerifier() { @Override public boolean verify(String urlHostName, SSLSession session) { // 如果需要验证https域名,可以在该处做判断,如果访问的hostname与判断不一致,则会出现如下异常 // if("localhost".equals(urlHostName)) { // return true; // }else { // return false; // } // 此处不校验hostname,接收所有hostname,只是用于测试。 return true; } }; SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(certFile.getFile(), keyPassword.toCharArray(), new TrustSelfSignedStrategy()) .loadKeyMaterial(keyStore, keyPassword.toCharArray()).build(); sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, hv); } catch (Exception e) { e.printStackTrace(); } return sslConnectionSocketFactory; } }
注:在上述HostnameVerifier 中,可以验证hostname有效性,如果无效,返回fase,则会出现类似以下异常
javax.net.ssl.SSLPeerUnverifiedException: Certificate for <localhost> doesn't match any of the subject alternative names: []
测试controller
package com.demo.bootdemo; import javax.annotation.Resource; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; @Controller public class HttpsSendController { @Resource(name = "skipVerifyRestTemplate") private RestTemplate skipVerifyRestTemplate; @Resource(name = "verifyCaRestTemplate") private RestTemplate verifyCaRestTemplate; @Resource(name = "secondSkipRestTemplate") private RestTemplate secondSkipRestTemplate; @RequestMapping("/skip") @ResponseBody public String skipVerifyCert() { ResponseEntity<String> forEntity = skipVerifyRestTemplate.getForEntity("https://127.0.0.1:8443/test", String.class, new Object[] {}); return forEntity.getBody(); } @RequestMapping("/secondskip") @ResponseBody public String secondSkipVerifyCert() { ResponseEntity<String> forEntity = skipVerifyRestTemplate.getForEntity("https://127.0.0.1:8443/test", String.class, new Object[] {}); return forEntity.getBody(); } @RequestMapping("/verify") @ResponseBody public String verifyCert() { ResponseEntity<String> forEntity = verifyCaRestTemplate.getForEntity("https://127.0.0.1:8443/test", String.class, new Object[] {}); return forEntity.getBody(); } }
可分别访问当前客户端的skip、secondskip、verify验证结果