Android SSL-Connection <-> Tomcat Server with self-signed certificate
Intro: Still working on 'bringing back Wood Games 3D' which have a rest-server in the background that handles leaderboards and some competition (the daily world championship). On android I was relying (in 2010/11) on scoreloop-service that handled this for me (but got taken down sometime). I later had an java-applet version for which I had to create a server on my own (since scoreloop was only for mobile). And this is the server I want now to handle android as well...
Problem Android 10(sdk 29) and also earlier(?) don't allow http-calls anymore. There are ways to 'opt-in' it back, but it makes absolutely sense to have a secured connection.
So if you stumbled over following you are right here:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Let's start
-
Setup Tomcat for SSL:
-
Create certificate:
keytool -genkey -keystore tomcat.keystore -alias tomcat -keyalg RSA -keysize 4096
Important: The CN/Name (first entry) must be the name of the server you are connecting to. At my (local) setup with only some IP-Address (e.g. 192.168.178.188 ) I had to choose this IP-Address as name (otherwise android will not be happy) But if you need to connect to a domain, you need to specify the domain-name itself
-
After the creation keytool warns (at least at my current version):
Warning: The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore tomcat.keystore -destkeystore tomcat.keystore -deststoretype pkcs12".
So do as you suggested. (Not sure if this is mandatory)
-
Extract the cert:
keytool -export -keystore tomcat.keystore -alias tomcat -file tomcat.cer`
Type in the keystore password and the file tomcat.cer appears in your current folder.
-
copy this file to your Android-Project in Folder res/raw
cp tomcat.cer /home/..../project/res/raw
-
locate your tomcat's server.xml:
- TOMCAT-root/conf/server.xml
- if running it from within eclipse: 'Project Explorer'-View->Servers->Tomcat->server.xml
Usually there is already an SSL-configuration that just needs to be uncommented and setup to use our just created certificate(locate and change so it fits)
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="FOLDER_WITH_KEYSTORE/tomcat.keystore" keystorePass="FILLIN_YOUR_KEYSTORE_PASSWORD"/>
-
restart tomcat. From now on your server accepts https on port 8443
References:
-
-
android-project:
Three (or more) problems that might occur with this topic and all will be gone once you have set up the right network-security-config:- Something about "cleartextTrafficPermitted....": You are not allowed to use http://...-calls (you can opt it in)
- "Trust anchor for certification path not found": The ssl-connection's certificate is neither signed by a trusted CA nor known (yet)
- "hostname in certificate didn't match: != ": The CN of your certificate does not match the hostname you are calling it with.
-
In your android-project create a file: res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="false" /> <domain-config> <domain includeSubdomains="true">YOUR_DOMAIN_OR_IP</domain> <trust-anchors> <!-- Trust a debug certificate in addition to the system certificates --> <certificates src="system" /> <certificates src="@raw/tomcat" /> </trust-anchors> </domain-config> </network-security-config>
Here we reference the formerly copied tomcat-certificate-file tomcat.cer in the line:
<certificates src="@raw/tomcat" />
You can add multiple domains here. But each domain needs obviously a valid certificate
-
Now we need to reference this new file in the Android-Manifest. In your application-tag add following attribute:
android:networkSecurityConfig="@xml/network_security_config"
resulting in somthing like this:
<application android:name="org.your.Application" android:enabled="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:allowBackup="false" android:networkSecurityConfig="@xml/network_security_config" >
-
TargetSDK: Insert something like this that fits your project as well right after the manifest-tag:
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="29"/>
If you choose a minSdkVersion < 24 you get warned:
Attribute `networkSecurityConfig` is only used in API level 24 and higher (current min is 8)
I don't know what that means. Does it mean that <24 devices won't work or don't need it, because they just call this host without knowing the CA?? EDIT:: In this case you will need to initialize the SSL-Certificate on your own, I tried to use the same method as I use for the desktop client but get some 'Unknown Class'-Exception. So for android-version < API-Level 24 I fall back to http. I will output some kind of warning about unsecure network-communication....
-
project.properties: I set this to target android-29. Set for your project accordingly
target=android-29
-
Make sure to use the right address/port now and use 'https'. But from now on you line in 'safe'
-
I had to activate legacy apache-http-client. I did try to use one of my own but the app crashed everytime (saying something like mismatched implementation-something).
After the application tag include this:<uses-library android:name="org.apache.http.legacy" android:required="false"/>
Not sure how long this option is available. It should be the goal to use the http-client of your own...
More read:
- https://developer.android.com/training/articles/security-ssl
- https://developer.android.com/training/articles/security-config
- https://codelabs.developers.google.com/codelabs/android-network-security-config/#0
- https://better-coding.com/solved-android-cannot-send-data-to-the-server-cleartext-communication-to-not-permitted-by-network-security-policy/
- https://developer.android.com/training/articles/security-config.html#CleartextTrafficPermitted