0

I have a small Android app that connects to a web app via VPN.

The app displays a wizard to type in and save all the credentials for the web app (domain, port, http/s, user and password). When the user saves, I check if the URL can be reached or not and display a warning or move to the next activity, containing a WebView.

This is what happens when the user taps on Save:

fun triggerSaveSettingsAction()
{
    Log.i("MyApp","ServerWizardActivity > triggerSaveSettingsAction")

    if(checkInputData() == false)
    {
        displayDialogBox("WARNING", message = "Please fill in all the fields in order to continue!")
    }
    else
    {
        val serverAddress: String = tvServerAddress?.text?.replace("\\s".toRegex(), "").toString()
        val serverPort: String = tvServerPort?.text?.replace("\\s".toRegex(), "").toString()
        val serverUser: String = tvServerUser?.text?.replace("\\s".toRegex(), "").toString()
        val serverPassword: String = tvServerPassword?.text?.replace("\\s".toRegex(), "").toString()
        val isSecureServer: String = (cbSecureServer?.isChecked == true).toString()

        setProgressBarVisible()

        checkURLResponse(serverAddress, serverPort, isSecureServer)

        Timer("SettingUp", false).schedule(3000) // Nasty workaround to prevent setProgressBarInvisible to early
        {
            if(isReachable!!)
            {
                sharedPref!!.saveDataToSharedPreferences("serverAddress", serverAddress)
                sharedPref!!.saveDataToSharedPreferences("serverPort", serverPort)
                sharedPref!!.saveDataToSharedPreferences("serverUser", serverUser)
                sharedPref!!.saveDataToSharedPreferences("serverPassword", serverPassword)
                sharedPref!!.saveDataToSharedPreferences("isSecureServer", isSecureServer)

                runOnUiThread{
                    setProgressBarInvisible()
                    displayDialogBox("NOTIFICATION", "The new settings will become availiable the next time you start the application!")
                }
            }
            else
            {
                runOnUiThread{
                    setProgressBarInvisible()
                    displayDialogBox("WARNING", "The server address or port are not valid or there is a problem with your Internet connection!")
                }
            }
        }
    }
}

And here is checkURLResponse(serverAddress, serverPort, isSecureServer):

fun checkURLResponse(serverAddress: String, serverPort: String, isSecureServer: String)
{
    Log.i("MyApp","ServerWizardActivity > checkURLResponse")

    var fullURL: String? = null
    val partialURL: String? = "/main.html"

    if(isSecureServer.toBoolean()) { fullURL = "https://$serverAddress:$serverPort${partialURL}" }
    else { fullURL = "http://$serverAddress:$serverPort${partialURL}" }

    Thread{
        Log.i("MyApp","ServerWizardActivity > Thread")

        try
        {
            val url = URL(getPartialURL())
            val urlc = url.openConnection() as HttpURLConnection
            urlc.connectTimeout = 3 * 1000
            urlc.connect()

            if ((urlc.responseCode >= 200) && (urlc.responseCode <= 299))
            {
                Log.i("Connection", "Try > Success!")
                isReachable = true
            }
            else
            {
                Log.i("Connection", "Try > Failed!")
            }
        }
        catch (e: Exception)
        {
            Log.i("Connection", "Catch > Failed!")
        }
    }.start()
}

Leaving aside the nasty workaround, this works fine with other domains that don't require a VPN connection.

In this case, I get this:

I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false

E/Conscrypt: ------------------Untrusted chain: ----------------------
    == Chain0 == 
     Version: 3
     Serial Number: [Serial Number Here]
E/Conscrypt: SubjectDN: CN=, OU=, O=, C=
E/Conscrypt: IssuerDN: CN=, OU=, O=, C=
E/Conscrypt: Get not before:   Fri Mar 19 15:46:56 GMT+02:00 2021
E/Conscrypt: Get not after:   Sat Mar 19 15:46:56 GMT+02:00 2022
     Sig ALG name: [Serial Number Here]
