eu.verdelhan.acr122urw.MifareUtils.java Source code

Java tutorial

Introduction

Here is the source code for eu.verdelhan.acr122urw.MifareUtils.java

Source

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Marc de Verdelhan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package eu.verdelhan.acr122urw;

import static eu.verdelhan.acr122urw.HexUtils.bytesToHexString;
import static eu.verdelhan.acr122urw.HexUtils.hexStringToBytes;
import static eu.verdelhan.acr122urw.HexUtils.isHexString;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.smartcardio.CardException;
import org.nfctools.mf.MfAccess;
import org.nfctools.mf.MfException;
import org.nfctools.mf.MfReaderWriter;
import org.nfctools.mf.block.BlockResolver;
import org.nfctools.mf.block.MfBlock;
import org.nfctools.mf.card.MfCard;
import org.nfctools.mf.classic.Key;
import org.nfctools.mf.classic.MemoryLayout;
import org.apache.commons.codec.binary.Hex;

/**
 * Mifare utility class.
 */
public final class MifareUtils {

    /** Mifare Classic 1K sector count */
    public static final int MIFARE_1K_SECTOR_COUNT = 16;

    /** Mifare Classic 1K block count (per sector) */
    public static final int MIFARE_1K_PER_SECTOR_BLOCK_COUNT = 4;

    /** Common Mifare Classic 1K keys */
    public static final List<String> COMMON_MIFARE_CLASSIC_1K_KEYS = Arrays.asList("001122334455", "000102030405",
            "A0A1A2A3A4A5", "B0B1B2B3B4B5", "AAAAAAAAAAAA", "BBBBBBBBBBBB", "AABBCCDDEEFF", "FFFFFFFFFFFF");

    private MifareUtils() {
    }

    /**
     * @param s a string
     * @return true if the provided string is a valid Mifare Classic 1K key, false otherwise
     */
    public static boolean isValidMifareClassic1KKey(String s) {
        return isHexString(s) && (s.length() == 12);
    }

