版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/sgn5200/article/details/82855478

Android NFC M1卡读写&芯片卡读写(CPU卡读写)(RFID读写)

NFC 读写分几种,本文主要讲M1卡扇区读写和芯片卡读写

权限

初始化

1 onCreate( initNFC() )

2 onResume( )

3 onPause()

4 NFC设备刷卡时触发 onNewIntent(Intent)

1,标签读写

2,扇区读写

3 CPU卡读写 重头戏

NFC 读写分几种,本文主要讲M1卡扇区读写和芯片卡读写

NFC 标签读写

NFC 扇区读写

NFC 文件读写

权限

android:name="android.hardware.nfc"

android:required="true"/>

1

2

3

4

5

1

2

3

4

5

6

7

8

初始化

在Activity#onCreate()注册,在Activity#onResume()开启前台调度系统,在Activity#onPause退出前台调度。

1

1 onCreate( initNFC() )

private void initNFC() {

// 获取nfc适配器,判断设备是否支持NFC功能

nfcAdapter = NfcAdapter.getDefaultAdapter(this);

if (nfcAdapter == null) {

shotToast("当前设备不支持NFC功能");

} else if (!nfcAdapter.isEnabled()) {

shotToast("NFC功能未打开,请先开启后重试!");

}

pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,

getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);

ndef.addCategory("*/*");

// 允许扫描的标签类型

mWriteTagFilters = new IntentFilter[]{ndef};

mTechLists = new String[][]{

new String[]{MifareClassic.class.getName()},

new String[]{NfcA.class.getName()}};// 允许扫描的标签类型

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

2 onResume( )

@Override

protected void onResume() {

super.onResume();

//开启前台调度系统

nfcAdapter.enableForegroundDispatch(this, pendingIntent, mWriteTagFilters, mTechLists);

}

1

2

3

4

5

6

3 onPause()

@Override

protected void onPause() {

super.onPause();

nfcAdapter.disableForegroundDispatch(this);

}

1

2

3

4

5

4 NFC设备刷卡时触发 onNewIntent(Intent)

给伪代码,详细见下面3点分解

1

@Override

protected void onNewIntent(Intent intent) {

super.onNewIntent(intent);

//当该Activity接收到NFC标签时,运行该方法

if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) ||

NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

1,标签读写

Ndef ndef = Ndef.get(tag);//如果ndef为空表示不支持该格式

//可进行格式 如果格式化失败则不能只能换个方式

2,M1 扇区读写

MifareClassic mfc = MifareClassic.get(tag);//CPU卡时 mfc将为空

3,CPU卡 读写

NfcCpuUtilsnfc = new NfcCpuUtils(IsoDep.get(tag));

}

}

1,标签读写

/**

* 写标签

* @param ndef

* @param tag

* @param ndefMessage

* @return

* @throws IOException

* @throws FormatException

*/

private boolean writeMsg(Ndef ndef, Tag tag, NdefMessage ndefMessage) throws IOException, FormatException {

try {

if (ndef == null) {

shotToast("格式化数据开始");

//Ndef格式类

NdefFormatable format = NdefFormatable.get(tag);

format.connect();

format.format(ndefMessage);

} else {

shotToast("写入数据开始");

//数据的写入过程一定要有连接操作

ndef.connect();

ndef.writeNdefMessage(ndefMessage);

}

return true;

} catch (IOException e) {

e.printStackTrace();

shotToast("IO异常,读写失败");

} catch (FormatException e) {

e.printStackTrace();

shotToast("格式化异常,读写失败");

} catch (NullPointerException e) {

shotToast("格NullPointerException异常,读写失败");

}catch (IllegalStateException e){

shotToast("Close other technology first!");

}

return false;

}

/**

* 读取NFC标签文本数据

*/

private void readNfcTag(Intent intent) {

if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())||

NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {

Parcelable[] rawMsgs = intent.getParcelableArrayExtra(

NfcAdapter.EXTRA_NDEF_MESSAGES);

NdefMessage msgs[] = null;

int contentSize = 0;

if (rawMsgs != null) {

msgs = new NdefMessage[rawMsgs.length];

for (int i = 0; i < rawMsgs.length; i++) {

msgs[i] = (NdefMessage) rawMsgs[i];

contentSize += msgs[i].toByteArray().length;

}

}

try {

if (msgs != null) {

print(msgs.length+" 长度");

NdefRecord record = msgs[0].getRecords()[0];

String textRecord = parseTextRecord(record);

mTagText += textRecord + "\n\ntext\n" + contentSize + " bytes";

print(mTagText);

}

} catch (Exception e) {

}

}

}

