HttpClient绕过https验证

发布时间 2023-11-17 16:33:16作者: 鹿鹿的布丁
  • 场景: 使用HttpClient请求httpsWebService接口
  • 报错: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

原始调用代码

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

public class SendWSUtil {
    private static Logger logger = LoggerFactory.getLogger(SendWSUtil.class);
    private static String wsUrl = "https://localhost:1443/webservice";

    private SendWSUtil() {
    }

    public static String invokeWs(String serverName, String reqXml, String reqId){
        String soapAction = "";
        String contentType = "text/xml;charset=UTF-8";
        String response = null;
        int statusCode;
        InputStream inputStream = null;
        HttpClient httpClient = new HttpClient();
        PostMethod postMethod = new PostMethod(wsUrl);
        try {
            logger.info("{} WebService Soap Request URL:{}, SoapAction:{}, ContentType:{}", new String[]{reqId, wsUrl, soapAction, contentType});
            String req = formatReq(serverName, reqXml);
            logger.info("{} {}-接口入参--{}", new String[]{reqId, reqXml, req});
            postMethod.setRequestHeader("SOAPAction", soapAction);
            byte[] requestBytes = req.getBytes("utf-8");
            inputStream = new ByteArrayInputStream(requestBytes, 0, requestBytes.length);
            RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, requestBytes.length, contentType);
            postMethod.setRequestEntity(requestEntity);
            statusCode = httpClient.executeMethod(postMethod);
            logger.info("{} WebService Soap Request StatusCode :{}", new String[]{reqId, statusCode+""});
            response = postMethod.getResponseBodyAsString();
            logger.info("{} {}-接口返回--{}", new String[]{reqId, reqXml, response});
            response = response.replace("&lt;", "<");
            response = response.replace("&gt;", ">");
            response = response.replace("<?xml version=\"1.0\" ?>", "");
            response = response.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "");
            response = response.replace("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>","");
            response = response.replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>","");
            logger.info("{} {}-接口替换转义字符返回--{}", new String[]{reqId, reqXml, response});
        } catch (Exception e) {
            logger.warn("{} WebService Soap Request Response :{}", new String[]{reqId, response});
            logger.error("{} {}-接口报错--{}", new String[]{reqId, reqXml, ExceptionUtils.getStackTrace(e)});
        } finally {
            postMethod.releaseConnection();
            if (Objects.nonNull(inputStream)) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return response;
    }
    
    //拼接完整的请求值
    private static String formatReq(String serverName, String message) {
        StringBuilder req = new StringBuilder();
        req.append("<soapenv:Envelope")
                .append("<soapenv:Header/>")
                .append("<soapenv:Body>")
                .append("<HIPMessageServer>")
                .append("<input1>"+serverName+"</input1>")
                .append("<input2><![CDATA["+message+"]]></input2>")
                .append("</HIPMessageServer>")
                .append("</soapenv:Body>")
                .append("</soapenv:Envelope>");
        return req.toString();
    }
}

!> 为什么可以绕过?
在网页中可以提示不是安全的,点击可以强行访问。(这个是可以绕过的前提)

绕过代码

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

public class MyX509TrustManager implements X509TrustManager {
    /* (non-Javadoc)
     * @see javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String)
     */
    public void checkClientTrusted(X509Certificate[] arg0, String arg1)
            throws CertificateException {

    }
    /* (non-Javadoc)
     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String)
     */
    public void checkServerTrusted(X509Certificate[] arg0, String arg1)
            throws CertificateException {

    }
    /* (non-Javadoc)
     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
     */
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClientError;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;

public class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory {

    //这里添加一个属性,主要目的就是来获取ssl跳过验证
    private SSLContext sslContext = null;
    /**
     * Constructor for MySecureProtocolSocketFactory.
     */
    public MySecureProtocolSocketFactory() {
    }
    /**
     * 这个创建一个获取SSLContext的方法,导入MyX509TrustManager进行初始化
     * @return
     */
    private static SSLContext createEasySSLContext() {
        try {
            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, new TrustManager[] { new MyX509TrustManager() },
                    null);
            return context;
        } catch (Exception e) {
            throw new HttpClientError(e.toString());
        }
    }

