@@ -263,6 +263,12 @@ public class HttpRequest {
263263 */
264264 public static final String PARAM_CHARSET = "charset" ;
265265
266+ public static final String CERT_MODE_DEFAULT = "default" ;
267+
268+ public static final String CERT_MODE_PINNED = "pinned" ;
269+
270+ public static final String CERT_MODE_TRUSTALL = "trustall" ;
271+
266272 private static final String BOUNDARY = "00content0boundary00" ;
267273
268274 private static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary="
@@ -272,13 +278,13 @@ public class HttpRequest {
272278
273279 private static final String [] EMPTY_STRINGS = new String [0 ];
274280
275- private static SSLSocketFactory PINNED_FACTORY ;
281+ private static SSLSocketFactory SOCKET_FACTORY ;
276282
277- private static SSLSocketFactory TRUSTED_FACTORY ;
283+ private static String CURRENT_CERT_MODE = CERT_MODE_DEFAULT ;
278284
279285 private static ArrayList <Certificate > PINNED_CERTS ;
280286
281- private static HostnameVerifier TRUSTED_VERIFIER ;
287+ private static HostnameVerifier HOSTNAME_VERIFIER ;
282288
283289 private static String getValidCharset (final String charset ) {
284290 if (charset != null && charset .length () > 0 )
@@ -287,63 +293,107 @@ private static String getValidCharset(final String charset) {
287293 return CHARSET_UTF8 ;
288294 }
289295
290- private static SSLSocketFactory getPinnedFactory ()
291- throws HttpRequestException {
292- if (PINNED_FACTORY != null ) {
293- return PINNED_FACTORY ;
296+ /**
297+ * Configure SSL cert handling for all future HTTPS connections
298+ *
299+ * @param mode
300+ */
301+ public static void setSSLCertMode (String mode ) {
302+ try {
303+ if (mode == CERT_MODE_TRUSTALL ) {
304+ SOCKET_FACTORY = createSocketFactory (getNoopTrustManagers ());
305+ } else if (mode == CERT_MODE_PINNED ) {
306+ SOCKET_FACTORY = createSocketFactory (getPinnedTrustManagers ());
307+ } else {
308+ SOCKET_FACTORY = null ;
309+ }
310+
311+ CURRENT_CERT_MODE = mode ;
312+ } catch (IOException e ) {
313+ throw new HttpRequestException (e );
314+ }
315+ }
316+
317+ /**
318+ * Configure host name verification for all future HTTPS connections
319+ *
320+ * @param enabled
321+ */
322+ public static void setHostnameVerification (boolean enabled ) {
323+ if (enabled ) {
324+ HOSTNAME_VERIFIER = null ;
294325 } else {
295- IOException e = new IOException ("You must add at least 1 certificate in order to pin to certificates" );
296- throw new HttpRequestException (e );
326+ HOSTNAME_VERIFIER = getTrustedVerifier ();
297327 }
298328 }
299329
300- private static SSLSocketFactory getTrustedFactory ()
301- throws HttpRequestException {
302- if ( TRUSTED_FACTORY == null ) {
303- final TrustManager [] trustAllCerts = new TrustManager [] { new X509TrustManager () {
330+ private static TrustManager [] getPinnedTrustManagers () throws IOException {
331+ if ( PINNED_CERTS == null ) {
332+ throw new IOException ( "You must add at least 1 certificate in order to pin to certificates" );
333+ }
304334
305- public X509Certificate [] getAcceptedIssuers () {
306- return new X509Certificate [0 ];
307- }
335+ try {
336+ String keyStoreType = KeyStore .getDefaultType ();
337+ KeyStore keyStore = KeyStore .getInstance (keyStoreType );
338+ keyStore .load (null , null );
308339
309- public void checkClientTrusted ( X509Certificate [] chain , String authType ) {
310- // Intentionally left blank
311- }
340+ for ( int i = 0 ; i < PINNED_CERTS . size (); i ++ ) {
341+ keyStore . setCertificateEntry ( "CA" + i , PINNED_CERTS . get ( i ));
342+ }
312343
313- public void checkServerTrusted (X509Certificate [] chain , String authType ) {
314- // Intentionally left blank
315- }
316- } };
317- try {
318- SSLContext context = SSLContext .getInstance ("TLS" );
319- context .init (null , trustAllCerts , new SecureRandom ());
344+ // Create a TrustManager that trusts the CAs in our KeyStore
345+ String tmfAlgorithm = TrustManagerFactory .getDefaultAlgorithm ();
346+ TrustManagerFactory tmf = TrustManagerFactory .getInstance (tmfAlgorithm );
347+ tmf .init (keyStore );
320348
321- if (android .os .Build .VERSION .SDK_INT < 20 ) {
322- TRUSTED_FACTORY = new TLSSocketFactory (context );
323- } else {
324- TRUSTED_FACTORY = context .getSocketFactory ();
325- }
326- } catch (GeneralSecurityException e ) {
327- IOException ioException = new IOException (
328- "Security exception configuring SSL context" );
329- ioException .initCause (e );
330- throw new HttpRequestException (ioException );
331- }
349+ return tmf .getTrustManagers ();
350+ } catch (GeneralSecurityException e ) {
351+ IOException ioException = new IOException ("Security exception configuring SSL trust managers" );
352+ ioException .initCause (e );
353+ throw new HttpRequestException (ioException );
332354 }
355+ }
356+
357+ private static TrustManager [] getNoopTrustManagers () {
358+ return new TrustManager [] { new X509TrustManager () {
359+ public X509Certificate [] getAcceptedIssuers () {
360+ return new X509Certificate [0 ];
361+ }
333362
334- return TRUSTED_FACTORY ;
363+ public void checkClientTrusted (X509Certificate [] chain , String authType ) {
364+ // Intentionally left blank
365+ }
366+
367+ public void checkServerTrusted (X509Certificate [] chain , String authType ) {
368+ // Intentionally left blank
369+ }
370+ }};
335371 }
336372
337- private static HostnameVerifier getTrustedVerifier () {
338- if (TRUSTED_VERIFIER == null )
339- TRUSTED_VERIFIER = new HostnameVerifier () {
373+ private static SSLSocketFactory createSocketFactory (TrustManager [] trustManagers )
374+ throws HttpRequestException {
375+ try {
376+ SSLContext context = SSLContext .getInstance ("TLS" );
377+ context .init (null , trustManagers , new SecureRandom ());
340378
341- public boolean verify (String hostname , SSLSession session ) {
342- return true ;
343- }
344- };
379+ if (android .os .Build .VERSION .SDK_INT < 20 ) {
380+ return new TLSSocketFactory (context );
381+ } else {
382+ return context .getSocketFactory ();
383+ }
384+ } catch (GeneralSecurityException e ) {
385+ IOException ioException = new IOException ("Security exception configuring SSL context" );
386+ ioException .initCause (e );
387+ throw new HttpRequestException (ioException );
388+ }
389+ }
345390
346- return TRUSTED_VERIFIER ;
391+ private static HostnameVerifier getTrustedVerifier () {
392+ return new HostnameVerifier () {
393+ public boolean verify (String hostname , SSLSession session ) {
394+ return true ;
395+ }
396+ };
347397 }
348398
349399 private static StringBuilder addPathSeparator (final String baseUrl ,
@@ -453,32 +503,15 @@ public static void setConnectionFactory(final ConnectionFactory connectionFactor
453503 * @throws IOException
454504 */
455505 public static void addCert (Certificate ca ) throws GeneralSecurityException , IOException {
456- if (PINNED_CERTS == null ) {
457- PINNED_CERTS = new ArrayList <Certificate >();
458- }
459- PINNED_CERTS .add (ca );
460- String keyStoreType = KeyStore .getDefaultType ();
461- KeyStore keyStore = KeyStore .getInstance (keyStoreType );
462- keyStore .load (null , null );
463-
464- for (int i = 0 ; i < PINNED_CERTS .size (); i ++) {
465- keyStore .setCertificateEntry ("CA" + i , PINNED_CERTS .get (i ));
466- }
467-
468- // Create a TrustManager that trusts the CAs in our KeyStore
469- String tmfAlgorithm = TrustManagerFactory .getDefaultAlgorithm ();
470- TrustManagerFactory tmf = TrustManagerFactory .getInstance (tmfAlgorithm );
471- tmf .init (keyStore );
506+ if (PINNED_CERTS == null ) {
507+ PINNED_CERTS = new ArrayList <Certificate >();
508+ }
472509
473- // Create an SSLContext that uses our TrustManager
474- SSLContext sslContext = SSLContext .getInstance ("TLS" );
475- sslContext .init (null , tmf .getTrustManagers (), null );
510+ PINNED_CERTS .add (ca );
476511
477- if (android .os .Build .VERSION .SDK_INT < 20 ) {
478- PINNED_FACTORY = new TLSSocketFactory (sslContext );
479- } else {
480- PINNED_FACTORY = sslContext .getSocketFactory ();
481- }
512+ if (CURRENT_CERT_MODE == CERT_MODE_PINNED ) {
513+ SOCKET_FACTORY = createSocketFactory (getPinnedTrustManagers ());
514+ }
482515 }
483516
484517 /**
@@ -1632,6 +1665,7 @@ public HttpRequest(final CharSequence url, final String method)
16321665 throw new HttpRequestException (e );
16331666 }
16341667 this .requestMethod = method ;
1668+ this .setupSecurity ();
16351669 }
16361670
16371671 /**
@@ -1645,6 +1679,23 @@ public HttpRequest(final URL url, final String method)
16451679 throws HttpRequestException {
16461680 this .url = url ;
16471681 this .requestMethod = method ;
1682+ this .setupSecurity ();
1683+ }
1684+
1685+ private void setupSecurity () {
1686+ final HttpURLConnection connection = getConnection ();
1687+
1688+ if (!(connection instanceof HttpsURLConnection )) {
1689+ return ;
1690+ }
1691+
1692+ if (SOCKET_FACTORY != null ) {
1693+ ((HttpsURLConnection ) connection ).setSSLSocketFactory (SOCKET_FACTORY );
1694+ }
1695+
1696+ if (HOSTNAME_VERIFIER != null ) {
1697+ ((HttpsURLConnection ) connection ).setHostnameVerifier (HOSTNAME_VERIFIER );
1698+ }
16481699 }
16491700
16501701 private Proxy createProxy () {
@@ -3351,58 +3402,6 @@ public HttpRequest form(final Map<?, ?> values, final String charset)
33513402 return this ;
33523403 }
33533404
3354- /**
3355- * Configure HTTPS connection to trust only certain certificates
3356- * <p>
3357- * This method throws an exception if the current request is not a HTTPS request
3358- *
3359- * @return this request
3360- * @throws HttpRequestException
3361- */
3362- public HttpRequest pinToCerts () throws HttpRequestException {
3363- final HttpURLConnection connection = getConnection ();
3364- if (connection instanceof HttpsURLConnection ) {
3365- ((HttpsURLConnection ) connection ).setSSLSocketFactory (getPinnedFactory ());
3366- } else {
3367- IOException e = new IOException ("You must use a https url to use ssl pinning" );
3368- throw new HttpRequestException (e );
3369- }
3370- return this ;
3371- }
3372-
3373- /**
3374- * Configure HTTPS connection to trust all certificates
3375- * <p>
3376- * This method does nothing if the current request is not a HTTPS request
3377- *
3378- * @return this request
3379- * @throws HttpRequestException
3380- */
3381- public HttpRequest trustAllCerts () throws HttpRequestException {
3382- final HttpURLConnection connection = getConnection ();
3383- if (connection instanceof HttpsURLConnection )
3384- ((HttpsURLConnection ) connection )
3385- .setSSLSocketFactory (getTrustedFactory ());
3386- return this ;
3387- }
3388-
3389- /**
3390- * Configure HTTPS connection to trust all hosts using a custom
3391- * {@link HostnameVerifier} that always returns <code>true</code> for each
3392- * host verified
3393- * <p>
3394- * This method does nothing if the current request is not a HTTPS request
3395- *
3396- * @return this request
3397- */
3398- public HttpRequest trustAllHosts () {
3399- final HttpURLConnection connection = getConnection ();
3400- if (connection instanceof HttpsURLConnection )
3401- ((HttpsURLConnection ) connection )
3402- .setHostnameVerifier (getTrustedVerifier ());
3403- return this ;
3404- }
3405-
34063405 /**
34073406 * Get the {@link URL} of this request's connection
34083407 *
0 commit comments