1

2,扇区读写

M1扇区默认是没有密码的,但有部分人闲不住要把密码改了,因此认证过程要加密码,一般认证KeyA就行。普通卡16个扇区64块,第一个扇区等闲不能操作。每个扇区4块,从0数起,第二扇区第一块索引就是8,每个扇区前3块存数据最后一块一般存密码。实例代码读的是2扇区8块。

/**

* 扇区写

* @param tag

* @param sectorIndex 扇区索引 一般16个扇区 64块

* @return

*/

public boolean writeTAG(Tag tag,int sectorIndex) {

MifareClassic mfc = MifareClassic.get(tag);

try {

mfc.connect();

if (mfc.authenticateSectorWithKeyA(sectorIndex, new byte[]{0x42,0x53,0x4B, (byte) sectorIndex,0x4C,0x53})) { //已知密码认证 r

// the last block of the sector is used for KeyA and KeyB cannot be overwritted

int block = mfc.sectorToBlock(sectorIndex);

mfc.writeBlock(block, "sgn-old000000000".getBytes());

mfc.close();

shotToast("旧卡 写入成功");

return true;

}else if(mfc.authenticateSectorWithKeyA(sectorIndex, MifareClassic.KEY_NFC_FORUM)){ //新卡 未设密码认证 r

int block = mfc.sectorToBlock(sectorIndex);

mfc.writeBlock(block, "SGN-new000000000".getBytes());

mfc.close();

shotToast("新卡 写入成功");

} else{

shotToast("未认证");

}

} catch (IOException e) {

e.printStackTrace();

shotToast("扇区连接异常");

try {

mfc.close();

} catch (IOException e1) {

e1.printStackTrace();

}

}

return false;

}

/**

* 读扇区

* @return

*/

private String readTag(Tag tag,MifareClassic mfc,int sectorIndex){

for (String tech : tag.getTechList()) {

System.out.println("------------"+tech);

}

//读取TAG

try {

String metaInfo = "";

//Enable I/O operations to the tag from this TagTechnology object.

mfc.connect();

int type = mfc.getType();//获取TAG的类型

int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数

String typeS = "";

switch (type) {

case MifareClassic.TYPE_CLASSIC:

typeS = "TYPE_CLASSIC";

break;

case MifareClassic.TYPE_PLUS:

typeS = "TYPE_PLUS";

break;

case MifareClassic.TYPE_PRO:

typeS = "TYPE_PRO";

break;

case MifareClassic.TYPE_UNKNOWN:

typeS = "TYPE_UNKNOWN";

break;

}

metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共" + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n";

int blockIndex;

if (mfc.authenticateSectorWithKeyA(sectorIndex, new byte[]{0x42,0x53,0x4B, (byte) sectorIndex,0x4C,0x53}) ) {

blockIndex = mfc.sectorToBlock(sectorIndex);

byte[] data = mfc.readBlock(blockIndex);

metaInfo += "旧卡 Block " + blockIndex + " : " + new String(data) + "\n";

}else if( mfc.authenticateSectorWithKeyA(sectorIndex, MifareClassic.KEY_NFC_FORUM)){

blockIndex = mfc.sectorToBlock(sectorIndex);

byte[] data = mfc.readBlock(blockIndex);

metaInfo += "新卡 Block " + blockIndex + " : " + new String(data) + "\n";

}else {

metaInfo += "Sector " + sectorIndex + ":验证失败\n";

}

return metaInfo;

} catch (Exception e) {

Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();

e.printStackTrace();

} finally {

if (mfc != null) {

try {

mfc.close();

} catch (IOException e) {

Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG)

.show();

}

}

}

return null;

}

99

3 CPU卡读写 重头戏

先直接上代码,看完代码在说吧,搞这个有点心力疲惫。

下面是一个写的完全流程,if的嵌套我承认有点low,但有助于流程理解。

这里要说下外部认证过程:

devices -----获取4字节随机数---------------------> cpu 卡

devices

四个字节随机数+四个字节0 使用密钥进行DES加密,如果是8个随机数DES3加密。

将命令00 82 00 00 08 以及加密后的随机数取前8位 7f cf 90 a0 5b 9c f1 73发送

devices ----00 82 00 00 08 7f cf 90 a0 5b 9c f1 73–>cpu 卡

devices

/**

* Description : cpu卡写的工具类 命令返回90 00 表示成功

* CreateAuthor: Cannan

* CreateTime : 2018/9/22 18:53

* Project : TestNFC

*/

