android NFC读写卡教程

    因为公司需求最近研究了大量的NFC读写M1卡的资料,自己撸了一个相对完整DEMO,让大家一起学习学习。由于之前一直是使用复旦方案的读卡器进行读写卡的,不了解基本原理的请移步:

  1. M1卡存取控制字节规则详解

  2. android 非接触式M1卡改密码

    现在我们使用手机自带的NFC标准来进行读写M1卡,毕竟现在手机都这么先进了还插着读卡器来进行读写M1卡实在是过不去,过于Low逼了。至于什么是NFC标准,基于什么原理请各位自行百度吧,我懒得凑字数了,直接开始吧!

    首先我们先封装一个关于NFC的工具类,其中包括初始化NFC、检查NFC是否打开、初始化NFC设置、读取NFCID(UID)等等方法,上代码:

public class NfcUtils {
    //nfc
    public static NfcAdapter mNfcAdapter;
    public static IntentFilter[] mIntentFilter = null;
    public static PendingIntent mPendingIntent = null;
    public static String[][] mTechList = null;

    /**
     * 构造函数,用于初始化nfc
     */
    public NfcUtils(Activity activity) {
        mNfcAdapter = NfcCheck(activity);
        NfcInit(activity);
    }

    /**
     * 检查NFC是否打开
     */
    public static NfcAdapter NfcCheck(Activity activity) {
        NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(activity);
        if (mNfcAdapter == null) {
            return null;
        } else {
            if (!mNfcAdapter.isEnabled()) {
                Intent setNfc = new Intent(Settings.ACTION_NFC_SETTINGS);
                activity.startActivity(setNfc);
            }
        }
        return mNfcAdapter;
    }

    /**
     * 初始化nfc设置
     */
    public static void NfcInit(Activity activity) {
        mPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity, activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        try {
            filter.addDataType("*/*");
        } catch (IntentFilter.MalformedMimeTypeException e) {
            e.printStackTrace();
        }
        mIntentFilter = new IntentFilter[]{filter, filter2};
        mTechList = null;
    }

