keytool 生成证书(包括支持指定IP)+Springboot支持https + RestTemplate忽略访问https的证书方式

最近几天弄Springboot的https的restful访问,期间遇到一系列的问题记录如下:

 1.springboot项目端口不能定义为 6666等特殊端口

     本来想选个酷炫的6666端口启动,结果启动是没有问题,但是通过浏览器访问就没有反应,这个问题后来查询是因为浏览器对一些特殊端口有做限制。具体哪些端口这儿就不记录了,反正6666是不行的。

 2.keytool生成证书及tomcat中设置SSL

2.1、Keytool介绍 


    Keytool是一个Java数据证书的管理工具。Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里,包含两种数据: 

1.   密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密) 

2.   可信任的证书实体(trusted certificate entries)——只包含公钥 

Alias(别名):每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写 

keystore的存储位置 

在没有制定生成位置的情况下,keystore会存在与用户的系统默认目录, 如:对于window xp系统,会生成在系统的C:/Documents and Settings/UserName/ 文件名为“.keystore” 

keystore的生成:keytool -genkey -alias tomcat -keyalg RSA   -keystore d:/mykeystore -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass changeit -storepass -validity 36500 

参数说明: 

-genkey表示要创建一个新的密钥 
-dname表示密钥的Distinguished Names, 
CN=commonName 
OU=organizationUnit 
O=organizationName 
L=localityName 
S=stateName 
C=country 
Distinguished Names表明了密钥的发行者身份 
-keyalg使用加密的算法,这里是RSA 
-alias密钥的别名 
-keypass私有密钥的密码,这里设置为changeit 
-keystore 密钥保存在D:盘目录下的mykeystore文件中 
-storepass 存取密码,这里设置为changeit,这个密码提供系统从mykeystore文件中将信息取出 
-validity该密钥的有效期为 36500表示100年 (默认为90天) 

cacerts证书文件(The cacerts Certificates File) 

改证书文件存在于java.home/lib/security目录下,是Java系统的CA证书仓库 

  2.2、准备工作    


1.   验证是否已创建过同名的证书 

Window : keytool -list -v -alias tomcat -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit 

Linux : keytool -list -v -alias tomcat -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit 

2.   删除已创建的证书 

Window : keytool -delete -alias tomcat -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit 

Linux : keytool -delete -alias tomcat -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit 


 2.3、创建证书 



1.   服务器中生成证书: 

(注:生成证书时,CN要和服务器的域名相同,如果在本地测试,则使用localhost) 

Window : keytool -genkey -alias tomcat -keyalg RSA -keystore d:/my.keystore -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass changeit -storepass changeit 

Linux : keytool -genkey -alias tomcat -keyalg RSA -keystore ~/my.keystore -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass changeit -storepass changeit 

2.   导出证书,由客户端安装: 

window : keytool -export -alias tomcat -keystore d:/my.keystore -file d:/mycerts.cer -storepass changeit

Linux : keytool -export -alias tomcat -keystore ~/my.keystore -file ~/mycerts.cer -storepass changeit 

3.   客户端配置:为客户端的JVM导入密钥(将服务器下发的证书导入到JVM中) 

window : keytool -import -trustcacerts -alias tomcat -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -file d:/mycerts.cer -storepass changeit 

Linux : keytool -import -trustcacerts -alias tomcat -keystore "$JAVA_HOME/jre/lib/security/cacerts" -file ~/mycerts.cer -storepass changeit 


2.4、配置Tomcat SSL 


修改server.xml中的SSL服务 
Window : 
<Connector port="8443" maxHttpHeaderSize="8192" 
     maxThreads="150" minSpareThreads="25" maxSpareThreads="75" 
     enableLookups="false" disableUploadTimeout="true" 
     acceptCount="100" scheme="https" secure="true" 
     clientAuth="false" sslProtocol="TLS" keystoreFile="d:/my.keystore" keystorePass="changeit"/> 

Linux: 
<Connector port="8443" maxHttpHeaderSize="8192" 
     maxThreads="150" minSpareThreads="25" maxSpareThreads="75" 
     enableLookups="false" disableUploadTimeout="true" 
     acceptCount="100" scheme="https" secure="true" 
     clientAuth="false" sslProtocol="TLS" keystoreFile="~/my.keystore" keystorePass="changeit"/> 


2.5、常见问题

 

1.   未找到可信任的证书 

主要原因为在客户端未将服务器下发的证书导入到JVM中,可以用 

keytool -list -alias tomcat -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit 

来查看证书是否真的导入到JVM中。 

2.   keytool错误:java.io.IOException:keystore was tampered with,or password was incorrect 

原因是在你的home目录下是否还有.keystore存在。如果存在那么把他删除掉,后再执行 

或者删除"%JAVA_HOME%/jre/lib/security/cacerts 再执行 

建议直接删掉cacerts再导入 

Tomcat配置https及访问http自动跳转至https 
完成上述操作就可以通过https://www.xxx.com:8443 或者 http://www.xxx.com:[port]访问网站; 
第二步:配置Tomcat 
  打开$CATALINA_HOME/conf/server.xml,修改如下: 

  <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> 
修改参数=> 
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000"  redirectPort="443" /> 