public class NfcCpuUtils {

/**

* 1. 在“COS命令框”输入“00A40000023F00”,然后点击“发送命令”,进入主目录

*/

private final byte[] CMD_START = new byte[]{0x00, (byte) 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00}; //6f,15,84,e,31,50,41,59,2e,53,59,53,2e,44,44,46,30,31,a5,3,88,1,1,90,0,

/**

* 2. 复合外部认证(秘钥:FFFFFFFFFFFFFFFF,秘钥标识号:00)

*/

private byte[] CMD_KEY = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};

/**

* 2.1 获取4位 随机码 {0x00, (byte) 0x84, 0x00, 0x00, 0x04}

*/

private final byte[] CMD_GET_RANDOM = {0x00, (byte) 0x84, 0x00, 0x00, 0x04};

private final byte[] CMD_DEL = {(byte) 0x80, 0x0E, 0x00, 0x00, 0x00}; //3.删除主目录下的所有文件:800E000000(注意:这个命令会删除主目录下的所有文件)

// 4. 建立外部认证秘钥 4.1选择根目录(00A4000000)

// 4.2建密钥文件 (80 E0 00 00 07 3F 00 B0 01 F0 FF FF

// 4.3创建外部认证密钥 (80 D4 01 00 0D 39 F0F0 AA 55 FFFFFFFFFFFFFFFF)

private final byte[] CMD_CREATE_DIR = {0x00, (byte) 0xA4, 0x00, 0x00, 0x02,0x3f,0x00};

private final byte[] CMD_CREATE_KEY = {(byte) 0x80, (byte) 0xE0, 0x00, 0x00, 0x07, 0x3F, 0x00, (byte) 0xB0, 0x01, (byte) 0xF0, (byte) 0xFF, (byte) 0xFF};

private final byte[] CMD_CREATE_OUT_KEY = {(byte) 0x80, (byte) 0xD4, (byte) 0x01, (byte) 0x00, (byte) 0x0D, (byte)0x39, (byte) 0xF0, (byte) 0xF0, (byte) 0xAA

, (byte) 0x55, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};

//5 建立访问自定义文件的密钥文件

private final byte[] CMD_ACCESS = {(byte) 0x80, (byte) 0xE0, (byte) 0x00, (byte) 0x01, (byte) 0x07, (byte) 0x3F, (byte) 0x01, (byte) 0x8F, (byte) 0x95, (byte) 0xF0, (byte) 0xFF, (byte) 0xFF};

// 填充密钥123456

private final byte[] CMD_ACCESS_INTO = {(byte) 0x80, (byte) 0xD4, (byte) 0x01, (byte) 0x01, (byte) 0x08, (byte) 0x3A, (byte) 0xF0, (byte) 0xEF, (byte) 0x44, (byte) 0x55, (byte) 0x12, (byte) 0x34, (byte) 0x56};

//6. 创建自定义文件,标识为005(80E000050728000FF4F4FF02)

private final byte[] CMD_ACCESS_FILE = {(byte) 0x80, (byte) 0xE0, (byte) 0x00, (byte) 0x05, (byte) 0x07, (byte) 0x28, (byte) 0x00, (byte) 0x0F, (byte) 0xF4, (byte) 0xF4, (byte) 0xFF, (byte) 0x02};

//7.写数据到文件标识为0005的文件

//7.1选中该文件(00A40000020005)

// 7.2写数据“112233445566”到该文件(00D6000006112233445566)

