martinstoeckli / SilentNotes

SilentNotes is a simple note taking app which respects your privacy.
https://www.martinstoeckli.ch/silentnotes
Mozilla Public License 2.0
212 stars 35 forks source link

Cannot connect to NextCloud with LetsEncrypt SSL certificate #111

Closed martinstoeckli closed 1 month ago

martinstoeckli commented 2 years ago

I got the report that SilentNotes on Android devices cannot connect to NextCloud instances anymore, even when they worked before and no settings have changed meanwhile (Windows installations are not affected). After some debugging and research I found out that this is caused by the LetsEncrypt SSL certificate, for an indepth explanation see: https://github.com/xamarin/xamarin-android/issues/6351#issuecomment-945646088

In short: Since September 29th 2021 the "DST Root CA X3" certificate expired, it was used by LetsEncrypt, so older devices/os/libraries which do not know about LetsEncrypt's own root certificate, can validate the certificate anyway.

Workaround: A workaround was implemenented, so that NextCloud users can connect to their NextCloud instance, even if the issue can currently not be solved in a satisfying way (scroll down to https://github.com/martinstoeckli/SilentNotes/issues/111#issuecomment-1095028982 for more information). WebDav connections now have a new option to accept invalid certificates, this allows to connect to NextCloud and /e/ cloud services. Unsafe SSL connections are not recommended, but since the content is always end-to-end encrypted anyway, it is justifiable.

martinstoeckli commented 2 years ago

There is a workaround which can be used, but it is not a final solution and cannot be built in into SilentNotes. You can do it on your own risk, should you encounter problems in other apps, it is possible to reactivate it any time.

Navigate to the Android settings: Settings/Security & location/Advanced/Encryption & Credentials/Trusted credentials

Deactivate this old root certificate: Digital Signature Trust Co.

Screenshot_1636365504

martinstoeckli commented 2 years ago

Maybe the problem can be overcome by switching the HttpClient to the native Android implementation: https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/http-stack?tabs=windows , this will be considered for the next version of SilentNotes.

A quick test with an ecloud (nextcloud of the /e/ foundation) still gave this error, so switching the option in the Android project doesn't seem to switch the used implementation:

Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/boringssl/ssl/handshake_client.c:1132

martinstoeckli commented 2 years ago

Switching to the native Android implementation indeed solves the problem with the SSL certificates, unfortunately the Java implementation of Android does not support the http method "PROPFIND", which is required by WebDav requests. So either we use the default HttpClient implementation (boring-ssl library) which cannot handle the expired SSL certificate, or we use the native Java HttpClient implementation, which cannot send "PROPFIND" requests.

The code below shows a possible native Android implementation, which raises the exception:

Java.Net.ProtocolException: Expected one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PATCH] but was PROPFIND

public class SilentNotes.Android.Startup
{
    public static void InitializeApplication(Activity rootActivity, ActivityResultAwaiter rootActivityResultWaiter)
    {
        ...
        ICloudStorageClientFactory cloudStorageClientFactory = Ioc.GetOrCreate<ICloudStorageClientFactory>();
        cloudStorageClientFactory.RegisterHttpMessageHandlerFactory(() => new Xamarin.Android.Net.AndroidClientHandler());
    }
}

public class CloudStorageClientFactory : ICloudStorageClientFactory
{
    public void RegisterHttpMessageHandlerFactory(Func<HttpMessageHandler> factory)
    {
        FlurlHttp.Configure(config => config.HttpClientFactory = new PlatformHttpClientFactory(factory));
    }

    public class PlatformHttpClientFactory : DefaultHttpClientFactory
    {
        private readonly Func<HttpMessageHandler> _factory;

        public PlatformHttpClientFactory(Func<HttpMessageHandler> factory)
        {
            _factory = factory;
        }

        public override HttpMessageHandler CreateMessageHandler()
        {
            return _factory();
        }
    }
}

See also: https://github.com/skazantsev/WebDavClient/issues/59 . The circumvention with the X-HTTP-Method-Override property didn't work neither, because the server doesn't accept it.

martinstoeckli commented 2 years ago

A workaround was built in, but the issue is kept open, for when the development environment allows to solve the issue properly.

pkoevesdi commented 2 years ago

I couldn't connect via SSL to my nextcloud server too. A generic Apache WebDAV server works, only with either of the builtin workaround or the workaround here. I'm stuck because I don't get any meaningful error message, see #174. Both are Let's Encrypt SSLed. What URL do I have to set for nextcloud? the main URL, usually example.com/nextcloud?

martinstoeckli commented 2 years ago

@pkoevesdi - Instead of the workaround it should now be possible to tick the option "Accept unsafe certificates".

It depends on your configuration what the url looks like, to get more information you can setup a WebDav connection with Windows Explorer (not Internet Explorer) if you have this OS available. The same credentials should work with SilentNotes too.

pkoevesdi commented 2 years ago

I'm aware of the "Accept unsafe certificates", which was also classified as a workaround here. But as said, on my generic webdav both workarounds work, on my nextcloud none.

So, I seem to have a different problem there, sorry for looking for support here, maybe, with the knowledge now, it shows up as the wrong place.

I can connect to nextcloud via webdav, which is the url example.com/nextcloud/remote.php/dav/files/USERNAME/ But there are also apps out there, which connect to example.com/nextcloud and then offer me the nextcloud content to choose the location from. Of which type is silentnotes? If of first type (connecting to the long webdav-URL), as I assume from Your last comment, then, what is the difference between choosing "webdav" or "nextcloud" connection?

martinstoeckli commented 2 years ago

On a test account I had to use an url of this form: https://example.com/nextcloud/remote.php/dav/files/USERNAME maybe in your case it could be just http:// .

There is no diference, the "Nextcloud WebDav" connection was only added to make it more recognizeable, so a user can find it easier.

If the problem persists and you can access the path with other tools, I would be interested in a test account, I would then try to debug the problem.

pkoevesdi commented 2 years ago

On a test account I had to use an url of this form: https://example.com/nextcloud/remote.php/dav/files/USERNAME maybe in your case it could be just http:// .

Nope, I rewrite http to https on the server. But it works, now that I have the correct URL. :-) In my Tests my URL was most of the time malformed, and if not, the workarounds were switched off. Ok, I use it with the first mentioned workaround, which I consider more secure than the second, built-in one?

There is no diference, the "Nextcloud WebDav" connection was only added to make it more recognizeable, so a user can find it easier.

Ah, ok. See, these two menu points lead me to the assumption, that I can give the main nexcloud URL example.com/nextcloud there (as in other nextcloud-supporting apps), in opposite to "Webdav", where I have to put the direct Webdav-URL. From my side, I'd suggest rather make a single menu point and maybe name it "webdav/nextcloud". But, just my humble opinion. Also, it could be helpful to mention an example nextcloud URL https://example.com/nextcloud/remote.php/dav/files/USERNAME/FOLDER in the credentials dialog.

But, anyway, thanks for the help and the project!

martinstoeckli commented 11 months ago

Workaround: On Android the default HttpClientHandler uses the underlying Java class "HttpURLConnection", which unfortunately cannot handle the http method "PROPFIND".

So when running on Android we make an expection for this single request and use the "SocketsHttpHandler" which has its own implementation, though it cannot validate SSL certificates from LetsEncrypt. We can safely ignore the validation, because consecutive calls will do the validation with the Android implementation.

For lower versions of Android (e.g. v7), the "accept unsafe certificates" will still be available, so users can connect to WebDav servers with a certificate which is not yet known to this Android version.

martinstoeckli commented 1 month ago

Finally solved this issue in version 8.0.0, see description above.