HttpsUrlConnection 双向证书验证时报错
服务器 API 需要进行双向证书验证(类似于微信支付), 使用 HttpClient 时返回值正常, 但是换成 HttpsUrlConnection 或者 OkHttp3 服务器就会返回错误码 400.
HttpsUrlConnection 代码如下:
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 与 HttpsUrlConnection 一样, 返回错误码 400, 所以代码就不贴了.
HttpClient 是正常的, 代码如下
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 了很久, 唯一找到的一点头绪是微信开发文档中对于 Http 框架选用的说明 -> 传送门, 似乎有点符合情况
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论