-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathJavaNetHttpClientFactory.java
157 lines (130 loc) · 5.9 KB
/
JavaNetHttpClientFactory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package com.getindata.connectors.http.internal.utils;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.util.StringUtils;
import com.getindata.connectors.http.internal.config.HttpConnectorConfigConstants;
import com.getindata.connectors.http.internal.security.SecurityContext;
import com.getindata.connectors.http.internal.security.SelfSignedTrustManager;
@Slf4j
@NoArgsConstructor(access = AccessLevel.NONE)
public class JavaNetHttpClientFactory {
/**
* Creates Java's {@link HttpClient} instance that will be using default, JVM shared {@link
* java.util.concurrent.ForkJoinPool} for async calls.
*
* @param properties properties used to build {@link SSLContext}
* @return new {@link HttpClient} instance.
*/
public static HttpClient createClient(Properties properties) {
SSLContext sslContext = getSslContext(properties);
return HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
.sslContext(sslContext)
.build();
}
/**
* Creates Java's {@link HttpClient} instance that will be using provided Executor for all async
* calls.
*
* @param properties properties used to build {@link SSLContext}
* @param executor {@link Executor} for async calls.
* @return new {@link HttpClient} instance.
*/
public static HttpClient createClient(Properties properties, Executor executor) {
SSLContext sslContext = getSslContext(properties);
return HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
.sslContext(sslContext)
.executor(executor)
.build();
}
/**
* Creates an {@link SSLContext} based on provided properties.
* <ul>
* <li>{@link HttpConnectorConfigConstants#ALLOW_SELF_SIGNED}</li>
* <li>{@link HttpConnectorConfigConstants#SERVER_TRUSTED_CERT}</li>
* <li>{@link HttpConnectorConfigConstants#PROP_DELIM}</li>
* <li>{@link HttpConnectorConfigConstants#CLIENT_CERT}</li>
* <li>{@link HttpConnectorConfigConstants#CLIENT_PRIVATE_KEY}</li>
* </ul>
*
* @param properties properties used to build {@link SSLContext}
* @return new {@link SSLContext} instance.
*/
private static SSLContext getSslContext(Properties properties) {
SecurityContext securityContext = createSecurityContext(properties);
boolean selfSignedCert = Boolean.parseBoolean(
properties.getProperty(HttpConnectorConfigConstants.ALLOW_SELF_SIGNED, "false"));
String[] serverTrustedCerts = properties
.getProperty(HttpConnectorConfigConstants.SERVER_TRUSTED_CERT, "")
.split(HttpConnectorConfigConstants.PROP_DELIM);
String clientCert = properties
.getProperty(HttpConnectorConfigConstants.CLIENT_CERT, "");
String clientPrivateKey = properties
.getProperty(HttpConnectorConfigConstants.CLIENT_PRIVATE_KEY, "");
for (String cert : serverTrustedCerts) {
if (!StringUtils.isNullOrWhitespaceOnly(cert)) {
securityContext.addCertToTrustStore(cert);
}
}
if (!StringUtils.isNullOrWhitespaceOnly(clientCert)
&& !StringUtils.isNullOrWhitespaceOnly(clientPrivateKey)) {
securityContext.addMTlsCerts(clientCert, clientPrivateKey);
}
// NOTE TrustManagers must be created AFTER adding all certificates to KeyStore.
TrustManager[] trustManagers = getTrustedManagers(securityContext, selfSignedCert);
return securityContext.getSslContext(trustManagers);
}
private static TrustManager[] getTrustedManagers(
SecurityContext securityContext,
boolean selfSignedCert) {
TrustManager[] trustManagers = securityContext.getTrustManagers();
if (selfSignedCert) {
return wrapWithSelfSignedManagers(trustManagers).toArray(new TrustManager[0]);
} else {
return trustManagers;
}
}
private static List<TrustManager> wrapWithSelfSignedManagers(TrustManager[] trustManagers) {
log.warn("Creating Trust Managers for self-signed certificates - not Recommended. "
+ "Use [" + HttpConnectorConfigConstants.SERVER_TRUSTED_CERT + "] "
+ "connector property to add certificated as trusted.");
List<TrustManager> selfSignedManagers = new ArrayList<>(trustManagers.length);
for (TrustManager trustManager : trustManagers) {
selfSignedManagers.add(new SelfSignedTrustManager((X509TrustManager) trustManager));
}
return selfSignedManagers;
}
/**
* Creates a {@link SecurityContext} with empty {@link java.security.KeyStore} or loaded from
* file.
*
* @param properties Properties for creating {@link SecurityContext}
* @return new {@link SecurityContext} instance.
*/
private static SecurityContext createSecurityContext(Properties properties) {
String keyStorePath =
properties.getProperty(HttpConnectorConfigConstants.KEY_STORE_PATH, "");
if (StringUtils.isNullOrWhitespaceOnly(keyStorePath)) {
return SecurityContext.create();
} else {
char[] storePassword =
properties.getProperty(HttpConnectorConfigConstants.KEY_STORE_PASSWORD, "")
.toCharArray();
if (storePassword.length == 0) {
throw new RuntimeException("Missing password for provided KeyStore");
}
return SecurityContext.createFromKeyStore(keyStorePath, storePassword);
}
}
}