Android NFC read data from ePassport

epalleva picture epalleva · Feb 18, 2014 · Viewed 10.5k times · Source

I'm working on a ePassport Reader App, I've followed some older question and I've used the following code to connect to the passport successfully. My problem is that I can't understand how can I read all data (name, surname, photo....) stored into the passport. Here is the code I've used, the app is working well (prompt when is near an NFC tag).

    @Override
    protected String doInBackground(Tag... params) {

        //Tag tag = params[0];

        Intent intent = getIntent();

        //Log.d(TAG,"params " + intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));

        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

        IsoDep dep = IsoDep.get(tag);

        if (dep == null) {
            // IsoDep is not supported by this Tag. 
            return null;
        }

        byte[] CMD = {
                    (byte)0x00, /* CLA = 00 (first interindustry command set) */
                    (byte)0xA4, /* INS = A4 (SELECT) */
                    (byte)0x04, /* P1  = 04 (select file by DF name) */
                    (byte)0x0C, /* P2  = 0C (first or only file; no FCI) */
                    (byte)0x07, /* Lc  = 7  (data/AID has 7 bytes) */
                    /* AID = A0000002471001: */
                    (byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x02,
                    (byte)0x47, (byte)0x10, (byte)0x01
            };

        byte[] GET_RANDOM = { 
                (byte) 0x00, // CLA Class        
                (byte) 0x84, // INS Instruction
                (byte) 0x00, // P1  Parameter 1
                (byte) 0x00, // P2  Parameter 2
                (byte) 0x0E  // LE  maximal number of bytes expected in result
            };

            try {
                dep.connect();

                byte[] result = dep.transceive(CMD);// CONNECT

                Log.d(TAG, "result " + result[0] + " " + (byte)0x90);

                if (!(result[0] == (byte) 0x90 && result[1] == (byte) 0x00))
                        throw new IOException("could not select applet");

                if(dep.isConnected()==true)
                    Log.d(TAG,"IS CONNECTED!");
                else
                    Log.d(TAG,"ISN'T CONNECTED!");

                    result = dep.transceive(GET_RANDOM); // EXEC A CMD
                    int len = result.length;
                    if (!(result[len-2]==(byte)0x90 && result[len-1]==(byte) 0x00))
                       throw new RuntimeException("could not retrieve msisdn");

                    byte[] data = new byte[len-2];
                    System.arraycopy(result, 0, data, 0, len-2);
                    String str = new String(data);

                    Log.d(TAG, str);

                    dep.close();

            } catch (IOException e1) {
                e1.printStackTrace();
            }



        return null;
    }

Answer

ldemay picture ldemay · Feb 22, 2014

You need the make a BAC (Basic Access Control) against your epassport to be able to read the basic informations printed on the passport (Country, Name, Surname, Nationality, Date of birth, Sex...) and the MRZ (Machine Readable Zone, that is to say the two big lines at the bottom of your passport). All this information is located in the DG (Data Group) 1, the photo is located in the DG2. You can find other informations in the other DG's except for example the DG3 which needs an EAC (Extended Access Control) to be read because it contains sensitive data (fingerprints).

You can use the JMRTD library to read it from your Android Phone. A "demo" Android application is available on the market here. Otherwise, you can start reading the official documentation from ICAO (International Civil Aviation Organization) located here. At the end of this document, you can find some examples so you can make your own implementation of the BAC.

You can also look at the JMRTD source code to help you write your code. IMO it's quite complicated to code but very interesting to learn. The code you wrote is a good start!