我是靠谱客的博主 美丽棉花糖,这篇文章主要介绍Android NFC M1卡读写&芯片卡读写(CPU卡读写)(RFID读写)NFC 读写分几种,本文主要讲M1卡扇区读写和芯片卡读写,现在分享给大家,希望可以做个参考。

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 文件读写

权限

复制代码
1
2
3
4
5
6
<uses-feature android:name="android.hardware.nfc" android:required="true"/> <uses-permission android:name="android.permission.NFC"/>
复制代码
1
2
3
4
5
6
7
8
9
<activity android:name=".ReadTextActivity" android:launchMode="singleTop"> <intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> <action android:name="android.nfc.action.TECH_DISCOVERED" /> <data android:mimeType="text/plain"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>

初始化

复制代码
1
2
在Activity#onCreate()注册,在Activity#onResume()开启前台调度系统,在Activity#onPause退出前台调度。

1 onCreate( initNFC() )

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()}};// 允许扫描的标签类型 }

2 onResume( )

复制代码
1
2
3
4
5
6
7
@Override protected void onResume() { super.onResume(); //开启前台调度系统 nfcAdapter.enableForegroundDispatch(this, pendingIntent, mWriteTagFilters, mTechLists); }

3 onPause()

复制代码
1
2
3
4
5
6
@Override protected void onPause() { super.onPause(); nfcAdapter.disableForegroundDispatch(this); }

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

复制代码
1
2
给伪代码,详细见下面3点分解
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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,标签读写

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/** * 写标签 * @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; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** * 读取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 + "nntextn" + contentSize + " bytes"; print(mTagText); } } catch (Exception e) { } } }
复制代码
1
2

2,扇区读写

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** * 扇区写 * @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() + "Bn"; 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; }

3 CPU卡读写 重头戏

先直接上代码,看完代码在说吧,搞这个有点心力疲惫。
下面是一个写的完全流程,if的嵌套我承认有点low,但有助于流程理解。
这里要说下外部认证过程:
devices -----获取4字节随机数---------------------> cpu 卡
devices <--------随机数+90 00--------------------- cpu 卡
四个字节随机数+四个字节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 <------------- 90 00---------------------------- cpu 卡

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/** * 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

最后

以上就是美丽棉花糖最近收集整理的关于Android NFC M1卡读写&芯片卡读写(CPU卡读写)(RFID读写)NFC 读写分几种,本文主要讲M1卡扇区读写和芯片卡读写的全部内容,更多相关Android内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(84)

评论列表共有 0 条评论

立即
投稿
返回
顶部