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验证结果