g***@126.com
g***@126.com
  • 发布:2019-02-15 14:54
  • 更新:2022-10-30 22:43
  • 阅读:13618

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

分类:HBuilderX

近段时间在做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权限。

4 关注 分享
1***@163.com 2***@qq.com 8***@qq.com 刘

要回复文章请先登录注册

7***@qq.com

7***@qq.com

// 包路径
const package_NdefRecord = 'android.nfc.NdefRecord';
const package_NdefMessage = 'android.nfc.NdefMessage';
const package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
const package_Intent = 'android.content.Intent';
const package_Activity = 'android.app.Activity';
const package_PendingIntent = 'android.app.PendingIntent';
const package_IntentFilter = 'android.content.IntentFilter';
const package_NfcAdapter = 'android.nfc.NfcAdapter';
const package_Ndef = 'android.nfc.tech.Ndef';
const package_NdefFormatable = 'android.nfc.tech.NdefFormatable';
const package_Parcelable = 'android.os.Parcelable';
const package_String = 'java.lang.String';

let NfcAdapter;
let NdefRecord;
let NdefMessage;
let readyWriteData = false;
let readyRead = false;
let noNFC = false;
let techListsArray = [
["android.nfc.tech.IsoDep"],
["android.nfc.tech.NfcA"],
["android.nfc.tech.NfcB"],
["android.nfc.tech.NfcF"],
["android.nfc.tech.Ndef"],
["android.nfc.tech.NfcV"],
["android.nfc.tech.NdefFormatable"],
["android.nfc.tech.MifareClassic"],
["android.nfc.tech.MifareUltralight"]
];
// 要写入的数据
let text = '{id:123,name:nfc,stie:cssmini.com}';
let readResult = '';

export default {
listenNFCStatus: function() {
let that = this;
try {
let main = plus.android.runtimeMainActivity();
let Intent = plus.android.importClass('android.content.Intent');
let Activity = plus.android.importClass('android.app.Activity');
let PendingIntent = plus.android.importClass('android.app.PendingIntent');
let IntentFilter = plus.android.importClass('android.content.IntentFilter');
NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
let nfcAdapter = NfcAdapter.getDefaultAdapter(main);

if (nfcAdapter == null) {
uni.showToast({
title: '设备不支持NFC!',
icon: 'none'
})
noNFC = true;
return;
}

if (!nfcAdapter.isEnabled()) {
uni.showToast({
title: '请在系统设置中先启用NFC功能!',
icon: 'none'
});
noNFC = true;
return;
} else {
noNFC = false;
}

let intent = new Intent(main, main.getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
let pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
let ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
ndef.addDataType("*/*");
let intentFiltersArray = [ndef];

plus.globalEvent.addEventListener('newintent', function() {
console.log('newintent running');
// 监听 NFC
setTimeout(that.nfcRuning(), 1000);
});
plus.globalEvent.addEventListener('pause', function(e) {
console.log('pause running');
if (nfcAdapter) {
//关闭前台调度系统
//恢复默认状态
nfcAdapter.disableForegroundDispatch(main);
}
});
plus.globalEvent.addEventListener('resume', function(e) {
console.log('resume running');
if (nfcAdapter) {
//开启前台调度系统
nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
}
});
nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
} catch (e) {
console.error(e);
}
},
nfcRuning: function() {
NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
let main = plus.android.runtimeMainActivity();
let intent = main.getIntent();
let that = this;

console.log("action type:" + intent.getAction());

if (package_TECH_DISCOVERED == intent.getAction()) {
console.log("readyWriteData=" + readyWriteData + " readyRead=" + readyRead)
if (readyWriteData) {
that.write(intent);
readyWriteData = false;
} else if (readyRead) {

that.read(intent);
readyRead = false;
}
}
},
write(intent) {
try {
toast('请勿移开标签 正在写入...');
console.log("text=" + text);

let textBytes = plus.android.invoke(text, "getBytes");
// image/jpeg text/plain
let textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
plus.android.invoke("text/plain", "getBytes"),
plus.android.invoke("", "getBytes"), textBytes);
let message = new NdefMessage([textRecord]);
let Ndef = plus.android.importClass('android.nfc.tech.Ndef');
let NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
let tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
let ndef = Ndef.get(tag);
if (ndef != null) {
// 待写入的数据长度
let size = message.toByteArray().length;
ndef.connect();
if (!ndef.isWritable()) {
toast('tag不允许写入!');
return;
}
if (ndef.getMaxSize() < size) {
toast('文件大小超出容量!');
return;
}
ndef.writeNdefMessage(message);
toast('写入数据成功!');
return;
} else {
let format = NdefFormatable.get(tag);
if (format != null) {
try {
format.connect();
format.format(message);
toast('格式化tag并且写入message');
return;
} catch (e) {
toast('格式化tag失败.');
return;
}
} else {
toast('Tag不支持NDEF');
return;
}
}
} catch (e) {
toast('写入失败');
console.log("error=" + e);
}

},
read(intent) {
this.getData(intent);
},
byteArrayToHexString: function(inarray) { // converts byte arrays to string
let i, j, inn;
let hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
let out = "";

for (j = 0; j < inarray.length; ++j) {
if(inarray[j]==0) continue;
inn = inarray[j] & 0xff;
i = (inn >>> 4) & 0x0f;
out += hex[i];
i = inn & 0x0f;
out += hex[i];
}
return out;
},
writeData: function() {
if (noNFC) {
toast('请检查设备是否支持并开启 NFC 功能!');
return;
}
// 监听事件,触发条件
readyWriteData = true;
toast('请将NFC标签靠近!');
},
readData: function() {
if (noNFC) {
toast('请检查设备是否支持并开启 NFC 功能!');
return;
}
// 监听事件,触发条件
readyRead = true;
toast('请将NFC标签靠近!');

},
getData: function(intent) {
var sector = 1;//第一扇区
var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
let mc = plus.android.importClass('android.nfc.tech.MifareClassic');
let mfc = mc.get(tag);
console.log('mfc', mfc)
mfc.connect();
var cmdBytes = [0x6b, 0x69, 0x61, 0x6A, 0x75, 0x6E]//a密码 根据自己具体的来
console.log('mfc', cmdBytes)
var auth = mfc.authenticateSectorWithKeyA(sector, cmdBytes)

console.log('auth', auth);
if (!auth) return;
try {
var tmpRet,result;
for (var i = 0; i < 3; i++) {
tmpRet = mfc.readBlock(sector * 4 + i);
result+=this.hexCharCodeToStr(this.byteArrayToHexString(tmpRet))
}

console.log('result',result);
} 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;
},
hexCharCodeToStr: function(hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim();
var rawStr =
trimedStr.substr(0, 2).toLowerCase() === "0x" ?
trimedStr.substr(2) :
trimedStr;
var len = rawStr.length;
if (len % 2 !== 0) {
alert("Illegal Format ASCII Code!");
return "";
}
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
resultStr.push(String.fromCharCode(curCharCode));
}
return resultStr.join("");
}
}