    /**
     * 判断获取SSLContext
     * @return
     */
    private SSLContext getSSLContext() {
        if (this.sslContext == null) {
            this.sslContext = createEasySSLContext();
        }
        return this.sslContext;
    }
    //后面的方法基本上就是带入相关参数就可以了
    /*
     * (non-Javadoc)
     *
     * @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String,
     *      int, java.net.InetAddress, int)
     */
    public Socket createSocket(String host, int port, InetAddress clientHost,int clientPort) throws IOException, UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(host, port,clientHost, clientPort);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String,
     *      int, java.net.InetAddress, int,
     *      org.apache.commons.httpclient.params.HttpConnectionParams)
     */
    public Socket createSocket(final String host, final int port,final InetAddress localAddress, final int localPort,
                               final HttpConnectionParams params) throws IOException,UnknownHostException, ConnectTimeoutException {
        if (params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }
        int timeout = params.getConnectionTimeout();
        if (timeout == 0) {
            return createSocket(host, port, localAddress, localPort);
        } else {
            return ControllerThreadSocketFactory.createSocket(this, host, port,localAddress, localPort, timeout);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
     */
    public Socket createSocket(String host, int port) throws IOException,UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(host, port);
    }

    /*
     * (non-Javadoc)
     *
     * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
     */
    public Socket createSocket(Socket socket, String host, int port,boolean autoClose) throws IOException, UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(socket, host,port, autoClose);
    }
}

上面两个是固定代码,直接复制就可以

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

public class SendWSUtil {
    private static Logger logger = LoggerFactory.getLogger(SendWSUtil.class);
    private static String wsUrl = "https://localhost:1443/webservice";

    private SendWSUtil() {
    }

    public static String invokeWs(String serverName, String reqXml, String reqId){
        String soapAction = "";
        String contentType = "text/xml;charset=UTF-8";
        String response = null;
        int statusCode;
        InputStream inputStream = null;
        //调用:跳过https
        ProtocolSocketFactory fcty = new MySecureProtocolSocketFactory();
        Protocol.registerProtocol("https", new Protocol("https", fcty, 1443));
        
        HttpClient httpClient = new HttpClient();
        PostMethod postMethod = new PostMethod(wsUrl);
        try {
            logger.info("{} WebService Soap Request URL:{}, SoapAction:{}, ContentType:{}", new String[]{reqId, wsUrl, soapAction, contentType});
            String req = formatReq(serverName, reqXml);
            logger.info("{} {}-接口入参--{}", new String[]{reqId, reqXml, req});
            postMethod.setRequestHeader("SOAPAction", soapAction);
            byte[] requestBytes = req.getBytes("utf-8");
            inputStream = new ByteArrayInputStream(requestBytes, 0, requestBytes.length);
            RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, requestBytes.length, contentType);
            postMethod.setRequestEntity(requestEntity);
            statusCode = httpClient.executeMethod(postMethod);
            logger.info("{} WebService Soap Request StatusCode :{}", new String[]{reqId, statusCode+""});
            response = postMethod.getResponseBodyAsString();
            logger.info("{} {}-接口返回--{}", new String[]{reqId, reqXml, response});
            response = response.replace("&lt;", "<");
            response = response.replace("&gt;", ">");
            response = response.replace("<?xml version=\"1.0\" ?>", "");
            response = response.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "");
            response = response.replace("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>","");
            response = response.replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>","");
            logger.info("{} {}-接口替换转义字符返回--{}", new String[]{reqId, reqXml, response});
        } catch (Exception e) {
            logger.warn("{} WebService Soap Request Response :{}", new String[]{reqId, response});
            logger.error("{} {}-接口报错--{}", new String[]{reqId, reqXml, ExceptionUtils.getStackTrace(e)});
        } finally {
            postMethod.releaseConnection();
            if (Objects.nonNull(inputStream)) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return response;
    }
    
    //拼接完整的请求值
    private static String formatReq(String serverName, String message) {
        StringBuilder req = new StringBuilder();
        req.append("<soapenv:Envelope")
                .append("<soapenv:Header/>")
                .append("<soapenv:Body>")
                .append("<HIPMessageServer>")
                .append("<input1>"+serverName+"</input1>")
                .append("<input2><![CDATA["+message+"]]></input2>")
                .append("</HIPMessageServer>")
                .append("</soapenv:Body>")
                .append("</soapenv:Envelope>");
        return req.toString();
    }
}

在发起请求之前,添加下面两行代码。1443为https请求的端口号,根据实际修改
ProtocolSocketFactory fcty = new MySecureProtocolSocketFactory();
Protocol.registerProtocol("https", new Protocol("https", fcty, 1443));