@@ -30,13 +30,31 @@ import java.net.URLEncoder;
30
30
import java.net.URLConnection;
31
31
32
32
import java.io.File;
33
+ import java.io.InputStream;
33
34
import java.io.IOException;
34
35
import java.io.UnsupportedEncodingException;
35
36
37
+ import java.security.GeneralSecurityException;
38
+ import java.security.KeyStore;
39
+ import java.security.SecureRandom;
40
+ import java.security.cert.Certificate;
41
+ import java.security.cert.CertificateException;
42
+ import java.security.cert.CertificateFactory;
43
+ import java.security.cert.X509Certificate;
44
+
36
45
import java.text.DateFormat;
37
46
import java.text.SimpleDateFormat;
38
47
import java.text.ParseException;
39
48
49
+ import javax.net.ssl.HostnameVerifier;
50
+ import javax.net.ssl.KeyManager;
51
+ import javax.net.ssl.KeyManagerFactory;
52
+ import javax.net.ssl.SSLContext;
53
+ import javax.net.ssl.SSLSession;
54
+ import javax.net.ssl.TrustManager;
55
+ import javax.net.ssl.TrustManagerFactory;
56
+ import javax.net.ssl.X509TrustManager;
57
+
40
58
import okio.BufferedSink;
41
59
import okio.Okio;
42
60
@@ -64,12 +82,17 @@ public class ApiClient {
64
82
private String datetimeFormat;
65
83
private DateFormat datetimeFormatter;
66
84
85
+ private InputStream sslCaCert;
86
+ private boolean verifyingSsl;
87
+
67
88
private OkHttpClient httpClient;
68
89
private JSON json;
69
90
70
91
public ApiClient() {
71
92
httpClient = new OkHttpClient();
72
93
94
+ verifyingSsl = true ;
95
+
73
96
json = new JSON(this);
74
97
75
98
// Use ISO 8601 format for date and datetime.
@@ -134,6 +157,35 @@ public class ApiClient {
134
157
return responseHeaders;
135
158
}
136
159
160
+ public boolean isVerifyingSsl() {
161
+ return verifyingSsl;
162
+ }
163
+
164
+ /**
165
+ * Configure whether to verify certificate and hostname when making https requests.
166
+ * Default to true.
167
+ * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks.
168
+ */
169
+ public ApiClient setVerifyingSsl(boolean verifyingSsl) {
170
+ this.verifyingSsl = verifyingSsl;
171
+ applySslSettings();
172
+ return this;
173
+ }
174
+
175
+ public InputStream getSslCaCert() {
176
+ return sslCaCert;
177
+ }
178
+
179
+ /**
180
+ * Configure the CA certificate to be trusted when making https requests.
181
+ * Use null to reset to default.
182
+ */
183
+ public ApiClient setSslCaCert(InputStream sslCaCert) {
184
+ this.sslCaCert = sslCaCert;
185
+ applySslSettings();
186
+ return this;
187
+ }
188
+
137
189
public String getDateFormat() {
138
190
return dateFormat;
139
191
}
@@ -501,7 +553,7 @@ public class ApiClient {
501
553
}
502
554
503
555
/**
504
- * Deserialize response body to Java object, according the Content-Type
556
+ * Deserialize response body to Java object, according to the Content-Type
505
557
* response header.
506
558
*
507
559
* @param response HTTP response
@@ -840,4 +892,69 @@ public class ApiClient {
840
892
return contentType;
841
893
}
842
894
}
895
+
896
+ /**
897
+ * Apply SSL related settings to httpClient according to the current values of
898
+ * verifyingSsl and sslCaCert.
899
+ */
900
+ private void applySslSettings() {
901
+ try {
902
+ KeyManager[] keyManagers = null;
903
+ TrustManager[] trustManagers = null;
904
+ HostnameVerifier hostnameVerifier = null;
905
+ if (! verifyingSsl) {
906
+ TrustManager trustAll = new X509TrustManager() {
907
+ @Override
908
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
909
+ @Override
910
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
911
+ @Override
912
+ public X509Certificate[] getAcceptedIssuers() { return null; }
913
+ };
914
+ SSLContext sslContext = SSLContext.getInstance("TLS");
915
+ trustManagers = new TrustManager[]{ trustAll } ;
916
+ hostnameVerifier = new HostnameVerifier() {
917
+ @Override
918
+ public boolean verify(String hostname, SSLSession session) { return true ; }
919
+ };
920
+ } else if (sslCaCert != null) {
921
+ char[] password = null; // Any password will work.
922
+ CertificateFactory certificateFactory = CertificateFactory.getInstance(" X.509" );
923
+ Collection< ? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert);
924
+ if (certificates.isEmpty()) {
925
+ throw new IllegalArgumentException(" expected non-empty set of trusted certificates" );
926
+ }
927
+ KeyStore caKeyStore = newEmptyKeyStore(password);
928
+ int index = 0;
929
+ for (Certificate certificate : certificates) {
930
+ String certificateAlias = " ca" + Integer.toString(index++);
931
+ caKeyStore.setCertificateEntry(certificateAlias, certificate);
932
+ }
933
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
934
+ trustManagerFactory.init(caKeyStore);
935
+ trustManagers = trustManagerFactory.getTrustManagers();
936
+ }
937
+
938
+ if (keyManagers != null || trustManagers != null) {
939
+ SSLContext sslContext = SSLContext.getInstance(" TLS" );
940
+ sslContext.init(keyManagers, trustManagers, new SecureRandom());
941
+ httpClient.setSslSocketFactory(sslContext.getSocketFactory());
942
+ } else {
943
+ httpClient.setSslSocketFactory(null);
944
+ }
945
+ httpClient.setHostnameVerifier(hostnameVerifier);
946
+ } catch (GeneralSecurityException e) {
947
+ throw new RuntimeException(e);
948
+ }
949
+ }
950
+
951
+ private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
952
+ try {
953
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
954
+ keyStore.load(null, password);
955
+ return keyStore;
956
+ } catch (IOException e) {
957
+ throw new AssertionError(e);
958
+ }
959
+ }
843
960
}
0 commit comments