The pitfalls and solutions encountered in calling the Https interface

Directory

The process of stepping on the pit

background

premise

Start troubleshooting

in conclusion

About the interface of the Https protocol

Java implements the method of calling Https interface

1. RestTemplate skips validation

2. Verification method

3. Forest framework call interface

reference documents


The process of stepping on the pit

Background

Now there is an interface provided by a platform, which is in the form of Https, but the document provided is written as Http, and they have kindly paid for the code sample. Do you think that you can CV in this way? Think simple, young people .

But when I actually called it, I found that the interface was not working. The person in charge told me that this is a simple https interface, you can just call it, and I will not give me a server.pem and a server.key until I send him the error report about the lack of certificate. , using these two files to call, there is still a problem;

Later, I gave a pem file, and using this latest file to transfer the interface, still reported an error. After changing various codes for calling the interface, the error was still the same;

Prerequisite

1. Because the server prohibits ping, use the telnet command (if not, you need to install it first)

2. The hosts file on this server also maps IP to domain name

3. The DNS settings on the server are also done

Start troubleshooting

1. First use the keytool command to import the latest pem certificate file into the JDK of the client server

keytool -import -v -trustcacerts -alias mycacerts -file radiance-com-cn.pem -storepass changeit -keystore JDK/jre/lib/security/cacerts

2. Check the imported certificate

keytool -list -keystore "JDKfile/jdk1.8.0_131/jre/lib/security/cacerts" -storepass changeit 

3. Use the curl command to call the interface and view the results

curl -v https://xxxxxxxx

The curl command needs to support the https protocol

If curl does not support the https protocol, check whether there is an openssl master on the server

4. Try to use the code to adjust the interface, but it doesn’t work. It keeps reporting an error, connect reset

java.net.SocketException: Connection reset
        at java.net.SocketInputStream.read(SocketInputStream.java:209) ~[?:1.8.0_102]
        at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[?:1.8.0_102]

5. Capture packets

Capture packets on the https client side, the result is that after the TCP three-way handshake, the client starts SSL verification, sends client hello to the server, and the server returns reset, disconnecting this connection, and the SSL/TSL calibration behind test no longer exists;

Looking at the packet capture on the server side, it is the reset sent by 43600, which does not match the above server side!

Conclusion

Therefore, seeing this, it can only be proved that there are other network devices between the client server and the server server, resulting in network problems, which in turn lead to inconsistencies in the capture of packets between the client and the server.

After feeding back the packet capture and phenomenon to the person in charge of the platform, I don’t know what they have changed, so the interface can be adjusted, and the following things will be easy to handle, just adjust the interface according to the packet capture;

Shout: Grab the bag yyds!

It is too common for the actual interface input parameters, return values and original documents to be inconsistent…

About the interface of the Https protocol

The Https interface is different from the Http interface, and a layer of SSL/TSL certificate verification is added on top of Http;

The general way to call is to skip the verification of the certificate and directly access the interface. There are also many ways to skip. I used the RestTemplate method at the beginning;

SYN: Synchronize Sequence Numbers synchronization sequence number

ACK: Acknowledge character confirmation character

RST: reset

A normal two-way verification is shown in the figure below

First perform the TCP three-way handshake, after the handshake is successful

1. The client sends client hello to the server, carries the encryption suite supported by the client, and sends a 32-bit random number to the server

2. After the server receives the client hello, it replies with ACK

3. The server then replies Server Hello, and generates random numbers, the encryption algorithm used by Ciper Suite, and the version of tls here we use TLS v1.2;

4. After the client receives the Server Hello, it replies with ACK

5. Next, the server will generate a public key and a private key, keep the private key and send the public key to the client to authenticate the identity, and send Server Hello Done to indicate the end of Server Hello;

6. The client receives the certificate reply ACK from the server

7. The client sends the certificate to the server, exchanges the certificates Client key Exchange, generates a pair of public key and private key, and sends the public key to the server;

8. After the server receives it, it will reply ACK

9. The client and the server exchange, start to use the negotiated key at the beginning, and the server replies ACK after receiving it

10. The client generates the corresponding key according to the information exchanged during the interaction and the cipher suite of the server

encrypted handshake message: The client sends an encrypted finished message to the server to prove the encrypted communication established by the handshake just now;

Java realizes the method of calling Https interface

There are many ways to call Https at the java code level. Here are a few brief introductions. They are all online codes of CV, which can be used directly without reinventing the wheel.

1.RestTemplate skip verification

@Configuration
public class RestTemplateConfig {
    /**
     * restTemplate skips Https interface SSL authentication
     *
     * @return
     */
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        return new RestTemplate(generateHttpRequestFactory());
    }


    private HttpComponentsClientHttpRequestFactory generateHttpRequestFactory() {
        TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
        SSLContext sslContext = null;
        try {
            sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());

        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
        CloseableHttpClient httpClient = httpClientBuilder.build();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setHttpClient(httpClient);
        return factory;
    }
}

In the http tool class, you can directly use restTemplate to call the interface

2. Verification method

