amugofjava / anytime_podcast_player

Simple, easy to use Podcast player app written in Flutter and Dart.
BSD 3-Clause "New" or "Revised" License
403 stars 101 forks source link

Podcasts fail to load on Android 6 devices if the SSL CA is Let's Encrypt #63

Closed amugofjava closed 2 years ago

amugofjava commented 2 years ago

Describe the bug Podcasts that use Let's Encrypt as their CA fail to load.

To Reproduce Steps to reproduce the behavior:

  1. Search for the 'Once Bitten' podcast.
  2. Tap on the icon to load the podcast.
  3. Busy spinner keeps on spinning,

Issue The Let's Encrypt CA expired at the end of September 2021. This is causing issues with older Android devices which cannot update it's list of CA's. Later versions of Android are fine.

Chralu commented 2 years ago

Hi @amugofjava ,

do you have a solution in mind yet ?

Just in case : On another project, adding the ISRG Root X1 certificate as trusted on runtime worked fine.

amugofjava commented 2 years ago

Hi @Chralu,

There looks to be a couple of ways of doing it. You can call HttpOverrides.global, set your own implementation and then handle the cert error callback. Not ideal though.

The second is to add the updated CA to the trusted list which is what I am currently looking at.

How did you add the ISRG Root X1 certificate to your project?

amugofjava commented 2 years ago

Adding the Let's Encrypt certificate to the chain seems to work.

Chralu commented 2 years ago

Download the cert

You can download the ISRG Root X1 certificate here. I used the self-signed PEM.

Add it to your assets

Declare the certificate as trusted in the SecurityContext

Setup default SecurityContext

I never tested this, but it seems pretty clean to me.

    final cerFile = await rootBundle.load("assets/isrg-root-x1.pem");
    SecurityContext.defaultContext.setTrustedCertificatesBytes(
      cerFile.buffer.asUint8List(),
    );

Setup a SecurityContext injected in third party libs

This is the way I used previously. The way to inject SecurityContext depends on the HTTP lib used.

    final securityContext = SecurityContext();
    final cerFile = await rootBundle.load("assets/isrg-root-x1.pem");
    securityContext.setTrustedCertificatesBytes(
      cerFile.buffer.asUint8List(),
    );

    // Inject securityContext in the HTTP lib/SDK
Chralu commented 2 years ago

Damn, you were faster than me XD

amugofjava commented 2 years ago

Thanks @Chralu, that backs up my thoughts on the solution. It does seem to work; however, I have to set the context just before the first time I use it. If I try and set that up in the constructor of my API class it fails. Odd, but hopefully I'll get to the bottom of it. Maybe at construction time the context is not ready.

Chralu commented 2 years ago

It might be because of the constructor being sync, so it ends before the await rootBundle.load(...) is done.

You might have to add a Future init(){} method to the API class, and call it during app startup.

amugofjava commented 2 years ago

Fixed in latest feature branch. Testing before merging into master.