Skip to content

[FIX] SSL handling#4861

Draft
jesmrec wants to merge 2 commits into
masterfrom
fix/ssl_handling
Draft

[FIX] SSL handling#4861
jesmrec wants to merge 2 commits into
masterfrom
fix/ssl_handling

Conversation

@jesmrec
Copy link
Copy Markdown
Contributor

@jesmrec jesmrec commented May 19, 2026

From

opencloud-eu/android#121
opencloud-eu/android#126

  • Host is now correctly verified with a KnownServersHostnameVerifier that checks if:

    • Host certificate is "acceptable" through the network library
        if (mDelegate.verify(hostname, session)) {
            return true;
        }
      
    • If not, it checks if host certificate was previously accepted and exists in the certificate store
       return NetworkUtils.isCertInKnownServersStore(peerCerts[0], mContext);
      
  • If a SslPeerUnverifiedException raises, it is enclosed in a CertificateCombinedException to be handled separately from a generic SSLException

     CertificateCombinedException sslPeerUnverifiedException = new CertificateCombinedException(lastCert);
     sslPeerUnverifiedException.setSslPeerUnverifiedException((SSLPeerUnverifiedException) e);
    
  • When SslPeerUnverifiedException raises, the certificate accepting dialog turns into an alert

       if (m509Certificate == null) {
           ok.setText(android.R.string.ok);
           mView.findViewById(R.id.question).setVisibility(View.GONE);
           cancel.setVisibility(View.GONE);
       }
    
  • Certificate and connection are aborted

     ((OnSslUntrustedCertListener) getActivity()).onCancelCertificate();
    

Related Issues

App:

  • Add changelog files for the fixed issues in folder changelog/unreleased. More info here
  • Add feature to Release Notes in ReleaseNotesViewModel.kt creating a new ReleaseNote() with String resources (if required)

QA

@jesmrec jesmrec added this to the 4.8.1 - Current milestone May 19, 2026
@jesmrec jesmrec changed the title [FIX] ssl handling [FIX] SSL handling May 19, 2026
@jesmrec jesmrec force-pushed the fix/ssl_handling branch from af2e071 to 7f30c21 Compare May 19, 2026 12:25
@jesmrec jesmrec force-pushed the fix/ssl_handling branch from 7f30c21 to 5e3a5f8 Compare May 20, 2026 07:48
@jesmrec
Copy link
Copy Markdown
Contributor Author

jesmrec commented May 20, 2026

Some checks done with badssl.com

Test case URL Checkpoints Expected result Comments
Valid https flow https://badssl.com/ 1. KnownServersHostnameVerifier.verify(...)
2. mDelegate.verify(hostname, session)
3. Final result of verify(...)
1. Reached ✅
2. mDelegate.verify(...) == true
3. KnownServersHostnameVerifier.verify(...) == true
3. No fallback to isCertInKnownServersStore(...)
3. No SSL dialog ✅
The badssl.com is intentionally valid and is used as the valid https case.
Hostname not verified https://wrong.host.badssl.com/ 1. mDelegate.verify(hostname, session)
2. NetworkUtils.isCertInKnownServersStore(...)
3. getCertificateAlias(cert)
4. e instanceof SSLPeerUnverifiedException
5. mException / mCode
6. SslUntrustedCertDialog.m509Certificate
7. Final button action
1. mDelegate.verify(...) == false
2. isCertInKnownServersStore(...) == false
3. getCertificateAlias(cert) == null
4. e instanceof SSLPeerUnverifiedException == true
5. mException instanceof CertificateCombinedException
5. mCode == SSL_RECOVERABLE_PEER_UNVERIFIED
6. m509Certificate == null
6. Dialog shows only OK
7. Pressing OK calls onCancelCertificate()
This is the main scenario. The certificate does not cover wrong.host.badssl.com, so the app must not allow the user to save/trust it from the dialog and it is aborted.
Untrusted certificate, hostname correct https://self-signed.badssl.com/ 1. mDelegate.verify(hostname, session)
2. e instanceof SSLPeerUnverifiedException
3. SslUntrustedCertDialog.m509Certificate
4. Final button action if user accepts
1. mDelegate.verify(...) == true
1. No fallback to isCertInKnownServersStore(...) from hostname verifier ✅
2. e instanceof SSLPeerUnverifiedException == false
3. m509Certificate != null
4. If user accepts: addCertToKnownServersStore(...) and onSavedCertificate() is called ✅
The hostname is correct, but the certificate is self-signed. This should be treated as a classic trust problem, not as a peer/hostname verification failure.
Certificate validity error, hostname correct https://expired.badssl.com/ 1. mDelegate.verify(hostname, session)
2. e instanceof SSLPeerUnverifiedException
3. SslUntrustedCertDialog.m509Certificate
4. UI Dialog
1. mDelegate.verify(...) == true
1. No fallback to isCertInKnownServersStore(...) from hostname verifier ✅
2. e instanceof SSLPeerUnverifiedException == false
3. m509Certificate != null
4. Dialog to continue with connection ✅
The hostname matches, but the certificate is expired. This validates that certificate validity errors are not confused with peer/hostname failures.
Untrusted root CA, hostname correct https://untrusted-root.badssl.com/ 1. mDelegate.verify(hostname, session)
2. e instanceof SSLPeerUnverifiedException
3. SslUntrustedCertDialog.m509Certificate
4. UI dialog
1. mDelegate.verify(...) == true
1. No fallback to isCertInKnownServersStore(...) from hostname verifier ✅
2. e instanceof SSLPeerUnverifiedException == false
3. m509Certificate != null
4. Dialog to continue with connection ✅
The hostname matches, but the root CA is not trusted. This validates that trust-chain errors are not confused with peer/hostname failures.
Missing intermediate certificate, hostname correct https://incomplete-chain.badssl.com/ 1. mDelegate.verify(hostname, session)
2. e instanceof SSLPeerUnverifiedException
3. SslUntrustedCertDialog.m509Certificate
4. UI dialog
1. mDelegate.verify(...) == true
1. No fallback to isCertInKnownServersStore(...) from hostname verifier ✅
2. e instanceof SSLPeerUnverifiedException == false
3. If dialog appears: m509Certificate != null
4. Dialog to continue with connection ✅
The hostname matches, but the certificate chain is incomplete. This validates that chain-building errors are not confused with peer/hostname failures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant