uni-app android NFC MifareClassic 读IC卡加密扇区方法

发布于 1周前 作者 sinazl 来自 Uni-App

uni-app android NFC MifareClassic 读IC卡加密扇区方法

近段时间在做android下NFC的M1卡的加密扇区的读取,参考了Android_磊子的操作基础内容,但是涉及到MifareClassic类操作验证未能找到相关资料,后来查看了MifareClassic类的源代码和基于android原生案例,实现了加密扇区读取。分享下代码,希望对大家有帮助。

对于NFC操作的基本部分有很多资料都一样的,这里不再赘述,仅简单介绍MifareClassic验证和读取的过程

  1. 创建MifareClassic对象,MifareClassic.get(tag);
  2. 建立MifareClassic类连接,MifareClassic.connect();
  3. 对扇区(0-15)进行验证,按KeyA和KeyB两种验证,
    1. MifareClassic.authenticateSectorWithKeyA(sector,key); 其中sector为扇区号,key为6字节验证码,此处Key需要处理为-128至127的字节,开始使用0x00-0xFF会报错
    2. MifareClassic.authenticateSectorWithKeyB(sector,key); 其中sector为扇区号,key为6字节验证码,此处Key需要处理为-128至127的字节,开始使用0x00-0xFF会报错
  4. 按块(sector*4+i)读取扇区内的16Byte内容,MifareClassic.readBlock(Block); Block0=sector×4+0; Block1=sector×4+1; Block2=sector×4+2; Block3=sector×4+3;
  5. 关闭MifareClassic连接,MifareClassic.close();

MifareClassic读取代码

//MifareClassic读取代码  
readData:function(intent){  
    var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
    var techList = tag.getTechList();  
    var bisMfc=false;  
    for(var i=0;i<techList.length;i++){  
        if(techList[i].indexOf('MifareClassic')>=0){  
            bisMfc=true;  
            break;  
        }  
    }  
    if(!bisMfc){  
        this.ICERROR='卡片类型错误!';  
        return;  
    }  
    var mfc=MifareClassic.get(tag);  
    if(!mfc){  
        this.ICERROR='卡片获取错误!';  
        return;  
    }  
    mfc.setTimeout(3000);  
    if(!mfc.isConnected()){  
        try{  
            invoke(mfc,'connect');   
        }catch(e){  
            this.ICERROR='卡片连接错误!';  
            return;  
        }  
    }  
    try{  
        this.ICUID=this.ByteArrayToHexString(tag.getId());  
        var cmdBytes=this.HexStringToByteArray(this.keyVal);  
        var auth=false;  
        if(this.keyType=="A"){  
            auth=invoke(mfc,"authenticateSectorWithKeyA",parseInt(this.sector),cmdBytes);  
        }else{  
            auth=invoke(mfc,"authenticateSectorWithKeyB",parseInt(this.sector),cmdBytes);  
        }  
        if(!auth){  
            this.ICERROR='扇区验证失败';  
            return;  
        }  
        var sectorData=[];  
        var tmpRet;  
        this.ICData='IC卡扇区数据>>';  
        for(var i=0;i<4;i++){   
            tmpRet=invoke(mfc,"readBlock",this.sector*4+i);  
            this.ICData=this.ICData+'\n';  
            this.ICData=this.ICData+this.ByteArrayToHexString(tmpRet);  
            sectorData.push.apply(sectorData,tmpRet);  
        }  
        console.log(this.ByteArrayToHexString(sectorData));  
        this.ICERROR='读卡完成';  
    }catch(e){  
        this.ICERROR=e.message;  
        console.error(e);   
    }finally{  
        mfc.close();  
    }  
}  
//Key处理函数  
HexStringToByteArray:function(instr) {  
    var hexA = new Array();  
    var pos = 0;  
    var len = instr.length/2;  
    for(var i=0; i<len; i++)  
    {  
        var s = instr.substr(pos, 2);  
        var v = parseInt(s, 16);  
        if(v>=128)  
            v=v-256;  
        hexA.push(v);  
        pos += 2;  
    }  
    return hexA;  
}

项目是基于nui-app项目的vue测试通过的,附件共享了vue文件。

项目需设置android.permission.NFC权限。


1 回复

在uni-app中直接操作Android NFC(近场通信)功能,尤其是读取MifareClassic IC卡的加密扇区,是一个比较复杂的任务。uni-app主要面向跨平台开发,原生硬件操作(如NFC)通常需要依赖原生插件或者通过原生代码扩展来实现。以下是一个简要的思路和代码示例,展示如何在Android平台上使用NFC读取MifareClassic卡的加密扇区。

步骤概述

  1. 配置AndroidManifest.xml:添加NFC权限。
  2. 创建NFCAdapter和PendingIntent:用于监听NFC事件。
  3. 实现Activity的NFC回调接口:处理NFC卡读取事件。
  4. 使用MifareClassic类读取加密扇区:通过APDU命令或已知的密钥进行认证和读取。

示例代码

1. 配置AndroidManifest.xml

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

2. 创建NFCAdapter和PendingIntent

在Activity的onCreate方法中:

NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
    Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
    finish();
    return;
}

if (!nfcAdapter.isEnabled()) {
    Toast.makeText(this, "NFC is disabled.", Toast.LENGTH_LONG).show();
    finish();
    return;
}

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
        new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
    ndef.addDataType("*/*");
} catch (IntentFilter.MalformedMimeTypeException e) {
    throw new RuntimeException("fail", e);
}
IntentFilter[] intentFiltersArray = new IntentFilter[]{ndef, };
String[][] techListsArray = new String[][]{
    new String[]{NfcF.class.getName()},
    new String[]{MifareClassic.class.getName()}
};

nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);

3. 实现NFC回调接口

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    if (tag != null) {
        MifareClassic mifareClassic = MifareClassic.get(tag);
        if (mifareClassic != null) {
            try {
                mifareClassic.connect();
                // 假设你已经知道密钥A和密钥B
                byte[] keyA = new byte[]{/* 密钥A */};
                byte[] keyB = new byte[]{/* 密钥B */};
                mifareClassic.authenticateSectorWithKeyA(1, keyA); // 扇区1
                byte[] data = mifareClassic.readBlock(4); // 读取块4(扇区1的第2块)
                // 处理数据
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    mifareClassic.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注意

  • 权限与安全:在实际应用中,处理NFC读取和密钥操作需要格外注意安全性。
  • 错误处理:示例代码中的错误处理较为简单,实际应用中应增加更多的异常处理和用户提示。
  • uni-app集成:上述代码是原生Android代码,要在uni-app中使用,需通过原生插件机制集成。
回到顶部