You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Trust Manager与Hostname Verifier合规实现求助(SonarCloud检测不通过)

问题背景

向第三方服务发送POST请求时,使用了全信任的DummyTrustManager和DummyHostnameVerifier,导致无法通过SonarCloud检测,检测抛出以下安全违规:

  • S4423:信任管理器不应盲目信任所有SSL证书,会暴露在中间人攻击风险下
  • S5527:主机名验证器不应接受任意主机名,绕过SSL主机名校验存在安全隐患

原代码如下:

public static class DummyTrustManager implements X509TrustManager {

        public DummyTrustManager() {
        }

        public boolean isClientTrusted(X509Certificate cert[]) {
            return true;
        }

        public boolean isServerTrusted(X509Certificate cert[]) {
            return true;
        }

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

        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {

        }

        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {

        }
    }

    public static class DummyHostnameVerifier implements HostnameVerifier {

        public boolean verify(String urlHostname, String certHostname) {
            return true;
        }

        public boolean verify(String arg0, SSLSession arg1) {
            return true;
        }
    }

    public String nsdlResponseLine(String data, String signature){

        String line = null;
        try {
            String urlOfNsdl = nsdlKycVerificationUrl;
            final String version = nsdlKycVerificationVersion;
            SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
            sslcontext.init(new KeyManager[0],
                    new TrustManager[]{new DummyTrustManager()},
                    new SecureRandom());
            SSLSocketFactory factory = sslcontext.getSocketFactory();
            String urlParameters = getUrlParameters(data, signature, version);
            URL url = new URL(urlOfNsdl);

            connection = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
            connection.setRequestProperty("Content-Language", "en-US");
            connection.setUseCaches(false);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setSSLSocketFactory(factory);
            connection.setHostnameVerifier(new DummyHostnameVerifier());
            OutputStream os = connection.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os);
            osw.write(urlParameters);
            osw.flush();
            osw.close();

            InputStream is = connection.getInputStream();
            BufferedReader in = new BufferedReader(new InputStreamReader(is));
            line = in.readLine();
            is.close();
            in.close();
        } catch (Exception e) {
            log.debug("::Exception: {}",e.getMessage());
        }
        return line;
    }

    private String getUrlParameters(String data, String signature, String version) throws UnsupportedEncodingException {
        return "data=" + URLEncoder.encode(data, "UTF-8") + "&signature=" + URLEncoder.encode(signature, "UTF-8") + "&version=" + URLEncoder.encode(version, "UTF-8");
    }
合规实现方案

1. 自定义TrustManager:仅信任指定第三方证书

不再盲目信任所有证书,仅校验并信任第三方服务的合法证书,可通过加载本地证书文件实现。

示例代码:

public static class TrustedCertTrustManager implements X509TrustManager {
    private final X509Certificate trustedCert;

    public TrustedCertTrustManager(X509Certificate trustedCert) {
        this.trustedCert = trustedCert;
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        // 若无需验证客户端证书,保持空实现即可
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        boolean isTrusted = false;
        // 遍历服务端证书链,校验是否包含信任的证书
        for (X509Certificate cert : chain) {
            // 校验证书有效期
            cert.checkValidity();
            try {
                // 验证证书签名是否匹配信任证书的公钥
                cert.verify(trustedCert.getPublicKey());
                isTrusted = true;
                break;
            } catch (Exception e) {
                // 单个证书验证失败,继续检查下一个
            }
        }
        if (!isTrusted) {
            throw new CertificateException("服务端证书未通过信任校验");
        }
    }

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

    // 加载本地X.509证书的工具方法
    public static X509Certificate loadCertificate(InputStream certStream) throws CertificateException {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        return (X509Certificate) cf.generateCertificate(certStream);
    }
}

2. 自定义HostnameVerifier:严格校验主机名

仅允许指定的合法主机名通过校验,或直接复用JDK默认的标准主机名验证逻辑。

示例代码:

public static class StrictHostnameVerifier implements HostnameVerifier {
    private final Set<String> allowedHostnames;

    public StrictHostnameVerifier(Set<String> allowedHostnames) {
        this.allowedHostnames = allowedHostnames;
    }

    @Override
    public boolean verify(String hostname, SSLSession session) {
        // 优先校验是否在允许列表中,再调用默认逻辑处理通配符等场景
        if (allowedHostnames.contains(hostname)) {
            return true;
        }
        return HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session);
    }

    @Override
    public boolean verify(String urlHostname, String certHostname) {
        return HttpsURLConnection.getDefaultHostnameVerifier().verify(urlHostname, certHostname);
    }
}

3. 改造后的请求发送代码

public String nsdlResponseLine(String data, String signature){
    String line = null;
    try {
        String urlOfNsdl = nsdlKycVerificationUrl;
        final String version = nsdlKycVerificationVersion;
        
        // 从本地资源加载第三方信任证书(替换为实际证书路径)
        InputStream certInputStream = getClass().getResourceAsStream("/nsdl-trust-cert.crt");
        X509Certificate trustedCert = TrustedCertTrustManager.loadCertificate(certInputStream);
        
        // 初始化安全的SSL上下文
        SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
        sslcontext.init(new KeyManager[0],
                new TrustManager[]{new TrustedCertTrustManager(trustedCert)},
                new SecureRandom());
        SSLSocketFactory factory = sslcontext.getSocketFactory();
        
        String urlParameters = getUrlParameters(data, signature, version);
        URL url = new URL(urlOfNsdl);

        connection = (HttpsURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
        connection.setRequestProperty("Content-Language", "en-US");
        connection.setUseCaches(false);
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setSSLSocketFactory(factory);
        
        // 设置允许的合法主机名(替换为第三方服务实际域名)
        Set<String> allowedHosts = new HashSet<>(Arrays.asList("official.nsdl-service.com"));
        connection.setHostnameVerifier(new StrictHostnameVerifier(allowedHosts));
        
        OutputStream os = connection.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(os);
        osw.write(urlParameters);
        osw.flush();
        osw.close();

        InputStream is = connection.getInputStream();
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        line = in.readLine();
        is.close();
        in.close();
    } catch (Exception e) {
        log.debug("::Exception: {}",e.getMessage());
    }
    return line;
}
替代简化方案

如果第三方服务使用的是受全球信任CA签发的证书,无需自定义TrustManager和HostnameVerifier,直接删除原代码中自定义信任管理器、SSL上下文初始化、主机名验证器设置的逻辑,使用JDK默认的HTTPS信任机制即可。

内容的提问来源于stack exchange,提问作者Vedant Dixit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 13:05:42