<!-- 
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" 
              maxThreads="150" scheme="https" secure="true" 
              clientAuth="false" sslProtocol="TLS"/> 
--> 
去掉注释且修改参数=> 
<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true" 
               maxThreads="150" scheme="https" secure="true" 
               clientAuth="false" sslProtocol="TLS" keystoreFile="/etc/tomcat.keystore" keystorePass="changeit"/> 


<!-- 
   <Connector port="8009" enableLookups="false" protocol="AJP/1.3" redirectPort="8443" /> 
--> 
修改参数=> 
<Connector port="8009" enableLookups="false" protocol="AJP/1.3" redirectPort="443" /> 

第三步:配置tomcat的web.xml在该文件末尾增加:强制https访问 
及输入http:// 自动跳转https:// 
配置如下: 
<login-config> 
     <!-- Authorization setting for SSL --> 
     <auth-method>CLIENT-CERT</auth-method> 
     <realm-name>Client Cert Users-only Area</realm-name> 
</login-config> 
<security-constraint> 
     <!-- Authorization setting for SSL --> 
     <web-resource-collection > 
     <web-resource-name >SSL</web-resource-name> 
     <url-pattern>/*</url-pattern> 
     </web-resource-collection> 
     <user-data-constraint> 
     <transport-guarantee>CONFIDENTIAL</transport-guarantee> 
     </user-data-constraint> 
</security-constraint>

3.keytool生成证书时指定IP

  网上有些资料说,生成证书时只能输入域名,不然用另外一台电脑导入证书后,通过https访问时回报 “java.security.cert.CertificateException: No subject alternative names present”,但是根据我的实际操作后验证,可以在生成证书时设置为IP,操作如下:

 服务器端:

  keytool -genkey -alias rsso3 -keyalg RSA -keystore e:/my.keystore -dname "CN=10.15.7.173, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -ext san=ip:10.15.7.173  -keypass changeit -storepass changeit  -validity 36500

 keytool -export -alias rsso3 -keystore e:/my.keystore -file e:/mycerts.cer -storepass changeit

客户端:

keytool -import -trustcacerts -alias rsso3 -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -file w:/mycerts.cer -storepass changeit

4.Springboot支持https

 springboot工程,支持https还是很方便的,一个证书文件,一些简单ssl配置即可:

在application.properties中设置SSL参数,如下:

##(密钥文件路径,也可以配置绝对路径)
server.ssl.key-store=classpath:my.keystore
#(密钥生成时输入的密钥库口令)
server.ssl.key-store-password=changeit
#(密钥类型,与密钥生成命令一致)
#server.ssl.keyStoreType=RSA
#(密钥别名,与密钥生成命令一致)
server.ssl.keyAlias=rsso3

配置好以后,再启动springboot工程,就可以访问https服务了。

5.Springboot中利用RestTemplate访问https的restful服务,绕过证书验证

     这步有点特别,能绕过https的证书验证而能访问https接口,这个如果在生产环境会特别不友好!暂时没找到更多的一些信息。只把怎么绕过的代码先放这儿已做记录。

首先得配置RestTemplateConfig的配置文件,代码如下:

import com.barry.springbootdemo.utils.HttpClientUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate配置模板
 *
 * @author like
 */
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }

    @Bean
    public RestTemplate httpsRestTemplate(HttpComponentsClientHttpRequestFactory httpsFactory){
        RestTemplate restTemplate = new RestTemplate(httpsFactory);
        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) {
                return false;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) {
                //默认处理非200的返回,会抛异常
            }
        });
        return restTemplate;
    }

    @Bean(name = "httpsFactory")
    public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception{
        CloseableHttpClient httpClient = HttpClientUtils.acceptsUntrustedCertsHttpClient();
        HttpComponentsClientHttpRequestFactory httpsFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        httpsFactory.setReadTimeout(2000); //单位为ms
        httpsFactory.setConnectTimeout(2000); //单位为ms
        return httpsFactory;
    }
}

  然后还得弄一个设置过滤掉安全证书的工具类

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class HttpClientUtils {

    public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder b = HttpClientBuilder.create();

        // setup a Trust Strategy that allows all certificates.
        //

        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        b.setSSLContext(sslContext);

        // don't check Hostnames, either.
        //      -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // here's the special part:
        //      -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
        //      -- and create a Registry, to register it.
        //
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        // now, we create connection-manager using our Registry.
        //      -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager( socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager( connMgr);

        // finally, build the HttpClient;
        //      -- done!
        CloseableHttpClient client = b.build();

        return client;
    }

}

最后创建一个Controller调用方法测试验证:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


@RestController
public class HelloController {

    @Autowired
    private RestTemplate restTemplate;


    /**
     *
     *
     * @return
     */
    @GetMapping("/world")
    public String worldController() {

        ResponseEntity response = restTemplate.getForEntity("https://10.15.7.173:8989/rsso/index/hello2",String.class);
        HttpHeaders httpHeaders = response.getHeaders();		//响应头
        HttpStatus httpStatus = response.getStatusCode();	//响应码
        System.out.println("hello==="+response.getBody().toString());
        return "yes";
    }

}

启动springboot工程,访问这个world的restful接口,验证能绕过证书,调用到https的接口:

 

页面访问显示:

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页