There are many ways to perform verification. In the process of stepping on the pit in the above case, I found many ways to use certificate verification on the Internet, and wrote a lot of test methods to try to call their interfaces, but they all reported errors. ;

There are many ways to do this, but since the certificates given by the platform do not meet the requirements, I will not list them one by one. chatgpt really helped a lot, and I wrote them in a quack

Write this one now, and study slowly later

The following code is generated by chatgpt and has not passed actual verification. Use with caution! ! !

/**
     * Use pem for SSL verification, call https interface, post request
     *
     * @param httpsUrl interface URL
     * @param body input parameter
     * @param keystoreFile pem file path
     * @param keystorePass pem file password
     * @param sslType SSL type
     * @return call interface return
     */
    public String sendPostByHttps(String httpsUrl, JSONObject body, String keystoreFile, String keystorePass, String sslType) {
        try {
            //Set the https request can be accessed through the ip address
            HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
            URL url = new URL(httpsUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url. openConnection();
            // Set up the SSL context before connecting
            SSLContext sslContext = SSLContext. getInstance(sslType);

            // Create an SSLContext object and initialize it with the trust manager we specified
            TrustManager[] tm = {new MyX509TrustManager(keystoreFile, keystorePass)};
            sslContext.init(null, tm, new java.security.SecureRandom());
            conn.setSSLSocketFactory(sslContext.getSocketFactory());
            
// Subsequent operations and HTTPS requests can be made normally
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);// Open the output stream to submit data to the server
            conn.setDoInput(true); // Open the input stream to get data from the server


            PrintWriter out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
            out. print(body. toJSONString());
            out. flush();
            out. close();

            int responseCode = conn. getResponseCode();
            log.info("Call the interface, the HTTPS return status code is: {}", responseCode);

            if (responseCode == HttpURLConnection. HTTP_OK) {
                InputStream inputStreamRes = conn. getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreamRes));
                String line;
                StringBuilder result = new StringBuilder();
                while ((line = reader. readLine()) != null) {
                    result.append(line);
                }
                reader. close();
                inputStreamRes. close();
                return result.toString(); //is the return of the interface
            }
        } catch (Exception e) {
            log.error("Request HTTPS interface, request failed | e = {}", e.getMessage(), e);
        }
        return null;
    }
public class MyX509TrustManager implements X509TrustManager {

    private X509TrustManager sun JSSEX509TrustManager;

    MyX509TrustManager(String keystoreFile, String pass) throws Exception {
        KeyStore ks = KeyStore. getInstance("JKS");
        ks.load(new FileInputStream(keystoreFile), pass.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory. getInstance("SunX509", "SunJSSE");
        tmf.init(ks);
        TrustManager[] tms = tmf. getTrustManagers();
        for (TrustManager tm : tms) {
            if (tm instanceof X509TrustManager) {
                sunJSSEX509TrustManager = (X509TrustManager) tm;
                return;
            }
        }
        throw new Exception("Couldn't initialize");
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        try {
            sunJSSEX509TrustManager.checkClientTrusted(chain, authType);
        } catch (CertificateException except) {
            except. printStackTrace();
        }
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        try {
            sunJSSEX509TrustManager.checkServerTrusted(chain, authType);
        } catch (CertificateException except) {
            except. printStackTrace();
        }
    }

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

}

3.Forest framework calling interface

This method has been verified to be possible, and it is also the fastest to get started;

Newbie Introduction | Forest (dtflyx.com)

pom dependencies

 <dependency>
            <groupId>com.dtflys.forest</groupId>
            <artifactId>forest-spring-boot-starter</artifactId>
            <version>1.5.0</version>
  </dependency>

Configuration items in the properties file

#Configure the backend HTTP-API as okhttp3
forest.backend=okhttp3
Whether to enable the main switch of log printing in the #forest component
forest.log-enabled=false
#id value in forest component
forest.ssl-key-stores.id=myhttpstest
The path to the certificate in the #forest component
forest.ssl-key-stores.file=
The key in the #forest component is not empty
forest.ssl-key-stores.keystore-pass=
The key in the #forest component is not empty
forest.ssl-key-stores.cert-pass=
Encrypted version in #forest component
forest.ssl-key-stores.protocols=TLSv1.2
public interface HttpsClient {

    /**
     * Https interface test
     *
     * @param url interface name
     * @param param input parameter
     * @param headerMap interface Header
     * @return
     */
    @Post(url = "${url}", keyStore = "myhttpstest")
    ForestResponse<String> getHttpsTest(@DataVariable("url") String url,
                                        @JSONBody String param,
                                        @Header Map<String, Object> headerMap
    );

Add the following annotations to the Application startup class

@ForestScan(basePackages = "com.test.forest")
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Directly inject HttpsClient into the business method of calling the interface, and the calling of the interface can be realized through the form of method calling. The specific principle is that the framework has already realized it for us, so there is no need to write so many codes repeatedly;

....

ForestResponse<String> resultEntity = httpClient.getIdmPersonInfo(url, param.toString(), headerMap);

Reference documents

https://blog.csdn.net/qq78442761/article/details/120149824

Wireshark packet capture analysis HTTPS – Zhihu (zhihu.com)