function toast(content) {
uni.showToast({
title: content,
icon: 'none'
})
}


看我这个已实战可以读出加密扇区数据
2020-10-11 19:03
霍霍

霍霍

请问怎么修改密钥呢
2020-05-29 17:08
霍霍

霍霍

java.io.IOException;at android.nfc.tech.MifareClassic.connect 连接出错 '卡片连接错误!',这个是什么原因呢
2020-05-29 11:53
一只小菜鸡

一只小菜鸡

java.io.IOException:android.nfc.tech.MifareClassic.connect Undefind 遇到个这个 请问是为什么呢
2019-07-29 10:37
周BUG

周BUG

有没有写卡的代码参考一下,我参考您的读卡写了写卡的,但总是报错,最新的报错是Event Handler Error
2019-07-25 23:55
g***@126.com

g***@126.com (作者)

回复 jeffrey2022 :
手机肯定要支持nfc功能,才能作为读卡设备使用。连接nfc读卡器,你指的是电脑操作吗?手机是连不了外置的nfc读卡器吧。
2019-06-06 10:42
g***@126.com

g***@126.com (作者)

回复 coderYang :
文章最下面有附件,下载参考就可以了。
2019-06-06 10:40
coderYang

coderYang

你好,MifareClassic写入数据的代码可以提供下吗?谢谢了
2019-06-06 02:39
jeffrey2022

jeffrey2022

请问plus.android读取nfc公交卡的时候,必须是手机支持nfc才行呢,还是接nfc读卡器也可以
2019-05-30 13:45
jeffrey2022

jeffrey2022

请问各位,plus.android读取nfc公交卡的时候,必须是手机支持nfc才行呢,还是接nfc读卡器也可以
2019-05-30 13:45