    /**
     * 读取NFC的数据
     */
    public static String readNFCFromTag(Intent intent) throws UnsupportedEncodingException {
        Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawArray != null) {
            NdefMessage mNdefMsg = (NdefMessage) rawArray[0];
            NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
            if (mNdefRecord != null) {
                String readResult = new String(mNdefRecord.getPayload(), "UTF-8");
                return readResult;
            }
        }
        return "数据为空";
    }


    /**
     * 往nfc写入数据
     */
    public static void writeNFCToTag(String data, Intent intent) throws IOException, FormatException {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        Ndef ndef = Ndef.get(tag);
        ndef.connect();
        NdefRecord ndefRecord = NdefRecord.createTextRecord(null, data);
        NdefRecord[] records = {ndefRecord};
        NdefMessage ndefMessage = new NdefMessage(records);
        ndef.writeNdefMessage(ndefMessage);
    }

    /**
     * 读取nfcID
     */
    public static String readNFCId(Intent intent) throws UnsupportedEncodingException {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String id = ByteArrayToHexString(tag.getId());
        return id;
    }

    /**
     * 将字节数组转换为字符串
     */
    private static String ByteArrayToHexString(byte[] inarray) {
        int i, j, in;
        String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
        String out = "";

        for (j = 0; j < inarray.length; ++j) {
            in = (int) inarray[j] & 0xff;
            i = (in >> 4) & 0x0f;
            out += hex[i];
            i = in & 0x0f;
            out += hex[i];
        }
        return out;
    }
}

        上面代码有不懂的可以看一下官方文档里面有详细的介绍NFCAPI,写完工具类后我们可以通过在任意你想实现NFC功能的Activity类里面重写onNewIntent这个方法。因为刷卡后会实例化一个新的Intent ,通过这个方法我们可以收集到该实例的Intent的Action是不是一个NFCAction。如果是,我们可以通过getParcelableExtra这个方法获取Intent对应的NFC的Tag数据。接着我们通过MifareClassic类里的get方法创建一个MifareClassic的对象,基于MifareClassic协议进行无握手式的连接M1卡。这个是射频技术,具体原理百度。连接后我们就可以对M1卡基于MifareClassic协议进行读写,代码如下:

     String action = intent.getAction();
       if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
           Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
           MifareClassic mifareClassic = MifareClassic.get(tag);
           try {
               mifareClassic.connect();
               //获取扇区数量
               int count = mifareClassic.getSectorCount();
               Log.e("onNewIntent:","扇区数量 ===" + count);
               //用于判断时候有内容读取出来
               for (int i = 0;i < count;i++){
                   boolean isOpen = mifareClassic.authenticateSectorWithKeyA(i,MifareClassic.KEY_DEFAULT);
                   if (isOpen){
                       //获取扇区里面块的数量
                       int bCount = mifareClassic.getBlockCountInSector(i);
                       Log.e("onNewIntent:","扇区里面块的数量 ===" + bCount);
                       //获取扇区第一个块对应芯片存储器的位置
                       //存储器的位置为第一扇区为0后面叠加4直到60为止
                       int bIndex = mifareClassic.sectorToBlock(i);
                       for (int j = 0; j < bCount; j++){
                           Log.e("onNewIntent:","存储器的位置 ===" + bIndex + "当前块 === "+ (bIndex+j));
                           byte[] data = mifareClassic.readBlock(bIndex+j);//进行了读卡
                           msgBuffer.append("块"+(bIndex+j)+"数据:").append(StringTool.byteHexToSting(data)).append("\r\n");
                           handler.sendEmptyMessage(0);
                           Log.e("数据","第"+(bIndex+j)+"块" + StringTool.byteHexToSting(data));

                       //修改KeyA和KeyB
                           if ((bIndex+j)==(4*i+3)){
                       //将所有扇区的最后一个Block修改为111111111111ff078069111111111111
                               mifareClassic.writeBlock(bIndex+j,new byte[]{(byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0xff, 0x07, (byte) 0x80, (byte) 0x69,(byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11});
                               Log.e("onNewIntent:",(bIndex+j)+"块加密成功");

                           }
                            
                       }
                   }else {
                       msgBuffer.append("第"+(i+1)+"扇区"+"验证新卡密码失败\r\n");
                       handler.sendEmptyMessage(0);
                       Log.e("失败 ","验证密码");
                  }

       通过上面的操作基本是可以进行刷卡读写操作了,但是这还没有结束,我们还需要在这个Activity的生命周期对NFC进行开启和关闭前台调度系统。除此之外我们还要在这个Activity创建之时先检查我们的设备是否支持NFC功能,做到尽可能的完善,代码如下:

 @Override
    protected void onResume() {
        super.onResume();
        //开启前台调度系统
        if (mAdapter != null) {
            mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        //关闭前台调度系统
        mAdapter.disableForegroundDispatch(this);
    }

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        msgBuffer = new StringBuffer();
        mAdapter = NfcAdapter.getDefaultAdapter(this);
        mPendingIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        try {
            ndef.addDataType("*/*");

        } catch (IntentFilter.MalformedMimeTypeException e) {
            throw new RuntimeException("fail", e);
        }
        mFilters = new IntentFilter[]{ndef,};
        mTechLists = new String[][]{{IsoDep.class.getName()}, {NfcA.class.getName()},};
        Log.d(" mTechLists", NfcF.class.getName() + mTechLists.length);

        if (mAdapter == null) {
            Toast.makeText(this, "设备不支持NFC!", Toast.LENGTH_LONG).show();
            msgBuffer.append("\r\n").append("设备不支持NFC!");
            handler.sendEmptyMessage(0);
            return;
        }else {
            msgBuffer.append("\r\n").append("设备支持NFC!");
            handler.sendEmptyMessage(0);
        }
        if (!mAdapter.isEnabled()) {
            Toast.makeText(this, "请在系统设置中先启用NFC功能!", Toast.LENGTH_LONG).show();
            return;
        }


    

    }

这样我们的NFC读写卡功能就能实现了,如果有什么地方错误的话欢迎大家指正,互相探讨,这个领域好像很少人做。希望更多人一起来探讨。

原创:https://blog.csdn.net/weixin_40600325/article/details/88824910

你可能感兴趣的:(Android,NFC,android,M1卡基础原理与读写)