private final byte[] CMD_ACCESS_FILE_CHOOICE = {(byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x05};

private final byte[] CMD_ACCESS_FILE_WRITE = {(byte) 0x00, (byte) 0xD6, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x88, (byte) 0x88, (byte) 0x88, (byte) 0x44, (byte) 0x55, (byte) 0x66};

// 声明ISO-DEP协议的Tag操作实例

private final IsoDep tag;

public NfcCpuUtils(IsoDep tag) throws IOException {

// 初始化ISO-DEP协议的Tag操作类实例

this.tag = tag;

tag.setTimeout(5000);

tag.connect();

}

public byte[] wirte() throws IOException {

byte[] resp = tag.transceive(CMD_START); //1 进入主目录

if (checkRs(resp)) {

print("1 进入主目录成功");

resp = tag.transceive(CMD_GET_RANDOM); //2 获取随机码

if (checkRs(resp)) {

print("2 获取随机码");

byte[] random = {resp[0], resp[1], resp[2], resp[3], 0x00, 0x00, 0x00, 0x00};//3 随机码4个字节+4个字节0

byte[] desKey;

try {

desKey = encrypt(random, CMD_KEY); //4 生产加密后的随机码

print("3 生产加密后的随机码");

printByte(desKey);

} catch (Exception e) {

e.printStackTrace();

desKey = null;

}

//00 82 00 00 08 7f cf 90 a0 5b 9c f1 73

if (desKey != null && desKey.length > 8) {

byte[] respondKey = {0x00, (byte) 0x82, 0x00, 0x00, 0x08, desKey[0], desKey[1], desKey[2], desKey[3], desKey[4], desKey[5], desKey[6], desKey[7]};

print("4 生产加密后的随机码命令");

printByte(respondKey);

resp = tag.transceive(respondKey); //5 将加密后的随机码发送,注意此处第四字节表示密码标识符00,

}

if (checkRs(resp)) {

print("5 外部认证成功");

resp = tag.transceive(CMD_DEL);

if (checkRs(resp)) {

print("6 删除目录成功");

resp = tag.transceive(CMD_CREATE_DIR);

if (checkRs(resp)) {

print("7 选择目录");

resp = tag.transceive(CMD_CREATE_KEY);

if (checkRs(resp)) {

print("8 建立目录");

resp = tag.transceive(CMD_CREATE_OUT_KEY);

if (checkRs(resp)) {

print("9 创建外部认证密钥成功");

resp = tag.transceive(CMD_ACCESS);

if (checkRs(resp)) {

print("10 建立访问自定义文件的密钥文件成功");

resp = tag.transceive(CMD_ACCESS_INTO); //11 填充密钥123456

if (checkRs(resp)) {

print("11 填充密钥123456成功");

resp = tag.transceive(CMD_ACCESS_FILE); //12 创建自定义文件,标识为005

if (checkRs(resp)) {

print("12 创建自定义文件,标识为005成功");

resp = tag.transceive(CMD_ACCESS_FILE_CHOOICE); // 13 选中该文件0005

if (checkRs(resp)) {

print(" 13 选中该文件0005成功");

resp = tag.transceive(CMD_ACCESS_FILE_WRITE); //14 写数据“112233445566”到该文件

if (checkRs(resp)) { //15 应该有关闭连接

print("14 写数据“112233445566”到该文件成功");

return "01".getBytes();

}

}

}

}

}

}

}

}

}

}

}

}

return null;

}

private boolean checkRs(byte[] resp) {

String r = printByte(resp);

Log.i("---------", "response " + r);

int status = ((0xff & resp[resp.length - 2]) << 8) | (0xff & resp[resp.length - 1]);

return status == 0x9000;

}

private String printByte(byte[] data) {

StringBuffer bf = new StringBuffer();

for (byte b : data) {

bf.append(Integer.toHexString(b & 0xFF));

bf.append(",");

}

Log.i("TAG", bf.toString());

return bf.toString();

}

private void print(String msg) {

Log.i("TAG", msg);

}

/**

* Description 根据键值进行加密

* 随机码4个字节+4个字节0

*

* @param data

* @param key 加密键byte数组

* @return

* @throws Exception

*/

public byte[] encrypt(byte[] data, byte[] key) throws Exception {

}

}

注意接收和处理返回的信息,CPU卡常用的APDU指令

参考文献:很多博客,记不得了,RFID多功能读卡器说明

https://blog.csdn.net/qq_34075348/article/details/77877306

FMCOS2.0用户手册 50-70

如果需要源码:下载地址https://download.csdn.net/download/sgn5200/10688898

————————————————

版权声明:本文为CSDN博主「豆汤包谷饭」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/sgn5200/article/details/82855478