    /**
     * @param s a string
     * @return true if the provided string is a valid Mifare Classic 1K sector index, false otherwise
     */
    public static boolean isValidMifareClassic1KSectorIndex(String s) {
        try {
            int sectorIndex = Integer.parseInt(s);
            return sectorIndex >= 0 && sectorIndex < MIFARE_1K_SECTOR_COUNT;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    /**
     * @param s a string
     * @return true if the provided string is a valid Mifare Classic 1K block index, false otherwise
     */
    public static boolean isValidMifareClassic1KBlockIndex(String s) {
        try {
            int sectorIndex = Integer.parseInt(s);
            return sectorIndex >= 0 && sectorIndex < MIFARE_1K_PER_SECTOR_BLOCK_COUNT;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    /**
     * Dumps a Mifare Classic 1K card.
     * @param reader the reader
     * @param card the card
     * @param keys the keys to be tested for reading
     */
    public static String dumpMifareClassic1KCard(MfReaderWriter reader, MfCard card, List<String> keys)
            throws CardException {
        String value = "";
        for (int sectorIndex = 1; sectorIndex < 2; sectorIndex++) {
            // For each sector...
            for (int blockIndex = 2; blockIndex <= 2; blockIndex++) {
                // For each block...
                value = dumpMifareClassic1KBlock(reader, card, sectorIndex, blockIndex, keys);

            }
        }
        return value;
    }

    /**
     * Write data to a Mifare Classic 1K card.
     * @param reader the reader
     * @param card the card
     * @param sectorId the sector to be written
     * @param blockId the block to be written
     * @param key the key to be used for writing
     * @param dataString the data hex string to be written
     */
    public static void writeToMifareClassic1KCard(MfReaderWriter reader, MfCard card, int sectorId, int blockId,
            String key, String dataString) throws CardException {
        if (!isValidMifareClassic1KKey(key)) {
            System.out.println("The key " + key + "is not valid.");
            return;
        }
        if (!isHexString(dataString)) {
            System.out.println(dataString + " is not an hex string.");
            return;
        }

        byte[] keyBytes = hexStringToBytes(key);
        // Reading with key A
        MfAccess access = new MfAccess(card, sectorId, blockId, Key.A, keyBytes);
        String blockData = readMifareClassic1KBlock(reader, access);
        if (blockData == null) {
            // Reading with key B
            access = new MfAccess(card, sectorId, blockId, Key.B, keyBytes);
            blockData = readMifareClassic1KBlock(reader, access);
        }
        System.out.print("Old block data: ");
        if (blockData == null) {
            // Failed to read block
            System.out.println("<Failed to read block>");
        } else {
            // Block read
            System.out.println(blockData + " (Key " + access.getKey() + ": " + key + ")");

            // Writing with same key
            boolean written = false;
            try {
                byte[] data = hexStringToBytes(dataString);
                MfBlock block = BlockResolver.resolveBlock(MemoryLayout.CLASSIC_1K, sectorId, blockId, data);
                written = writeMifareClassic1KBlock(reader, access, block);
            } catch (MfException me) {
                System.out.println(me.getMessage());
            }
            if (written) {
                blockData = readMifareClassic1KBlock(reader, access);
                System.out.print("New block data: ");
                if (blockData == null) {
                    // Failed to read block
                    System.out.println("<Failed to read block>");
                } else {
                    // Block read
                    //System.out.println(blockData + " (Key " + access.getKey() + ": " + key + ")");
                }
            }
        }
    }

    /**
     * Reads a Mifare Classic 1K block.
     * @param reader the reader
     * @param access the access
     * @return a string representation of the block data, null if the block can't be read
     */
    private static String readMifareClassic1KBlock(MfReaderWriter reader, MfAccess access) throws CardException {
        String data = null;
        try {
            MfBlock block = reader.readBlock(access)[0];
            data = bytesToHexString(block.getData());
        } catch (IOException ioe) {
            if (ioe.getCause() instanceof CardException) {
                throw (CardException) ioe.getCause();
            }
        }
        return data;
    }

    /**
     * Writes a Mifare Classic 1K block.
     * @param reader the reader
     * @param access the access
     * @param block the block to be written
     * @return true if the block has been written, false otherwise
     */
    private static boolean writeMifareClassic1KBlock(MfReaderWriter reader, MfAccess access, MfBlock block)
            throws CardException {
        boolean written = false;
        try {
            reader.writeBlock(access, block);
            written = true;
        } catch (IOException ioe) {
            if (ioe.getCause() instanceof CardException) {
                throw (CardException) ioe.getCause();
            }
        }
        return written;
    }

    /**
     * Dumps Mifare Classic 1K block data.
     * @param reader the reader
     * @param card the card
     * @param sectorId the sector to be read
     * @param blockId the block to be read
     * @param keys the keys to be tested for reading
     */
    private static String dumpMifareClassic1KBlock(MfReaderWriter reader, MfCard card, int sectorId, int blockId,
            List<String> keys) throws CardException {
        //System.out.printf("%02d block%02d: ", sectorId, blockId);
        String blockData = "";
        for (String key : keys) {
            // For each provided key...
            if (isValidMifareClassic1KKey(key)) {
                byte[] keyBytes = hexStringToBytes(key);
                // Reading with key A
                MfAccess access = new MfAccess(card, sectorId, blockId, Key.A, keyBytes);
                blockData = readMifareClassic1KBlock(reader, access);
                //System.out.println("sd " + blockData);
                if (blockData == null) {
                    // Reading with key B
                    access = new MfAccess(card, sectorId, blockId, Key.B, keyBytes);
                    blockData = readMifareClassic1KBlock(reader, access);
                }
                if (blockData != null) {
                    // Block read
                    //System.out.println(blockData + " (Key " + access.getKey() + ": " + key + ")");
                    return blockData;
                }
            }
        }
        // All keys tested, failed to read block
        //System.out.println("<Failed to read block>");
        return blockData;
    }

    public static String convertHexToText(String hexString) {
        String result = "";
        try {
            byte[] bytes = Hex.decodeHex(hexString.toCharArray());
            result = new String(bytes, "UTF-8");

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

}