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.
Setup Tomcat for SSL:
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:
- 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
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:
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
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...