c语言读写nfc,Android NFC M1卡读写芯片卡读写(CPU卡读写)(RFID读写)相关推荐

  1. android开发 nfc,Android NFC开发概述

    Near  Field Communication (NFC) 为一短距离无线通信技术,通常有效通讯距离为4厘米以内.NFC工作频率为13.65 兆赫兹,通信速率为106 kbit/秒到 848kbi ...

  2. android pda界面美化,安卓设备扫描cpu卡和rfid超频卡插件cordova-plugin-pda

    1.该插件只支持安卓设备,可以扫描上海复旦fm1216卡(CPU卡),s50卡,rfid超频卡等:这是我写的一个cordova插件,下面是安装步骤 2.安装方法: cordova plugin add ...

  3. vb6荣士读写器ISO-14443-A系列M1 S50、S70、F08卡源码

    发卡器介绍:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-17663462238.11.1d75789eyZBxwL&id=6153 ...

  4. NFC开发 —————ID卡、IC卡(M1卡、CPU卡)的区别(三)

    Android NFC开发(一) NFC开发 -----实现NFC手机做门禁卡的方法(二) NFC开发 -----实用工具以及开发文档(四) IC卡的定义 : IC(Integrated Circui ...

  5. Android NFC读卡以及NFC的三种模式

    第一步添加权限: <uses-permission android:name="android.permission.NFC" /> <uses-permissi ...

  6. 复旦FM17522芯片读写M1卡(S50/S70)、CPU卡要点摘录

    目录 概述 卡的存储结构/文件结构 M1卡存储结构 复旦CPU卡的文件结构 卡的权限管理方式 M1卡控制字 CPU卡安全状态寄存器 卡的认证 M1卡的三次互相认证 复旦CPU卡的外部认证和内部认证 机 ...

  7. android NFC 开发学习笔记(1)

    由于工作需求,最近在研究android nfc开发,借鉴了很对大神的文章在此记录自己的学习过程: 大家学习android开发建议首选android开发文档,该文档在你下载的sdk中,路径:/sdk/d ...

  8. 【IoT】NFC CPU 卡在实际产品中的应用

    1.目前不同厂家采用的 CPU 卡方案分为四类 1.1.ID 方案 使用 CPU 卡的序列号来识别卡,这种方案与 ID 卡方案没有本质区别,在门禁.考勤.停车场系统中较多见,消费系统中采用这种方案需要 ...

  9. IC 卡、M1 卡、CPU 卡、SAM 卡、PSAM 卡的联系与区别

    一. 技术方面(非接触式 IC 卡) 1. 逻辑加密卡又叫存储卡,卡内的集成电路具有加密逻辑和 EEPROM (电可 擦除可编程只读存储器). 2. CPU 卡又叫智能卡, 卡内的集成电路包括中央处理 ...

最新文章

  1. iRank: 基于互联网类脑架构的阿尔法鹰眼发展趋势评估
  2. http://www.csdn.net/
  3. Java黑皮书课后题第7章:7.16(执行时间)编写程序,随机产生一个包含100 000个整数的数组和一个关键字。估算调用程序清单7-6中的linearSearch方法的执行时间
  4. c#中设计器中窗体释放  protected override void Dispose(bool disposing)改写解决部分窗体线程释放不彻底问题
  5. Javascript报错Failed to execute ‘querySelectorAll‘ on ‘Document‘: ‘#123456‘ is not a valid sele
  6. linux container框架,理解和配置LinuxContainerExecutor
  7. Spring boot (6)---SpringMVC框架和spring Boot的区别
  8. 操作系统锁的实现方法有哪几种_java 偏向锁、轻量级锁及重量级锁synchronized原理...
  9. shell:概述、脚本编写、变量的简单基础
  10. 配置eclipse编写html/js/css/jsp/java时自动提示
  11. adb下载、安装、环境配置
  12. 如何把Word文件设置成不能编辑
  13. c++国际象棋上的麦粒
  14. 路由器恢复出厂设置后dns服务器未响应,路由器恢复出厂设置后连不上网怎么办?...
  15. docker更换国内镜像(网易docker镜像)解决docker下载镜像慢问题
  16. 第6章第6节:颜色搭配:配色万金油之色相配色方案 [PowerPoint精美幻灯片实战教程]
  17. Java的主要应用领域有哪些?
  18. 3dmax转化html,VRayMtl Converter材质转换3dsmax插件V3.97版
  19. tomcat与servlet共存时报错 org.apache.catalina.LifecycleException:
  20. layui点击弹出层中按钮,关闭弹出层

热门文章

  1. java 自动装载_springboot自动装载
  2. 从实战中了解数据开发全流程——DataWorks OpenAPI实战
  3. 游戏美术本地化技巧:不重新创作,几个简单步骤修改出一张完美的游戏配图
  4. 专访《王者荣耀》美术总监:用6年研究东方美学
  5. 巨人网络李东旭:关于提高游戏流畅性的那些事
  6. 五个简单的原则,带你写出整洁代码
  7. VirtualBox压缩vmdk、vagrant打包box一口气全对
  8. LNMP单机高并发的简单优化
  9. springboot 打jar 包部署时 读取外部配置文件
  10. 第一章 SDN介绍 (附件2)【SDNNFV基础、云计算】