@@ -43,7 +43,6 @@ import java.security.cert.X509Certificate
4343
4444
4545const  val  START_VPN_REQUEST  =  123 
46- const  val  START_VPN_REQUEST_NO_CERT  =  124 
4746const  val  INSTALL_CERT_REQUEST  =  456 
4847const  val  SCAN_REQUEST  =  789 
4948const  val  PICK_APPS_REQUEST  =  499 
@@ -343,48 +342,11 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
343342        Log .i(TAG , if  (vpnIntent !=  null ) " got intent"   else  " no intent"  )
344343        val  vpnNotConfigured =  vpnIntent !=  null 
345344
346-         if  (whereIsCertTrusted(config) ==  null  &&  PROMPTED_CERT_SETUP_SUPPORTED ) {
347-             //  The cert isn't trusted, and the VPN may need setup, so there'll be a series of prompts
348-             //  here. Explain them beforehand, so users understand what's going on.
349-             withContext(Dispatchers .Main ) {
350-                 MaterialAlertDialogBuilder (this @MainActivity)
351-                     .setTitle(" Enable interception"  )
352-                     .setIcon(R .drawable.ic_info_circle)
353-                     .setMessage(
354-                         " To intercept traffic from this device, you need to "   + 
355-                         (if  (vpnNotConfigured) " activate HTTP Toolkit's VPN and "   else  " "  ) + 
356-                         " trust your HTTP Toolkit's certificate authority. "   + 
357-                         " \n\n "   + 
358-                         " Please accept the following prompts to allow this."   + 
359-                         if  (! isDeviceSecured(applicationContext))
360-                             " \n\n "   + 
361-                             " Due to Android security requirements, trusting the certificate will "   + 
362-                             " require you to set a PIN, password or pattern for this device." 
363-                         else  "  To trust the certificate, your device PIN will be required." 
364-                     )
365-                     .setPositiveButton(" Ok"  ) { _, _ -> 
366-                         if  (vpnNotConfigured) {
367-                             startActivityForResult(vpnIntent, START_VPN_REQUEST )
368-                         } else  {
369-                             onActivityResult(START_VPN_REQUEST , RESULT_OK , null )
370-                         }
371-                     }
372-                     .setNegativeButton(" Continue without certificate"  ) { _, _ -> 
373-                         if  (vpnNotConfigured) {
374-                             startActivityForResult(vpnIntent, START_VPN_REQUEST_NO_CERT )
375-                         } else  {
376-                             onActivityResult(START_VPN_REQUEST_NO_CERT , RESULT_OK , null )
377-                         }
378-                     }
379-                     .show()
380-             }
381-         } else  if  (vpnNotConfigured) {
382-             //  In this case the VPN needs setup, but the cert is trusted already, so it's
383-             //  a single confirmation. Pretty clear, no need to explain. This happens if the
384-             //  VPN/app was removed from the device in the past, or when using injected system certs.
345+         if  (vpnNotConfigured) {
346+             //  Show the 'Enable the VPN' prompt
385347            startActivityForResult(vpnIntent, START_VPN_REQUEST )
386348        } else  {
387-             //  VPN is trusted & cert setup  already, lets get to it. 
349+             //  VPN is trusted already, continue 
388350            onActivityResult(START_VPN_REQUEST , RESULT_OK , null )
389351        }
390352
@@ -512,7 +474,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
512474        Log .i(TAG , " onActivityResult: "   +  (
513475                when  (requestCode) {
514476                    START_VPN_REQUEST  ->  " start-vpn" 
515-                     START_VPN_REQUEST_NO_CERT  ->  " start-vpn-nocrt" 
516477                    INSTALL_CERT_REQUEST  ->  " install-cert" 
517478                    SCAN_REQUEST  ->  " scan-request" 
518479                    PICK_APPS_REQUEST  ->  " pick-apps" 
@@ -529,9 +490,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
529490            if  (requestCode ==  START_VPN_REQUEST  &&  currentProxyConfig !=  null ) {
530491                Log .i(TAG , " Installing cert..."  )
531492                ensureCertificateTrusted(currentProxyConfig!! )
532-             } else  if  (requestCode ==  START_VPN_REQUEST_NO_CERT  &&  currentProxyConfig !=  null ) {
533-                 Log .i(TAG , " Ignore cert..."  )
534-                 onActivityResult(INSTALL_CERT_REQUEST , RESULT_OK , null )
535493            } else  if  (requestCode ==  INSTALL_CERT_REQUEST ) {
536494                Log .i(TAG  ," Cert installed, checking notification perms..."  )
537495                ensureNotificationsEnabled()
@@ -656,26 +614,56 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
656614        if  (existingTrust ==  null ) {
657615            Log .i(TAG , " Certificate not trusted, prompting to install"  )
658616
659-             if  (Build .VERSION .SDK_INT  >=  Build .VERSION_CODES .R ) {
660-                 //  Android 11+, with no trusted cert: we need to download the cert to Downloads and
661-                 //  then tell the user how to install it manually:
662-                 launch { promptToManuallyInstallCert(proxyConfig.certificate) }
663-             } else  {
617+             if  (PROMPTED_CERT_SETUP_SUPPORTED ) {
664618                //  Up until Android 11, we can prompt the user to install the CA cert into the user
665619                //  CA store. Notably, if the cert is already installed as a system cert but
666620                //  disabled, this will get triggered, and will enable the cert, rather than adding
667621                //  a normal user cert.
668-                 val  certInstallIntent =  KeyChain .createInstallIntent()
669-                 certInstallIntent.putExtra(EXTRA_NAME , " HTTP Toolkit CA"  )
670-                 certInstallIntent.putExtra(EXTRA_CERTIFICATE , proxyConfig.certificate.encoded)
671-                 startActivityForResult(certInstallIntent, INSTALL_CERT_REQUEST )
622+                 launch { promptToAutoInstallCert(proxyConfig.certificate) }
623+             } else  {
624+                 //  Android 11+, with no trusted cert: we need to download the cert to Downloads and
625+                 //  then tell the user how to install it manually:
626+                 launch { promptToManuallyInstallCert(proxyConfig.certificate) }
672627            }
673628        } else  {
674629            Log .i(TAG , " Certificate already trusted, continuing"  )
675630            onActivityResult(INSTALL_CERT_REQUEST , RESULT_OK , null )
676631        }
677632    }
678633
634+     private  suspend  fun  promptToAutoInstallCert (certificate :  Certificate ) {
635+         withContext(Dispatchers .Main ) {
636+             MaterialAlertDialogBuilder (this @MainActivity)
637+                 .setTitle(" Enable HTTPS interception"  )
638+                 .setIcon(R .drawable.ic_info_circle)
639+                 .setMessage(
640+                     " To intercept HTTPS traffic from this device, you need to "   + 
641+                     " trust your HTTP Toolkit's certificate authority. "   + 
642+                     " \n\n "   + 
643+                     " Please accept the following prompts to allow this."   + 
644+                     if  (! isDeviceSecured(applicationContext))
645+                         " \n\n "   + 
646+                         " Due to Android security requirements, trusting the certificate will "   + 
647+                         " require you to set a PIN, password or pattern for this device." 
648+                     else  "  To trust the certificate, your device PIN will be required." 
649+                 )
650+                 .setPositiveButton(" Install"  ) { _, _ -> 
651+                     val  certInstallIntent =  KeyChain .createInstallIntent()
652+                     certInstallIntent.putExtra(EXTRA_NAME , " HTTP Toolkit CA"  )
653+                     certInstallIntent.putExtra(EXTRA_CERTIFICATE , certificate.encoded)
654+                     startActivityForResult(certInstallIntent, INSTALL_CERT_REQUEST )
655+                 }
656+                 .setNeutralButton(" Skip"  ) { _, _ -> 
657+                     onActivityResult(INSTALL_CERT_REQUEST , RESULT_OK , null )
658+                 }
659+                 .setNegativeButton(" Cancel"  ) { _, _ -> 
660+                     disconnect()
661+                 }
662+                 .setCancelable(false )
663+                 .show()
664+         }
665+     }
666+ 
679667    @RequiresApi(Build .VERSION_CODES .Q )
680668    private  suspend  fun  promptToManuallyInstallCert (cert :  Certificate , repeatPrompt :  Boolean  = false) {
681669        if  (! repeatPrompt) {
@@ -713,7 +701,12 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
713701                    Html .fromHtml(
714702                    """ 
715703                        <p> 
716-                             Android ${Build .VERSION .RELEASE }  doesn't allow automatic certificate setup. 
704+                             ${  
705+                                 if  (PROMPTED_CERT_SETUP_SUPPORTED ) 
706+                                     " Automatic certificate installation failed, so it must be done manually."  
707+                                 else  
708+                                     " Android ${Build .VERSION .RELEASE }  doesn't allow automatic certificate setup."  
709+                             } 
717710                        </p> 
718711                        <p> 
719712                            To allow HTTP Toolkit to intercept HTTPS traffic: 
@@ -740,6 +733,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
740733                .setPositiveButton(" Open security settings"  ) { _, _ -> 
741734                    startActivityForResult(Intent (Settings .ACTION_SECURITY_SETTINGS ), INSTALL_CERT_REQUEST )
742735                }
736+                 .setNeutralButton(" Skip"  ) { _, _ -> 
737+                     onActivityResult(INSTALL_CERT_REQUEST , RESULT_OK , null )
738+                 }
743739                .setNegativeButton(" Cancel"  ) { _, _ -> 
744740                    disconnect()
745741                }
0 commit comments