Search code examples
androidsecurityauthenticationnfcmifare

How to prevent keys being leaked when reading MIFARE Classic cards on Android?


In Android, when reading MIFARE Classic cards, authentication is performed using MifareClassic.authenticateSectorWithKeyA (or authenticateSectorWithKeyB) methods, but the parameter keyA/keyB of these methods is plain text. Thus, it's very easy for a hacker to get the key.

Is there a way, when doing authentication, to not use a plain text key directly but to have the key stored in server? Then, every time authentication is performed, the MIFARE Classic card generates a random key, the random key is transfered to the server, the server calculates a token using the random key and the stored authentication key, then transfers the token to the MIFARE Classic card, where the token is verified, and if right, the authentication succeeds.


Solution

  • No, MIFARE Classic authentication is implemented in NXP's NFC controllers in a way that the NFC controller itself needs to know the key in order to compute the authentication challenges and responses. The reason for this is that the NFC controller performs the acutal authentication and encryption and emulates access to the tag memory through plain-text commands. Since the NFC controllers used in Android devices don't seem to support any form of transport key encryption, Android needs to send the plain-text key itself to the NFC controller. Consequently, the Android API for MIFARE Classic requires a plain-text key. There's currently no way to get around this.

    However, there are other tag technologies (e.g. MIFARE DESFire) where the authentication and encryption is diectly processed by your app (the NFC controller transparently forwards all communication). In that case, you could pass that communication on to some backend server to avoid storing plain-text keys inside your app.