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:

    1. 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

    2. 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)

    3. 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.

    4. copy this file to your Android-Project in Folder res/raw

      cp tomcat.cer /home/..../project/res/raw
      
    5. 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"/>     
      
    6. 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.
    1. 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

    2. 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"
         >     
      
    3. 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....

    4. project.properties: I set this to target android-29. Set for your project accordingly

      target=android-29
      
    5. Make sure to use the right address/port now and use 'https'. But from now on you line in 'safe'

    6. 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: