How To Read Your Passport With Android

April 5, 2016
Tags: , , ,

As I’ve been researching machine readable travel documents, I decided to do a little proof-of-concept on reading ePassports using an NFC-enabled smartphone (Android).

The result is on GitHub, and is based on the jMRTD library, which provides all the necessary low-level details.

As I pointed out in my previous article, the standards for the ePassports have evolved a lot throughout the years – from no protection, to BAC, to EACv1, EACv2 and SAC (which replaces BAC). Security is still doubtful, as most of the passports and inspection systems require backward compatibility to BAC. That’s slowly going away, but even when BAC goes away, it will be sufficient to enter the CAN (Card Authentication Number) for the PACE protocol, so the app will still work with minor modifications.

What the app does is:

  1. Establishes NFC communication
  2. Authenticates to the passport using the pre-entered passport number, date of birth and expiry date (hardcoded in the app at the moment). Note that the low security of the protocol is due to the low entropy of this combination, and brute force is an option, as passports cannot be locked after successive failures.
  3. Reads mandatory data groups – all the personal information present in the passport, including the photo. In the example code only the first data group (DG1) is read, and the personal identifier is shown on the screen. The way to read data groups is as follows:
    InputStream is = ps.getInputStream(PassportService.EF_DG1);
    DG1File dg1 = (DG1File) LDSFileUtil.getLDSFile(PassportService.EF_DG1, is);
    
  4. Performs chip authentication – the first step of EAC, which makes sure that the chip is not cloned – it requires proof of ownership of a private key, which is stored in the protected area of the chip.

The code has some questionable coding practices – e.g. the InputStream handling (the IDE didn’t initially allow me to use Java 7, and I didn’t try much harder), but I hope they’ll be fixed if used in real projects.

One caveat – for Android there’s a need for SpongyCastle (which is a port of the BouncyCastle security provider). However it is not enough, so both have to be present for certain algorithms to be supported. Unfortunately, jMRTD has a hardcoded reference to BouncyCastle in one method, which leads to the copy-pasted method for chip authentication.

There is one more step of EAC – the terminal authentication, which would allow the app to read the fingerprints (yup, sadly there are fingerprints there). However, EAC makes it harder to do that. I couldn’t actually test it properly, because the chip rejects verifying even valid certificates, but anyway, let me explain. EAC relies on a big infrastructure (PKI) where each participating country has a Document Verifier CA, whose root certificate is signed by all other participating countries (as shown here). Then each country issues short-lived (1 day) certificates signed by the DVCA, which are used in the inspection system (border polices and automatic gates). The certificate chain now contains all countries root certificates, followed by the DVCA certificate, followed by the inspection system certificate. The chip has to verify that this chain is valid (by verifying that each signature on a certificate is indeed performed by the private key of the issuer). The chip itself has the root certificate of its own country, so it has the root of the chain and can validate it (which is actually the first step). Finally, in order to make sure that the inspection system certificate is really owned by the party currently performing the protocol, the chip sends a challenge to be signed by the terminal.

So, unless a collision is found and a fake certificate is attached to the chain, you can’t easily perform “terminal authentication”. Well, unless a key pair leaks from some inspection system somewhere in the world. Then, because the chip does not have a clock, even though the certificates are short-lived, they would still allow reading the fingerprints, because the chip can’t know they are expired (it syncs the time with each successful certificate validation, but that only happens when going through border control at airports). Actually, you could also try to spam the chip with a huge chain, and it will at some point “crash”, and maybe it will do something that it wouldn’t normally do, like release the fingerprints. I didn’t do that for obvious reasons.

But the point of the app is not to abuse the passports – there may be legitimate use-cases to allow reading the data from them, and I hope my sample code is useful for that purpose.

Share Button

4 Responses to “How To Read Your Passport With Android”

  1. Hello,
    Thank you for the explanation, I just want to ask you if I want to read the DG2 (the photo on the epassport) how to do it ? since the readcontent method is protect in DG2File class, and the faceInfo will return only some info about the photo only.
    Thank you
    Rima

  2. Hi Bozhidar !

    Your sample code try to access the chip and handle data at the same time. That’s why you can just read the first data group DG1 but no more. That’s because the PassportService can’t wait for that. You should bring all the available DGs from the chip then use them.
    The jmrtd implementation for the version 0.5.0 use the class Passport.java which uses the PassportService.java

    The android project ajmrtd, implements a good sample to show how to connect and interept data from the chip using BAC mode.

    So if you need to prouve EAC, try to rearrage your code as amrtd for the 0.5.6 jmrtd. Then let me know if EAC is OK 😉

    good job!

  3. Hi again,
    You can check a sample code to retrive DGs based on your project and ajmrtd one.
    For the Chip Authent issue. I have also an exception. I don’t know the reason for your case, but for me I always have the keyId stored into DG14 = -1
    here a sample DG14 from an RFID card:
    DG14File [[ChipAuthenticationPublicKeyInfo [protocol = id_PK_ECDH, chipAuthenticationPublicKey = EC Public Key
    X: 1babc31053a1c58e3f179be231b48f699432f6ed406bc9c79a38ba0e
    Y: d2270042ceb368c4c977e7ef0605f5eee8619971c80cc2d0d28d9704
    , keyId = -1]]]

    NB : The same passpord trested whith an other reader for PCs show that Chip Authent is OK.

  4. should use
    static {
    Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
    }

    no more exception for doCA()

Leave a Reply