E/Conscrypt: Signature: [Sig ALG Name Here]
E/Conscrypt: Public key: [Public Key Here]
    == Chain1 == 
     Version: 3
     Serial Number: 9
E/Conscrypt: SubjectDN: CN=, OU=, O=, C=
E/Conscrypt: IssuerDN: CN=, OU=, O=, C=
E/Conscrypt: Get not before:   Wed Mar 23 17:45:49 GMT+02:00 2016
     Get not after: Mon Mar 23 17:45:49 GMT+02:00 2026
E/Conscrypt: Sig ALG name: [Sig ALG Name Here]
E/Conscrypt: Signature: [Signature Here]
E/Conscrypt: Public key: [Public Key Here]

I/Connection: Catch > Failed!
Catch > javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.!
I/MyApp: ServerWizardActivity > setProgressBarInvisible
I/MyApp: ServerWizardActivity > displayDialogBox
D/ScrollView: initGoToTop
D/ViewRootImpl@64e7312[ServerWizardActivity]: setView = DecorView@862aae3[] TM=true MM=false
D/ViewRootImpl@64e7312[ServerWizardActivity]: dispatchAttachedToWindow
V/Surface: sf_framedrop debug : 0x4f4c, game : false, logging : 0
D/ViewRootImpl@64e7312[ServerWizardActivity]: Relayout returned: old=[0,0][0,0] new=[27,659][1053,1261] result=0x7 surface={valid=true 530991779840} changed=true
D/mali_winsys: EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, egl_color_buffer_format *, EGLBoolean) returns 0x3000,  [1218x794]-format:1
D/OpenGLRenderer: eglCreateWindowSurface = 0x7bbabaf0b0
D/ScrollView:  onsize change changed 
D/ViewRootImpl@64e7312[ServerWizardActivity]: MSG_WINDOW_FOCUS_CHANGED 1
D/ViewRootImpl@64e7312[ServerWizardActivity]: MSG_RESIZED_REPORT: frame=Rect(27, 659 - 1053, 1261) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl@9d719a3[ServerWizardActivity]: MSG_WINDOW_FOCUS_CHANGED 0

The iOS version works fine with the same credentials and VPN. Also, the URL is reachable in any browser.

Not sure if this is a phone related issue or a connection issue, since I have only one phone to test on (Samsung Galaxy S7 with Android 8.0).

I found a few threads claiming this is a Samsung-only issue but i cannot verify it. Besides, it looks more like a certificates issue to me. But that doesn't explain why does it work in browser and in the iOS app.

Any ideas?

LE:

It seems it is a certificate error after all - SEC_ERROR_UNKNOWN_ISSUER. I managed to bypass it using handler.proceed():

override fun onPageStarted(view: WebView?, url: String, favicon: Bitmap?)
{
    Log.i("MyApp","MainActivity > onPageStarted")

    super.onPageStarted(view, url, favicon)

    val currentURL: String? = wvMainView!!.url
    Log.i("MyApp","MainActivity > onPageStarted > ${currentURL}")

    view?.setWebViewClient(object : WebViewClient()
    {
        override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError)
        {
            Log.i("MyApp","MainActivity > shouldOverrideUrlLoading > ${error}")
            Log.i("MyApp","MainActivity > onReceivedSslError > currentURL > ${currentURL!!}")

            handler.proceed()
        }
    })
}

The problem is, in onPageStarted and onPageFinished I am performing some actions, based on the current URL. Basically, I have a lot of ifs like this: if(currentURL!!.contains("main.html")).

Unfortunately, now it seems that currentURL always returns the same URL, the initial URL. No matter what I do inside the web app, it returns the initial URL.

Moreover, if I try to use it with web apps that have no certificate errors, it fails to load.

daydr3am3r
  • 860
  • 3
  • 12
  • 28

0 Answers0