我有一个 Spring Boot 应用程序试图打开一个 javax.net.ssl.HttpsURLConnection
到服务器,但收到的响应是:java.io.IOException: Server返回 HTTP 响应代码:403 URL:https://serverIP:8443/path
当 keyStore、trustStore 和它们的密码 被设置为系统属性 时请求有效正确并收到预期的 JSON 响应:
System.setProperty("javax.net.ssl.keyStore", "src/main/resources/myKeyStore.p12");
System.setProperty("javax.net.ssl.trustStore", "src/main/resources/myTrustStore.truststore");
System.setProperty("javax.net.ssl.keyStorePassword", "myPassword");
System.setProperty("javax.net.ssl.trustStorePassword", "myPassword");
但是当尝试在 SSLContext 中设置信息时收到 403 响应代码,而不是通过使用返回 SSLContext 对象的方法设置系统属性:
public static SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
throws GeneralSecurityException, IOException {
final KeyStore keystore = KeyStore.getInstance("pkcs12"); // also tried with JKS
try (final InputStream inKeystore = new FileInputStream(keystoreFile)) {
keystore.load(inKeystore, password.toCharArray());
}
try (final InputStream inTruststore = new FileInputStream(trustStoreFile)) {
keystore.load(inTruststore, password.toCharArray());
}
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); // also tried with .getDefaultAlgorithm()
keyManagerFactory.init(keystore, password.toCharArray());
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keystore);
X509TrustManager x509Tm = null;
for (final TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager) {
x509Tm = (X509TrustManager) trustManager;
break;
}
}
final X509TrustManager finalTm = x509Tm;
final X509ExtendedTrustManager customTm = new X509ExtendedTrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return finalTm.getAcceptedIssuers();
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
};
final SSLContext sslContext = SSLContext.getInstance("TLS"); // also tried with SSL
sslContext.init(
keyManagerFactory.getKeyManagers(),
new TrustManager[]{customTm},
new SecureRandom());
final HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
return sslContext;
}
OBS:trustStore 和 keyStore 具有相同的密码,这就是为什么该方法只有一个密码参数并且用于 key 和信任管理器工厂。
getSslContext方法的调用和使用方式是:
final SSLContext sslContext = SSLContextHelper.getSslContext("src/main/resources/myTrustStore.truststore",
"src/main/resources/myKeyStore.p12",
"myPassword");
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
final URL url = new URL("https://serverIP:8443/path");
final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(sslSocketFactory);
// tried adding some headers to the request
urlConnection.addRequestProperty("Content-Type", "application/json");
urlConnection.addRequestProperty("Accept", "application/json");
urlConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
urlConnection.connect();
final InputStream inputstream = urlConnection.getInputStream();
尝试获取 URL 连接的 inputStream 时在最后一行抛出错误。
此外,我尝试使用 org.apache.http
中的以下类:SSLConnectionSocketFactory、HttpClient、HttpGet、HttpResponse
,但响应代码仍然是 403。
我只能认为 SSL 配置中缺少某些东西,因为系统属性有效。欢迎就我在 SSLContext/SSLSocketFactory
中遗漏的设置或如何解决/更好地调试问题提出任何建议!谢谢!
最佳答案
我设法仅通过使用使用 org.apache. http.client.HttpClient
。
获取 RestTemplate
的方法是 SSLContext
keyStore、trustStore 及其密码以下内容:
public RestTemplate getRestTemplate(final String keyStoreFile, final String trustStoreFile,
final String password) throws Exception {
final SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(ResourceUtils.getFile(keyStoreFile), password.toCharArray(), password.toCharArray())
.loadTrustMaterial(ResourceUtils.getFile(trustStoreFile), password.toCharArray())
.build();
final HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
final HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpComponentsClientHttpRequestFactory.setHttpClient(client);
return new RestTemplate(httpComponentsClientHttpRequestFactory);
}
RestTemplate
用于 HTTPS 调用的方式是:
final String keyStoreFile = "src/main/resources/myKeyStore.p12";
final String trustStoreFile = "src/main/resources/myTrustStore.truststore";
final String password = "myPassword"; // same password for keyStore and trustStore
final String response = getRestTemplate(keyStoreFile, trustStoreFile, password).getForObject("https://serverIP:8443/path", String.class);
LOGGER.info("Response received: " + response);
希望这对任何人都有帮助,因为 HTTPS 连接有很多困难:)
关于java - 无法从 Spring 应用程序启动多个 HTTPS 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57372431/