springboot通过已有的server.key,crt,ca证书生成jks文件并开启HTTPS 并启动双向认证+动态加载信任库

yaml文件配置

server:
  port: 6820
  ssl:
    enabled: true
    key-store: C:\Users\Administrator\Desktop\server.jks ## 这里放到桌面,原因后面会说
    key-store-password: changeit
    key-alias: localhost
    key-store-type: JKS
    trust-store: C:\Users\Administrator\Desktop\server.jks
    trust-store-password: changeit ## 密码最好用这个
    trust-store-provider: SUN
    trust-store-typ: JKS
    client-auth: need ## 开启双向认证的必要条件

证书生成

1.通过服务端crt和key生成p12格式证书
openssl pkcs12 -export -in server.crt -inkey server.key  -out server.p12 -name localhost -CAfile ca.crt -caname root 
   
2.p12格式转为jks格式
keytool -importkeystore -destkeystore server.jks -srckeystore server.p12 -srcstoretype pkcs12 -alias localhost 

3.客户端证书导入服务端
keytool -import -alias client -file client.crt -keystore server.jks  -storepass changeit -trustcacerts

4.通过客户端crt和key生成p12格式证书
openssl pkcs12 -export -in client.crt -inkey client.key  -out client.p12 -name localhost -CAfile ca.crt -caname root

5.将服务端证书导入客户端
keytool -importcert -keystore client.p12 -alias servercert -file server.crt 
--------以下为验证部分
检验服务端是否具有自己的private key和客户端的cert
keytool -list -keystore server.jks

验证证书是否合法
CERTUTIL -urlfetch -verify client.crt

热加载信任库

  1. 服务端的jks文件不要放在resource文件下,网上很多博文都是放到这个目录,其实根本不能进行热加载,因为打包之后 是不能对resource文件夹下的文件进行修改替换的,必须放到jar包外层。
  2. 配置Tomcat,使得TrustManagerClassName可被设置,代码如下:
@Configuration
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
public class SslTomcatAutoConfiguration {

    @Autowired
    private X509TrustManager x509TrustManager;

    @Bean
    public SslWebServerFactoryCustomizer hotSslWebServerFactoryCustomizer() {
        return new SslWebServerFactoryCustomizer(x509TrustManager);
    }

    @Bean
    @ConditionalOnMissingBean
    public X509TrustManager x509TrustManager() {
        return new SslX509TrustManager();
    }
}

  1. 使用自定义{ProtocolHandler},主要是为了设置{@link AbstractHttp11Protocol#setTrustManagerClassName(String)}}
public class HttpsNioProtocol extends Http11NioProtocol {

    public HttpsNioProtocol() {
        super.setTrustManagerClassName(SslWebServerFactoryCustomizer.SslTrustManager.class.getName());
    }
}
  1. SslWebServerFactoryCustomizer
public class SslWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    private static X509TrustManager x509TrustManager;

    public SslWebServerFactoryCustomizer(X509TrustManager x509TrustManager) {
        SslWebServerFactoryCustomizer.x509TrustManager = x509TrustManager;
    }

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.setProtocol(HttpsNioProtocol.class.getName());
    }

    /**
     * 热加载受信证书管理器
     */
    public static class SslTrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            x509TrustManager.checkClientTrusted(x509Certificates, s);
        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            x509TrustManager.checkServerTrusted(x509Certificates, s);
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return x509TrustManager.getAcceptedIssuers();
        }
    }
}
  1. SslX509TrustManager
public class SslX509TrustManager implements X509TrustManager {

    @Value("${server.ssl.trust-store}")
    private String trustStore;

    @Value("${server.ssl.trust-store-password}")
    private String trustStorePassword = "";


    @Value("${server.ssl.trust-store-type:JKS}")
    private String trustStoreType;

    /**
     * @param x509Certificates 客户端传过来的证书
     * @param s                加密方式
     * @throws CertificateException
     */
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        KeyStore keyStore = KeyStoreLoader.load(trustStore, trustStorePassword, trustStoreType);
        if (ObjectUtils.isEmpty(keyStore)) {
            System.out.println("验证不通过");
            throw new CertificateException();
        }
        for (X509Certificate x509Certificate : x509Certificates) {
            try {
                if (keyStore.getCertificateAlias(x509Certificate) != null) {
                    System.out.println("验证通过");
                    return;
                }

            } catch (Exception e) {
                // pass
            }
        }
        System.out.println("验证不通过");
        try {
            storeTrust();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new CertificateException();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
        // 一般不用管,验证服务端的证书是否都有效
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

}
  1. KeyStore加载器,主要完成从文件加载
public class KeyStoreLoader {

    private KeyStoreLoader() {
    }

    private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    public static KeyStore load(String name, String password, String type) {
        File file = new File(name);
        KeyStore keyStore;
        try {
            InputStream inputStream = new FileInputStream(file);
            try {
                keyStore = KeyStore.getInstance(type);
                keyStore.load(inputStream, password.toCharArray());
                return keyStore;

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  1. 将以上代码配置好之后就不要其他操作拉。

测试

  1. 自定义接口
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/test")
    public String test(){
        return "777";
    }
}
  1. 首先未在server.jks中导入客户端证书,访问接口:
    在这里插入图片描述
    访问失败了。
  2. 通过keytool命令加入客户端证书,在进行访问:
    在这里插入图片描述

访问成功。
如果要测试删除证书是否能访问,需要重启浏览器哦。


版权声明:本文为qq_34239851原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。