Parkeisenbahn, Luxembourg

#Android

Access alternate certificates with acme4j

On January 11 2021, Let's Encrypt will change the default intermediate certificate from the cross-sign IdenTrust DST Root X3 certificate to their own ISRG Root X1 certificate.

The good news: The ISRG certificate is widely trusted by browsers by now, so the transition will be unnoticed by most users.

The bad news: The ISRG certificate is not included in Android devices before "Nougat" 7.1. These devices will show an error when trying to access sites that are signed with the new intermediate certificate. According to Let's Encrypt, stunning 34% of the Android devices out there shall be affected.

To mitigate the problem, Let's Encrypt provides an alternate certificate that is still cross-signed with the IdenTrust DST Root X3 certificate. If you have a web service that is accessed by a relevant number of older Android devices, you may want to use that alternate certificate. It will be available until September 29 2021. The IdenTrust DST Root X3 certificate itself will expire after that date, so this is a hard limit. Let's hope that the problem is going to be solved on Android side in time.

As acme4j fully implements the RFC 8555, it is easy to change your code so it will use the alternate certificate. Based on the acme4j example, this code block will use the first alternate certificate if present, and falls back to the main certificate if not:

Certificate certificate = order.getCertificate();
certificate = certificate.getAlternateCertificates().stream()
        .findFirst()
        .orElse(certificate);

Remember to remove the workaround after September 29 2021 January 2024, so you won't accidentally use other alternate certificates that may become available in the future.

PS: getAlternateCertificates() was added to the latest acme4j v2.11. If you have an older version, fear not: you just need to have a Login object, so you can bind the alternate certificate yourself. This is how it would look like in the example client:

Login login = session.login(acct.getLocation(), userKeyPair);

Certificate certificate = order.getCertificate();
certificate = certificate.getAlternates().stream()
        .map(login::bindCertificate)
        .findFirst()
        .orElse(certificate);

UPDATE: Let's Encrypt found a way to extend the Android compatibility until January 2024. However, this extension may only work for Android devices. To quote the article:

The new cross-sign will be somewhat novel because it extends beyond the expiration of DST Root CA X3. This solution works because Android intentionally does not enforce the expiration dates of certificates used as trust anchors.

How to feed DDMS with gpsbabel

The Android Device Monitor is not just an aid for debugging applications, but also allows to simulate GPS positions, so you won’t need to actually run around in the countryside for testing your GPS app. But where to get test data from?

I have recorded some of my hiking trips with my Garmin GPS 60, and saved them in Garmin’s proprietary gdb file format. These files contain waypoints, routes and also recorded tracks.

The Swiss Army Knife for GPS files, gpsbabel, comes in handy for converting a gdb file into the GPX file format that can be read by DDMS. This is the line I used for conversion:

gpsbabel -i gdb -f hike-track.gdb -o gpx,gpxver=1.1 -F hike-track.gpx

Note the gpxver=1.1 option, as DDMS is unable to read GPX 1.0 files.

After converting and loading the GPX file into DDMS, I can now send single waypoints as GPS events to the emulated device. But beyond that, I can also play back a recorded track, and simulate that I carry around the emulated device on that track. This is very useful for testing GPS apps.

Validating the Android 4.2.2 RSA fingerprint

Android 4.2.2 comes with a new security feature. If you try to connect to your smartphone via adb and USB debugging, you will note that your device is marked as "offline". Additionally, a dialog shows up on your device, presenting an RSA fingerprint of your computer and asking for confirmation to accept a connection.

The rationale is that if your device is lost or stolen, there is no way to read its content even if USB debugging was enabled.

Now, presenting an RSA fingerprint surely is a nice idea to avoid man-in-the-middle attacks. But how do you get that fingerprint in order to compare it with the one shown on the device? At first I thought there must be a command (or an adb option) that prints out the fingerprint, but I wasn't able to locate one. After spending some time with my favourite search engine, I finally dug up a rather more than less complicated command line that prints out the footprint:

awk '{print $1}' < adbkey.pub | openssl base64 -A -d -a | openssl md5 -c | \
  awk '{print $2}' | tr '[:lower:]' '[:upper:]'

The command must be executed in the directory where adb stores the adb key, which usually is ~/.android (or /root/.android if adb runs as root).

If you are somewhat security paranoid, you surely wonder why, on the one hand, Google shows a footprint on the device, but on the other hand makes it difficult to find out if that footprint actually belongs to your computer.