Java SSL/TLS证书认证逻辑

前言

最近做项目使用httpclient转发https请求,但是遇到一些坑,尤其是证书的认证,证书认证一般都是单向的,除非相互访问,证书一般基于host,但是如果访问需要ip,那么JDK默认的认证就会不通过,但是SSL的握手只要jdk信任证书就可以通过认证。

demo

还是上2章的zuul 的demo,Apache httpclient 4.5.13

bash 复制代码
## 配置zuul的路由规则
zuul:
  routes:
    rule1:
      path: /demo/**
      url: https://202.89.233.101:443/

url配置IP,实际上应该配置host,但是host需要DNS的解析,并不是所有情况DNS都能解析的。

IP证书认证不过

这里使用cn.bing.com的IP来实验,通过ping获取ip,SSL默认的端口为443,访问http://localhost:8080/demo/

bash 复制代码
Caused by: javax.net.ssl.SSLPeerUnverifiedException: Certificate for <202.89.233.101> doesn't match any of the subject alternative names: [*.platform.bing.com, *.bing.com, bing.com, ieonline.microsoft.com, *.windowssearch.com, cn.ieonline.microsoft.com, *.origin.bing.com, *.mm.bing.net, *.api.bing.com, *.cn.bing.net, *.cn.bing.com, ssl-api.bing.com, ssl-api.bing.net, *.api.bing.net, *.bingapis.com, bingsandbox.com, feedback.microsoft.com, insertmedia.bing.office.net, r.bat.bing.com, *.r.bat.bing.com, *.dict.bing.com, *.ssl.bing.com, *.appex.bing.com, *.platform.cn.bing.com, wp.m.bing.com, *.m.bing.com, global.bing.com, windowssearch.com, search.msn.com, *.bingsandbox.com, *.api.tiles.ditu.live.com, *.ditu.live.com, *.t0.tiles.ditu.live.com, *.t1.tiles.ditu.live.com, *.t2.tiles.ditu.live.com, *.t3.tiles.ditu.live.com, *.tiles.ditu.live.com, 3d.live.com, api.search.live.com, beta.search.live.com, cnweb.search.live.com, dev.live.com, ditu.live.com, farecast.live.com, image.live.com, images.live.com, local.live.com.au, localsearch.live.com, ls4d.search.live.com, mail.live.com, mapindia.live.com, local.live.com, maps.live.com, maps.live.com.au, mindia.live.com, news.live.com, origin.cnweb.search.live.com, preview.local.live.com, search.live.com, test.maps.live.com, video.live.com, videos.live.com, virtualearth.live.com, wap.live.com, webmaster.live.com, www.local.live.com.au, www.maps.live.com.au, webmasters.live.com, ecn.dev.virtualearth.net, www.bing.com]
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:507)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:437)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384)
	at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
	at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
	at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:118)
	at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.forwardRequest(SimpleHostRoutingFilter.java:392)
	at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.forward(SimpleHostRoutingFilter.java:311)
	at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.run(SimpleHostRoutingFilter.java:227)

报错了,因为证书是host,使用IP访问的时候host是认证不了的

那么关键是org.apache.http.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:507)

看这个类的定义,详细的说明了,证书认证不过怎么处理,导入自签证书或者jdk没收录的证书即可实现认证

看定义SSLv2,要制定协议版本TLS1.2 TLS1.3(extend),支持的加密套件,比如国密就不支持,需要额外的jar。

那么zuul的证书验证报错原理是默认情况下:org.apache.http.conn.ssl.DefaultHostnameVerifier

java 复制代码
    public boolean verify(final String host, final SSLSession session) {
        try {
            final Certificate[] certs = session.getPeerCertificates();
            final X509Certificate x509 = (X509Certificate) certs[0];
            verify(host, x509);
            return true;
        } catch (final SSLException ex) {
            if (log.isDebugEnabled()) {
                log.debug(ex.getMessage(), ex);
            }
            return false;
        }
    }

读取host(zuul的配置转发的url读取,这里配置了IP),获取证书链,获取链尾部的证书(尾部即第一个证书,根证书是CA的认证逻辑) ,判断证书的host是否相同,实际上也可以用IP作为证书的host

java 复制代码
    public void verify(
            final String host, final X509Certificate cert) throws SSLException {
        final HostNameType hostType = determineHostFormat(host);
        final List<SubjectName> subjectAlts = getSubjectAltNames(cert);
        if (subjectAlts != null && !subjectAlts.isEmpty()) {
            switch (hostType) {
                case IPv4:
                    matchIPAddress(host, subjectAlts);
                    break;
                case IPv6:
                    matchIPv6Address(host, subjectAlts);
                    break;
                default:
                    matchDNSName(host, subjectAlts, this.publicSuffixMatcher);
            }
        } else {
            // CN matching has been deprecated by rfc2818 and can be used
            // as fallback only when no subjectAlts are available
            final X500Principal subjectPrincipal = cert.getSubjectX500Principal();
            final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253));
            if (cn == null) {
                throw new SSLException("Certificate subject for <" + host + "> doesn't contain " +
                        "a common name and does not have alternative names");
            }
            matchCN(host, cn, this.publicSuffixMatcher);
        }
    }

获取host的过程,以上面的bing证书为例,JDK读取后会区分是DNS(type=2)还是IP(type=7)

java 复制代码
   static List<SubjectName> getSubjectAltNames(final X509Certificate cert) {
        try {
            final Collection<List<?>> entries = cert.getSubjectAlternativeNames();
            if (entries == null) {
                return Collections.emptyList();
            }
            final List<SubjectName> result = new ArrayList<SubjectName>();
            for (final List<?> entry : entries) {
                final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null;
                if (type != null) {
                    if (type == SubjectName.DNS || type == SubjectName.IP) {
                        final Object o = entry.get(1);
                        if (o instanceof String) {
                            result.add(new SubjectName((String) o, type));
                        } else if (o instanceof byte[]) {
                            // TODO ASN.1 DER encoded form
                        }
                    }
                }
            }
            return result;
        } catch (final CertificateParsingException ignore) {
            return Collections.emptyList();
        }
    }

笔者怀疑是jdk的问题,毕竟jdk的证书与操作系统不通用,但是go语言也是同样的逻辑,也是解析为IP和host,然后验证。

可以看到先把host判断类型,IPv4、IPv6、DNS(默认) ,比如bing的证书,这定义了很多DNS

导出证书方式,以Chrome 浏览器访问bing为例

先点击锁🔐图标,然后点击连接是安全的,也可能不安全,也是点击这一行

导出,选择Base64格式,其他的格式也可以,实际上JDK也可以支持,选择每一层证书可以看证书的信息

证书链,是从根证书开始的过程,如果是自签证书一般自己就是根证书,根证书一般是CA颁发用来签名的。

比如bing的根证书,操作系统就有认证

比如bing就是www.digicert.com 颁发的根证书,认证的,根证书有公钥和hash签名,根证书的私钥只有证书机构自己有,根证书是操作系统或浏览器信任的,所以根证书颁发的签名证书是信任的。证书认证的过程就是把自己的证书加入根证书的链中。

证书签名是使用私钥加密hash签名,使用SSL加密传输,对方使用公钥解密,然后使用自己的hash签名和解密的hash比对,相同表示没有篡改,否则都是已经篡改。

解决IP认证证书是host的情况

那么怎么解决呢,

  1. 使用host转发,host转发不涉及证书验证host的问题
  2. 使用IP的证书,不推荐,IP很可能经常变化,域名是相对长期固定的,证书也是长期的
  3. 使用证书host配置验证,zuul以这个为例

以zuul为例,实际上就是host的传递问题,验证证书时,zuul通过url解析,配置host域名转发即可,如果一定要配置IP,那么在org.springframework.cloud.netflix.zuul.filters.ZuulProperties

复制代码
ZuulRoute

中增加dns的字段,配置各个url正确的host后,然后通过threadlocal传递到转发逻辑,这样写的比较死,实际上可以在创建连接的工厂里,通过开关忽略host校验

java 复制代码
public class ZuulApacheHttpClientConnectionManagerFactory
        implements ApacheHttpClientConnectionManagerFactory {

    private static final Log LOG = LogFactory
            .getLog(ZuulApacheHttpClientConnectionManagerFactory.class);
    
    private boolean ignoreHostVerify;

    public boolean isIgnoreHostVerify() {
        return ignoreHostVerify;
    }

    public void setIgnoreHostVerify(boolean ignoreHostVerify) {
        this.ignoreHostVerify = ignoreHostVerify;
    }

    public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
                                                            int maxTotalConnections, int maxConnectionsPerRoute) {
        return newConnectionManager(disableSslValidation, maxTotalConnections,
                maxConnectionsPerRoute, -1, TimeUnit.MILLISECONDS, null);
    }

    @Override
    public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
                                                            int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive,
                                                            TimeUnit timeUnit, RegistryBuilder registryBuilder) {
        if (registryBuilder == null) {
            registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register(HTTP_SCHEME, PlainConnectionSocketFactory.INSTANCE);
        }
        if (disableSslValidation) {
            try {
                final SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null,
                        new TrustManager[] { new DisabledValidationTrustManager() },
                        new SecureRandom());
                registryBuilder.register(HTTPS_SCHEME, new SSLConnectionSocketFactory(
                        sslContext, NoopHostnameVerifier.INSTANCE));
            }
            catch (NoSuchAlgorithmException e) {
                LOG.warn("Error creating SSLContext", e);
            }
            catch (KeyManagementException e) {
                LOG.warn("Error creating SSLContext", e);
            }
        }
        else {
            if (ignoreHostVerify) {
                try {
                    SSLContext sslContext = SSLContext.getInstance("TLS");
                    sslContext.init(null, null, new SecureRandom());
                    registryBuilder.register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE));
                } catch (KeyManagementException | NoSuchAlgorithmException e) {
                    throw new RuntimeException(e);
                }
            } else {
                registryBuilder.register("https",
                        SSLConnectionSocketFactory.getSocketFactory());
            }
        }
        final Registry<ConnectionSocketFactory> registry = registryBuilder.build();

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
                registry, null, null, null, timeToLive, timeUnit);
        connectionManager.setMaxTotal(maxTotalConnections);
        connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);

        return connectionManager;
    }

    class DisabledValidationTrustManager implements X509TrustManager {

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

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

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

    }

}

加上配置

java 复制代码
@Configuration
public class DemoConfiguration {
    
    @Bean
    @ConditionalOnProperty(name = "zuul.ssl.ignore.host", havingValue = "true", matchIfMissing = false)
    public ApacheHttpClientConnectionManagerFactory initApacheHttpClientConnectionManagerFactory(){
        ZuulApacheHttpClientConnectionManagerFactory clientConnectionManagerFactory = new ZuulApacheHttpClientConnectionManagerFactory();
        clientConnectionManagerFactory.setIgnoreHostVerify(true);
        return clientConnectionManagerFactory;
    }
}

加上开关后

笔者尝试后结果如下(修改了端口,笔者开了几个demo):

说明访问成功,只是浏览器限制了我们访问

自建证书试验

那么根证书是没有认证的呢,或者没有根证书,就是我们自己通过openssl或者jdk的工具做的呢,先做一个证书试试,macOS内置了openssl,实际上很多Linux也内置了,不然需要安装

而且支持国密算法,JDK需要额外的套件jar支持,笔者就用国际算法吧,通用性强,先使用rsa创建私钥,这里不考虑安全性,就用默认算法长度

openssl genpkey -algorithm RSA -out private.key

然后创建签名请求

openssl req -new -key private.key -out demo.csr

然后生成公钥证书(公钥就是要给出去的)

openssl x509 -req -in demo.csr -signkey private.key -out server.crt

也可以查看证书信息

openssl x509 -in server.crt -text -noout

证书配置tomcat(包括嵌入式),nginx,或者ssl服务器即可,demo tomcat、nginx官网很详细

直接报错

java 复制代码
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

解决办法

目前通用的办法有2种,让JDK识别信任根证书;忽略认证证书的过程

证书认证过程分析

证书认证在ssl握手的时候,即socket(Linux sock)连接上后就会client hello和server hello,这个时候会传递证书,当然TLS1.2和TLS1.3会有些不同,但是证书的传递是必须的,即服务器公钥传递

SpringCloud使用httpclient的时候,在org.apache.http.conn.ssl.SSLConnectionSocketFactory发起socket的握手

复制代码
sslsock.startHandshake();

而JDK使用JCE和JSSE的jar来完成SSL的通信,认证过程在JSSE的jar,里面有判断是否TLS1.3,然后使用sun.security.ssl.SSLHandshake,来发送client hello

发送时协商tls版本和加密套件,会发送随机数,用于预主密钥的生成

发送client hello的信息

然后server发送serverhello回执,协商加密套件,服务端随机数,证书等

serverhello

keyexchange

进行证书认证

比如微软的证书,证书链和密钥交换算法

调用sun.security.validator.PKIXValidator,对证书链进行校验,和JDK自己内置的证书逐一对比,读取根证书

比如demo这里就从jdk读取到了根证书

然后sun.security.validator.Validator,检查host证书

加入JDK信任

实际上根据JDK的证书认证原理,只需要把JDK没有内置的根证书加入信任,或者把那个证书加入信任即可,建议信任那个证书,而非根证书

可以使用keytool工具导入,这位博主写的这个很好,直接用openssl工具导出证书,很多教程都是通过浏览器导出证书,这个在很多生产环境是不现实的。

将所访问的SSL站点证书添加至JVM。

echo -n |openssl s_client -connect 182.242.198.40:5000|sed -ne'/BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > xxxxx.cert

此命令获取服务端证书链。

keytool -importcert -alias 182.242.198.40-1 -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -file xxxxx.cert


版权声明:本文为CSDN博主「呆呆鸟哈密瓜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_37084673/article/details/108597947

笔者使用这个实际上也跳进坑里了,并不是命令的坑,而是JAVA_HOME,命令执行的JAVA_HOME和实际程序运行的JAVA_HOME未必是同一个,这个一定要在ps aux|grep java后根据实际执行的进程的JAVA_HOME执行命令

忽略认证

忽略信任这个实际上SpringCloud官方就已经支持,就是自己写一个信任manager类,然后里面什么都不做

然后载入sslcontext,这样每次请求就不去信任证书了,安全等级会降低,可以安装zuul的请求放开某一个url的信任,这样可以精确限制,对于SpringCloud只需要配置disableSslValiadation即可

JAVA程序自动加入信任

使用代码加载,最开始源代码来源于InstallCert,不过我改了一下

java 复制代码
package com.feng.zuul.demo.connection;

/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class InstallCert {

    // 我们要访问的HTTPS服务,如访问 https://www.bing.com
    public static final String hostName = "cn.bing.com";

    private static final char SEP = File.separatorChar;

    public static void main(String[] args) throws Exception {
        String host = hostName;
        int port = 443;
        char[] passphrase = "changeit".toCharArray();

        File file = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security" + SEP + "cacerts");
        if (!file.exists() || !file.isFile()) {
            System.out.println("jre lib security cacerts is not existed or not file in jdk");
            return;
        }

        System.out.println("Loading KeyStore " + file + "...");
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        try (InputStream in = Files.newInputStream(file.toPath())) {
            ks.load(in, passphrase);
        }

        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory factory = context.getSocketFactory();

        System.out.println("Opening connection to " + host + ":" + port + "...");
        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
            //当然不可能执行这个,因为我们的证书校验getAcceptedIssuers定义直接抛异常了
            return;
        } catch (SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        } finally {
            socket.close();
        }

        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();

        for (X509Certificate cert : chain) {
            Collection<List<?>> nativeNames = cert.getSubjectAlternativeNames();
            if (nativeNames == null || nativeNames.isEmpty()) continue;
            String alias = host + "-" + cert.getSerialNumber();
            ks.setCertificateEntry(alias, cert);
            System.out.println();
            System.out.println(cert);
            System.out.println();
            System.out.println("Added certificate to keystore 'jssecacerts' using alias '" + alias + "'");
        }

        try (OutputStream out = Files.newOutputStream(file.toPath());) {
            ks.store(out, passphrase);
            System.out.println("Added all certificate to keystore");
        }
    }

    private static class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;
        private X509Certificate[] chain;

        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }

        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            throw new UnsupportedOperationException();
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
    }

}

这个加载的证书,可以被JDK初始化载入,那么信任的证书必须重启应用才能生效,那么用什么办法不重启呢。方法来源于org.springframework.cloud.configuration.SSLContextFactory,还是Spring实现的,照搬过来即可,不过Spring是静态文件加载,我们可以动态代码加载

HTTPS是sslcontext去发起的,那么把证书动态给创建这个上下文即可

java 复制代码
	public SSLContext createSSLContext() throws GeneralSecurityException, IOException {
		SSLContextBuilder builder = new SSLContextBuilder();
		char[] keyPassword = properties.keyPassword();
		KeyStore keyStore = createKeyStore();

		try {
            // 载入文件
			builder.loadKeyMaterial(keyStore, keyPassword);
		}
		catch (UnrecoverableKeyException e) {
			if (keyPassword.length == 0) {
				// Retry if empty password, see
				// https://rt.openssl.org/Ticket/Display.html?id=1497&user=guest&pass=guest
				builder.loadKeyMaterial(keyStore, new char[] { '\0' });
			}
			else {
				throw e;
			}
		}

		KeyStore trust = createTrustStore();
		if (trust != null) {
            // 加入信任
			builder.loadTrustMaterial(trust, null);
		}

		return builder.build();
	}

改造一下:在自定义connectionmanager的bean增加信任证书加载的逻辑,把InstallCert的证书传过来,当然可以在初始化或者动态实时处理

比如应用启动初始化

java 复制代码
@Configuration
public class DemoConfiguration {

    @Bean
    @ConditionalOnProperty(name = "zuul.ssl.ignore.host", havingValue = "true", matchIfMissing = false)
    public ApacheHttpClientConnectionManagerFactory initApacheHttpClientConnectionManagerFactory(){
        ZuulApacheHttpClientConnectionManagerFactory clientConnectionManagerFactory = new ZuulApacheHttpClientConnectionManagerFactory();
        clientConnectionManagerFactory.setIgnoreHostVerify(true);
        return clientConnectionManagerFactory;
    }
    @Bean
    @ConditionalOnBean(InstallCert.class)
    @ConditionalOnProperty(name = "zuul.ssl.ignore.host", havingValue = "true", matchIfMissing = false)
    public ApacheHttpClientConnectionManagerFactory initApacheHttpClientConnectionManagerFactory(InstallCert installCert){
        ZuulApacheHttpClientConnectionManagerFactory clientConnectionManagerFactory = new ZuulApacheHttpClientConnectionManagerFactory();
        clientConnectionManagerFactory.setIgnoreHostVerify(true);
        clientConnectionManagerFactory.setInstallCert(installCert);
        return clientConnectionManagerFactory;
    }

    @Bean
    public InstallCert initCert(){
        return new InstallCert();
    }

可以通过各种方式控制,来达到自动JDK不识别证书的信任,还可以根据sslcontext的创建逻辑,实现租户隔离,达到我们按想法定制的目的。

总结

实际上证书的认证就是链式认证,加入根证书链,因为根证书是信任的,CA机构是认可的,那么CA颁发的根证书是信任的,经常报道的Chrome移除xxx机构颁发的根证书,表示这些证书链下的证书不信任了,毕竟公钥和私钥任何证书都能生成,证书链也可以仿造。

在服务器的应用中,如果证书验证不通过可以加入证书认证的过程,如果是IP访问,那么host认证失败就让IP的host来验证,如果国密算法不支持就加入国密支持包,实在不想认证也可以跳过认证,浏览器一般是飘红,程序无感知,前提是需要知道安全性,实际上绝大多数内网络证书都是不被认证的,除非定制操作系统配置或者JDK等中间件配置,可根据实际情况来决定解决。

相关推荐
潘yi.17 分钟前
web技术与nginx网站环境部署
服务器·网络·nginx
安顾里41 分钟前
Linux命令-iostat
linux·运维·服务器
whoarethenext1 小时前
初始https附带c/c++源码使用curl库调用
服务器·c++·qt·https·curl
100编程朱老师1 小时前
面试:什么叫Linux多路复用 ?
linux·运维·服务器
群联云防护小杜1 小时前
云服务器主动防御策略与自动化防护(下)
运维·服务器·分布式·安全·自动化·音视频
Jtti1 小时前
Jtti:nginx服务器如何限制访问频率
服务器·网络·nginx
struggle20252 小时前
LinuxAgent开源程序是一款智能运维助手,通过接入 DeepSeek API 实现对 Linux 终端的自然语言控制,帮助用户更高效地进行系统运维工作
linux·运维·服务器·人工智能·自动化·deepseek
只可远观2 小时前
Flutter 泛型 泛型方法 泛型类 泛型接口
服务器·windows·flutter
帽儿山的枪手3 小时前
socket套接字你搞清楚了吗
网络协议·面试
白山云北诗4 小时前
什么是 DDoS 攻击?高防 IP 如何有效防护?2025全面解析与方案推荐
网络协议·tcp/ip·ddos·高防ip·ddos攻击怎么防·高防ip是什么