HttpsUrlConnection two-way Certificate Verification Times error

Server API requires two-way certificate verification (similar to WeChat Pay). The return value is normal when using HttpClient, but the error code 400 will be returned by HttpsUrlConnection or OkHttp3 server.
HttpsUrlConnection code is as follows:

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.SecureRandom;

// FIXME  400, 
public class HUC {

    public static void main(String... args) throws Exception {

        // 
        final String requestUrl = "https://example.com";
        // POST 
        final String requestBody = "{\"loginName\":\"admin\", \"password\":\"admin\"}";
        // 
        final String certFile = "D:\\Downloads\\test.pfx";
        // 
        final String password = "drowssap";

        // SSL 
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        try (FileInputStream fileInputStream = new FileInputStream(certFile)) {
            keyStore.load(fileInputStream, password.toCharArray());
        }
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, password.toCharArray());
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());

        // 
        String response;
        URL url = new URL(requestUrl);
        HttpsURLConnection httpsURLConnection = null;
        try {
            httpsURLConnection = (HttpsURLConnection) url.openConnection();

            // SSL 
            httpsURLConnection.setHostnameVerifier((hostname, sslSession) -> true);
            httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());

            httpsURLConnection.setRequestMethod("POST");
            httpsURLConnection.setUseCaches(false);
            httpsURLConnection.setDoOutput(true);
            httpsURLConnection.setDoInput(true);
            httpsURLConnection.setConnectTimeout(5000);
            httpsURLConnection.setReadTimeout(5000);
            httpsURLConnection.setRequestProperty("Content-Type", "text/json; charset=utf-8");
            
            OutputStream outputStream = httpsURLConnection.getOutputStream();
            outputStream.write(requestBody.getBytes(StandardCharsets.UTF_8));
            outputStream.flush();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            InputStream inputStream = httpsURLConnection.getInputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, length);
            }
            response = new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
        } finally {
            if (httpsURLConnection != null) {
                httpsURLConnection.disconnect();
            }
        }

        System.out.print(response);
    }
}

OkHttp3, like HttpsUrlConnection, returns error code 400, so the code is not posted.

HttpClient is normal, the code is as follows

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.SecureRandom;

public class HC {

    public static void main(String[] args) throws Exception {
        BasicHttpClientConnectionManager connManager;

        char[] password = "drowssap".toCharArray();
        FileInputStream fileInputStream = new FileInputStream("D:\\Downloads\\test.pfx");
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(fileInputStream, password);
        fileInputStream.close();

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, password);

        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslContext,
                new String[]{"TLSv1.2"},
                null,
                new DefaultHostnameVerifier());

        connManager = new BasicHttpClientConnectionManager(
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", sslConnectionSocketFactory)
                        .build(),
                null,
                null,
                null
        );

        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();

        String url = "https://example.com";
        HttpPost httpPost = new HttpPost(url);

        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();
        httpPost.setConfig(requestConfig);

        StringEntity postEntity = new StringEntity("{\"loginName\":\"admin\", \"password\":\"admin\"}", "UTF-8");
        httpPost.addHeader("Content-Type", "text/json");
        httpPost.setEntity(postEntity);

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();

        InputStream inputStream = httpEntity.getContent();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int length;
        while ((length = inputStream.read(bytes)) != -1) {
            byteArrayOutputStream.write(bytes, 0, length);
        }

        System.out.println(new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8));
    }
}

Google for a long time, the only clue we can find is the instructions for the selection of Http framework in Wechat"s development document-> portal , which seems to be somewhat consistent with the situation

.
Menu