@@ -151,7 +151,7 @@ private DatabaseClientFactory.SecurityContext newSecurityContext() {
151151 }
152152 final String authType = (String ) typeValue ;
153153
154- final SSLInputs sslInputs = buildSSLInputs (authType );
154+ final SSLUtil . SSLInputs sslInputs = buildSSLInputs (authType );
155155 DatabaseClientFactory .SecurityContext securityContext = newSecurityContext (authType , sslInputs );
156156 if (sslInputs .getSslContext () != null ) {
157157 securityContext .withSSLContext (sslInputs .getSslContext (), sslInputs .getTrustManager ());
@@ -160,7 +160,7 @@ private DatabaseClientFactory.SecurityContext newSecurityContext() {
160160 return securityContext ;
161161 }
162162
163- private DatabaseClientFactory .SecurityContext newSecurityContext (String type , SSLInputs sslInputs ) {
163+ private DatabaseClientFactory .SecurityContext newSecurityContext (String type , SSLUtil . SSLInputs sslInputs ) {
164164 switch (type .toLowerCase ()) {
165165 case DatabaseClientBuilder .AUTH_TYPE_BASIC :
166166 return newBasicAuthContext ();
@@ -188,11 +188,15 @@ private String getRequiredStringValue(String propertyName) {
188188 }
189189
190190 private String getNullableStringValue (String propertyName ) {
191+ return getNullableStringValue (propertyName , null );
192+ }
193+
194+ private String getNullableStringValue (String propertyName , String defaultValue ) {
191195 Object value = propertySource .apply (PREFIX + propertyName );
192196 if (value != null && !(value instanceof String )) {
193197 throw new IllegalArgumentException (propertyName + " must be of type String" );
194198 }
195- return (String ) value ;
199+ return value != null ? (String ) value : defaultValue ;
196200 }
197201
198202 private DatabaseClientFactory .SecurityContext newBasicAuthContext () {
@@ -221,7 +225,7 @@ private DatabaseClientFactory.SecurityContext newCloudAuthContext() {
221225 return new DatabaseClientFactory .MarkLogicCloudAuthContext (apiKey , duration );
222226 }
223227
224- private DatabaseClientFactory .SecurityContext newCertificateAuthContext (SSLInputs sslInputs ) {
228+ private DatabaseClientFactory .SecurityContext newCertificateAuthContext (SSLUtil . SSLInputs sslInputs ) {
225229 String file = getNullableStringValue ("certificate.file" );
226230 String password = getNullableStringValue ("certificate.password" );
227231 if (file != null && file .trim ().length () > 0 ) {
@@ -234,6 +238,9 @@ private DatabaseClientFactory.SecurityContext newCertificateAuthContext(SSLInput
234238 throw new RuntimeException ("Unable to create CertificateAuthContext; cause " + e .getMessage (), e );
235239 }
236240 }
241+ if (sslInputs .getSslContext () == null ) {
242+ throw new RuntimeException ("An SSLContext is required for certificate authentication." );
243+ }
237244 return new DatabaseClientFactory .CertificateAuthContext (sslInputs .getSslContext (), sslInputs .getTrustManager ());
238245 }
239246
@@ -271,28 +278,34 @@ private DatabaseClientFactory.SSLHostnameVerifier determineHostnameVerifier() {
271278 * case the user does not define their own SSLContext or SSL protocol
272279 * @return
273280 */
274- private SSLInputs buildSSLInputs (String authType ) {
281+ private SSLUtil . SSLInputs buildSSLInputs (String authType ) {
275282 X509TrustManager userTrustManager = getTrustManager ();
276283
277284 // Approach 1 - user provides an SSLContext object, in which case there's nothing further to check.
278285 SSLContext sslContext = getSSLContext ();
279286 if (sslContext != null ) {
280- return new SSLInputs (sslContext , userTrustManager );
287+ return new SSLUtil . SSLInputs (sslContext , userTrustManager );
281288 }
282289
283- // Approaches 2 and 3 - user defines an SSL protocol.
284- // Approach 2 - "default" is a convenience for using the JVM's default SSLContext.
285- // Approach 3 - create a new SSLContext, and initialize it if the user-provided TrustManager is not null.
290+ // Approach 2 - user wants two-way SSL via a keystore.
291+ final String keyStorePath = getNullableStringValue ("ssl.keystore.path" );
292+ if (keyStorePath != null && keyStorePath .trim ().length () > 0 ) {
293+ return useKeyStoreForTwoWaySSL (keyStorePath , userTrustManager );
294+ }
295+
296+ // Approaches 3 and 4 - user defines an SSL protocol.
297+ // Approach 3 - "default" is a convenience for using the JVM's default SSLContext.
298+ // Approach 4 - create a new SSLContext, and initialize it if the user-provided TrustManager is not null.
286299 final String sslProtocol = getSSLProtocol (authType );
287300 if (sslProtocol != null ) {
288301 return "default" .equalsIgnoreCase (sslProtocol ) ?
289302 useDefaultSSLContext (userTrustManager ) :
290303 useNewSSLContext (sslProtocol , userTrustManager );
291304 }
292305
293- // Approach 4 - still return the user-defined TrustManager as that may be needed for certificate authentication,
306+ // Approach 5 - still return the user-defined TrustManager as that may be needed for certificate authentication,
294307 // which has its own way of constructing an SSLContext from a PKCS12 file.
295- return new SSLInputs (null , userTrustManager );
308+ return new SSLUtil . SSLInputs (null , userTrustManager );
296309 }
297310
298311 private X509TrustManager getTrustManager () {
@@ -332,27 +345,36 @@ private String getSSLProtocol(String authType) {
332345 return sslProtocol ;
333346 }
334347
348+ private SSLUtil .SSLInputs useKeyStoreForTwoWaySSL (String keyStorePath , X509TrustManager userTrustManager ) {
349+ final String password = getNullableStringValue ("ssl.keystore.password" );
350+ final String keyStoreType = getNullableStringValue ("ssl.keystore.type" , "JKS" );
351+ final String algorithm = getNullableStringValue ("ssl.keystore.algorithm" , "SunX509" );
352+ final char [] charPassword = password != null ? password .toCharArray () : null ;
353+ final String sslProtocol = getNullableStringValue ("sslProtocol" , "TLSv1.2" );
354+ return SSLUtil .createSSLContextFromKeyStore (keyStorePath , charPassword , keyStoreType , algorithm , sslProtocol , userTrustManager );
355+ }
356+
335357 /**
336358 * Uses the JVM's default SSLContext. Because OkHttp requires a separate TrustManager, this approach will either
337359 * user the user-provided TrustManager or it will assume that the JVM's default TrustManager should be used.
338360 */
339- private SSLInputs useDefaultSSLContext (X509TrustManager userTrustManager ) {
361+ private SSLUtil . SSLInputs useDefaultSSLContext (X509TrustManager userTrustManager ) {
340362 SSLContext sslContext ;
341363 try {
342364 sslContext = SSLContext .getDefault ();
343365 } catch (NoSuchAlgorithmException e ) {
344366 throw new RuntimeException ("Unable to obtain default SSLContext; cause: " + e .getMessage (), e );
345367 }
346368 X509TrustManager trustManager = userTrustManager != null ? userTrustManager : SSLUtil .getDefaultTrustManager ();
347- return new SSLInputs (sslContext , trustManager );
369+ return new SSLUtil . SSLInputs (sslContext , trustManager );
348370 }
349371
350372 /**
351373 * Constructs a new SSLContext based on the given protocol (e.g. TLSv1.2). The SSLContext will be initialized if
352374 * the user's TrustManager is not null. Otherwise, OkHttpUtil will eventually initialize the SSLContext using the
353375 * JVM's default TrustManager.
354376 */
355- private SSLInputs useNewSSLContext (String sslProtocol , X509TrustManager userTrustManager ) {
377+ private SSLUtil . SSLInputs useNewSSLContext (String sslProtocol , X509TrustManager userTrustManager ) {
356378 SSLContext sslContext ;
357379 try {
358380 sslContext = SSLContext .getInstance (sslProtocol );
@@ -368,27 +390,6 @@ private SSLInputs useNewSSLContext(String sslProtocol, X509TrustManager userTrus
368390 sslProtocol , e .getMessage ()), e );
369391 }
370392 }
371- return new SSLInputs (sslContext , userTrustManager );
372- }
373-
374- /**
375- * Captures the inputs provided by the caller that pertain to constructing an SSLContext.
376- */
377- private static class SSLInputs {
378- private final SSLContext sslContext ;
379- private final X509TrustManager trustManager ;
380-
381- public SSLInputs (SSLContext sslContext , X509TrustManager trustManager ) {
382- this .sslContext = sslContext ;
383- this .trustManager = trustManager ;
384- }
385-
386- public SSLContext getSslContext () {
387- return sslContext ;
388- }
389-
390- public X509TrustManager getTrustManager () {
391- return trustManager ;
392- }
393+ return new SSLUtil .SSLInputs (sslContext , userTrustManager );
393394 }
394395}
0 commit comments