Closed clem844 closed 4 years ago
Ok, I just had to RTFM
Self-signed certificates are not supported at the moment. Right now, the server certificate has to be signed by a certificate authority (CA). This CA has to be public and trusted by Android.
Does this mean I HAVE to open my home assistant to the outside world?
Hey, maybe you can use HTTP in your home network?
I think I set it up because Grocy doesn't allow to scan barcodes with http... I would be surprised if it works without security with the app
Does this mean I HAVE to open my home assistant to the outside world?
At the moment, our app cannot use self-signed certificates, because it needs a bit work and investigation (for me) to implement support for this. I'm sorry for that – currently I have even less time to work on that.
I would be surprised if it works without security with the app
Our app supports HTTP, because some people asked for that.
Ok no worries, thanks for your quick answer! 👍
You're welcome!
Hey there, I've just found this awsome project and have the same issue as you described here.
My setup is an internal network with my own CA installed on a couple of home devices. I run Grocy under an internal nginx proxy that listens only on https. It proxies requests to a docker network unaccessible to the rest of the network.
There is an official way for Android to support user Certificate Authority but since some newer version of Android it has to be explicitly allowed by the app developer...
There is an already implemented solution over at iNPUTmice/Conversations here on github.
The android manifest file must contain reference to a networkSecurityConfig file:
<application
...
android:networkSecurityConfig="@xml/network_security_configuration"
...>
And this file's content will explicitly say that it looks up certificates not only in the system store but also in the user one: network_security_configuration.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
By adding this to the manifest the app will lookup both certificate stores when doing TLS validation. Then any certificate signed by a User trused CA will be considered valid.
If you want I can add a PR with these changes.
Thanks for considering.
Hi, thanks for this solution! I thought that I have to implement the certificate picker which is in Settings of Conversations or DAVx5. :-) I will look if it is working for me today or tomorrow and then notifiy you here.
Thanks again!
The certificate picker should be useful only if the app wants to mange the user certificates, but I don't think it's the case.
If you need help to test this setup, give me a ping and I can run a test apk.
Thanks again for this really cool project! :).
Mmh, in my test I cannot connect with my server (with a self-signed cert which is added to user certs) because there is a SSLPeerUnverifiedException in volley. On StackOverflow or here are solutions to ignore this Peer Verification but I think this sounds a bit insecure because it allows MITM attacks. Did it work for you without this exception?
You are right I would not disable verification either.
In my setup and the one where this change work on other projects, I create a self-signed CA with the proper CA flags and import that to the User Trusted entities. Then the endpoint the app tries to connect to presents a server certificate signed by this user trused CA.
The idea behind this is that for private networks you can't really use Let's Encrypt to verify your internal network hostnames as they are not accesible via public NS resolution so their servers can't generate a valid certificate for your local network. So you are stuck with self-signed CA that is added as trusted to the internal devices.
I think the requirement here is that the server where Grocy is running needs to have in the SAN list the hostname where it is accessible and some flags that it's a server certificate.
Do you happen to have an apk to test? I don't have an android build setup right now, I've used to f-droid version so far.
Or a larger stacktrace I can help with debugging?
I will try to setup some android build system in the these days to help with this change.
EDIT: Oh and yes, users should consider the security implications of trusting custom CAs on their devices.
I would argue that it's the user's choice what to trust and the app should consider valid anything that the user trusts on their android system ;).
I did a quick build with gradlew (btw cool tool), and added the changes I suggested above, and I was able to connect to my local running server with the setup I described above.
If it makes sense, I can do a fork and push my changes with a PR, but the changes are just adding the network xml file and referencing it in AndroidManifest.
I'll add also the app-debug.apk file with the changes inside it, but probably it's safer to just use the apk you build on your own.
This is my git diff HEAD
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cb9cca57..75180c8b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
android:theme="@style/Theme.Grocy"
android:hardwareAccelerated="true"
android:usesCleartextTraffic="true"
+ android:networkSecurityConfig="@xml/network_security_configuration"
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
<activity
diff --git a/app/src/main/res/xml/network_security_configuration.xml b/app/src/main/res/xml/network_security_configuration.xml
new file mode 100644
index 00000000..02ecb348
--- /dev/null
+++ b/app/src/main/res/xml/network_security_configuration.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ <certificates src="system" />
+ <certificates src="user" />
+ </trust-anchors>
+ </base-config>
+</network-security-config>
\ No newline at end of file
Oh great, thanks for your assistance!
I have exactly the same changes on my codebase, so maybe I entered not all required infos in the openssl
command...
My command is the following:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
And I entered these infos:
Country Name (2 letter code) [XX]:DE
State or Province Name (full name) []:.
Locality Name (eg, city) [Default City]:.
Organization Name (eg, company) [Default Company Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (eg, your name or your server's hostname) []:grocy.myserver.com
Email Address []:.
So I only wrote DE and my hostname (if it's a remote hostname or a local IP shouldn't matter)... Did you enter more than that infos?
Or maybe another source for the difference between my and your setup: What's your Android Version on your phone? Mine is Android 10, maybe in older versions there is different behavior.
Oh, I see you are trying a self-signed cert for your server. The usecase I describe is a bit different. The selfsigned certificate is the CA (with some special flags when generating) and that signs the server certificate exposed by Grocy.
The CA is imported in android (for me version 9).
I actually use the Certificate Manager of pfsense to generate everything, but let me lookup a bit the openssl commands so you can try the same usecase.
This article seems to describe pretty well the generation of a CA and signing of a server certificate: How To Create CA and Generate SSL/TLS Certificates & Keys.
This is the same the initial description of the current issue.
It is not just a simple self-signed certificate but a full recommented PKI scenario with the only difference to Let's Encript (or other commercial signing methods) that the trusted authority is not a default System one provided by Android but a manual trusted authority imported by the user.
Hope this helps, I can go more in detail about how PKI works.
There is also this nice article that describes in more detail how such a setup works: OpenSSL create certificate chain with Root & Intermediate CA
Thanks a lot, I will try it tomorrow. :-)
Mmh, I'm struggling with that... I've tried it for almost five hours with my remote server (with domain) and with a local Raspberry Pi (with this cool tutorial).
I've followed the tutorial which you recommend to me (and others I found), and in Firefox it's always working after I imported the Root CA into it (it displays a secure connection then). But with this app here – with the few changes – I always get an error from Volley:
requestLogin: VolleyError: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
The bad is that there are multiple sources where it can occur. And I don't know what I am doing wrong – despite the fact that it's working in Firefox. (And yes, I always have imported it in Android, too)
Maybe you know what I can try next?
If you cannot help any further, I could commit these lines of code. But I still want to know what I am doing wrong in my setup... :-) I just saw this commit in a random app, and it's the same like you proposed and like in Conversations. So I think it's approved that this will fix the issue here.
What do you think?
No worries, I can try to help you debug it further. It should be working on your environment in the first place. I will try to test a setup on my own with certificates generated via openssl and come back to you with a result.
Do you connect directly to a Grocy instance or are you using some sorth of proxy in front?
I use a docker image from linixserver.io with an nginx in front, but the TLS part is the same no matter the setup.
I'm using a normal nginx server for Grocy, so no proxy is between me and the server. On my Raspi there is also no proxy between me and Apache.
Ah... found the issue. The guides I've sent miss an important x509 extension when creating the CSR for this usecase.
I've generated a certificate using the openssl commands from the first guide I sent you and compared it to the one generated by my pfSense certmanager and managed to see the difference. The ones generated from the inital guide also did not worked on my dev-built apk.
You need to make sure that the server certificate has defined:
Key Usage
Usages: Digital signature
Key encipherment
Critical: No
...
Extended Key Usage
Allowed Purposes: Server Authentication
Client Authentication
1.3.6.1.5.5.8.2.2
Critical: No
This is not defined with the CSR from the guides above. You need the following CSR.conf file when signing (or just use a certificate manager and make sure to generate a "server certificate"):
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $DOMAIN
As it is described here: How to Create Your Own SSL Certificate Authority for Local HTTPS Development
So in the end the issue with your environment was that the server's public certificate was not "allowed" to be a server certificate from Android's point of view.
Hope with this you will be able to have a working instance as well.
And thanks again for taking into accout this usecase in your application. Appreciate it!
Wow, thanks for your efforts! I will test it immediately...
Mmh, same error as before... I will simply commit the changes you proposed in the beginning because I know that it's working for you. :-)
I just saw that there is another issue #15 open with the same topic, so I will close this one again. This way the author of the old issue will be notified too, if it is implemented. So please use #15 for further discussion.
@murizorun Thanks again for your help! The code change will be released with v1.9.1
.
Oh well, sorry it did not work out for you.
These are the commands I used:
# generate CA private key
$ openssl genrsa -out grocyCA.key 2048
# generate CA certificate
$ openssl req -x509 -new -nodes -key grocyCA.key -subj "/CN=grocyCA" -days 1825 -out grocyCA.crt
# generate server private key
$ openssl genrsa -out grocyServer.key 2048
# create certificate signing request
$ openssl req -new -key grocyServer.key -out grocyServer.csr -config csr.conf
# create csr config
$ vim csr2.conf
# put as content the following with proper hostname in DNS.1 (add other .2 or .3 if needed)
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.com
# sign the request with the CA
$ openssl x509 -req -in grocyServer.csr -CA grocyCA.crt -CAkey grocyCA.key -CAcreateserial -out grocyServer.crt -days 10000 -extfile csr2.conf
Then I imported the CA to my android user certificate store checking the 2 checkboxes I was asked about, something about web apps and something else, don't remember right now :):
I used linuxserver.io docker image, so after mapping config folder I had access to the generated cert.key and cert.crt files which I replaced with the content of grocyServer.key and grocyServer.crt.
I also modified the Grocy config BASE_URL from data/config.php to https://mydockerhost:mymappedport/.
A docker container restart later and the android app worked with this test instance.
But thanks again for implementing this scenario even if it's not something that you have currently.
Hi @murizorun, is the current version v1.10.1 working with your self-signed certificates now?
Hey, I've just updated to 1.10.0 as it's the latest in F-Droid and it is working just fine. Should I try a manual install from github for v1.10.1 as well?
No, that's not necessary if v1.10.0
works for you because v1.10.1
only contains a hotfix for HTTP cleartext connections.
I had to move the cleartext traffic param from the manifest into the network_security_configuration xml, maybe because this file overwrites all other network params...
Thanks for your response!
Yes, you are right about the network_security_config param: https://developer.android.com/training/articles/security-config.
Thanks anyway for the change. Ping me again if you need any testing support with the move to grocy 3. I can setup some test instance on my docker node and try out test builds.
Cheers!
@MrJohnZoidberg I hate to necrobump, but as of versions 3.3.0 for the server and 2.2.1 for the app, this seems to not be working. Logging in with HTTP works just fine.
@Gitoffomalawn Please open a new issue for that where you describe what the problem is and how your setup looks like. To comment on a closed issue which is two years old is really not a great idea... And please check your setup, it looks like you are the only one with the problem, else there would be other issues or comments here.
Hello, I have a self-signed certificate on my server because it isn't open to the outside world. I added the
rootCA.pem
system wide on my Android phone. The web browser is happy with the SSL/TLS connection (no warnings). The problem is that the app is still complaining that the certificate isn't known or approved by Android... Does this mean the app does not check trusted certificates on the device? Is it possible to change that?Server: Home assistant (hassio) Grocy: 2.7.1