本例应用场合:对IC卡的读写、加密,个人及各企事业单位可以根据自己的加密方式对卡片进行处理后使用。本例在VS2010及以上编译环境中测试通过。
运行界面:
想调试你得有这些:
德卡D3非接触式IC卡读写器(USB免驱)
IC芯片卡
D3 SDK
在操作之前先了解一下IC卡的一些基础知识(本例以M1卡为例):
M1卡有16个扇区,即0到15扇区,每个扇区4块,每块16个字节,以块为存取单位,要读取扇区中的数据,首先要进行密码校验(校验读卡设备中的密码和卡上的密码);要读取哪个扇区的数据就要校验哪个扇区的密码,dc_read或dc_read_hex方法是用的地址是绝对地址(每个扇区4个地址,16个扇区共64个地址,即0到63),校验完0扇区的密码后,能读取0到3地址上的数据,校验完1扇区的密码后,能读取4到7地址上的数据,以此类推。假设扇区号是 sNo,数据块号是dNo,则:sNo号扇区的最小区块号为:4 * sNo,最大区块号为:4 * sNo + 3。
存储结构
第0扇区的块0(即绝对地址0块),它用于存放厂商代码,已经固化,不可更改。
每个扇区的块0、块1、块2为数据块,可用于存贮数据。
每个扇区的块3为控制块,包括了密码A、存取控制、密码B。具体结构如下:
A0 A1 A2 A3 A4 A5 FF 07 80 69 B0 B1 B2 B3 B4 B5
对IC卡操作的基本步骤:
初始化InitIC() -> 寻卡ReadIc() -> 校验密码getKeys(string cardID) -> 加密操作LoadKeysToCard(string cardID) -> 关闭串口ExitIC()。
本例测试用卡片的特定加密方式:
- 卡号+“ZMATRIX”+扇区号。
- 经MD5加密后取前12位字符形成字符串。
- 对字符串进行BCD码加密。
- 组合秘钥形成完整控制块数据。
- 写入卡片进行加密。
核心函数的代码:
1 /// <summary> 2 /// 寻卡 3 /// </summary> 4 /// <returns>物理卡号</returns> 5 public string ReadIc() 6 { 7 uint st = 4; 8 ulong icCardNo = 0; 9 char str = (char)0; 10 string cardID = ""; 11 int card = dc_card((Int16)_icdev, str, ref icCardNo); 12 if(card == 0) 13 { 14 string strCardID = Ten2Hex(icCardNo); 15 cardID = FormatCardID(String.Format("{0:X}", strCardID)); 16 cardUniID = cardID; 17 MessageBox.Show("寻卡成功!"); 18 } 19 else{ 20 MessageBox.Show("寻卡失败!"); 21 } 22 return cardID;//16进制 23 }
1 /// <summary> 2 /// 获取各扇区对应秘钥,核心函数 3 /// </summary> 4 /// <param name="cardID">卡物理号</param> 5 private void getKeys(string cardID) 6 { 7 int len = 16; 8 byte[] bMoren = { 255, 255, 255, 255, 255, 255 }; 9 byte[] bKey = new byte[6]; 10 byte[] bData = new byte[16]; 11 string[] befEncrypt = new string[len]; 12 string[] aftEncrypt = new string[len]; 13 for(int sector = 0 ; sector < len ; sector++) 14 { 15 befEncrypt[sector] = cardID + "ZMATRIX" + sector.ToString(); 16 aftEncrypt[sector] = MD5.MD5Encrypt(befEncrypt[sector], 32).Substring(0, 12); 17 bKey = BCDEncrypt.str2Bcd(aftEncrypt[sector]); 18 int checkNum = dc_authentication_passaddr(_icdev, 4, (sector * 4 + 3), bKey); 19 if (checkNum == 0) 20 { 21 MessageBox.Show("验证第" + (sector * 4 + 3) + "块密码成功!"); 22 int readData = dc_read(_icdev, sector * 4 + 3, bData); 23 }else{ 24 MessageBox.Show("验证第" + (sector * 4 + 3) + "块密码失败!"); 25 } 26 } 27 }
1 /// <summary> 2 /// 新卡加密,核心函数 3 /// </summary> 4 /// <param name="cardID">新卡序列号(物理卡号)</param> 5 private void LoadKeysToCard(string cardID) 6 { 7 byte[] bKey = new byte[6]; 8 string[] befEncrypt = new string[16]; 9 string[] aftEncrypt = new string[16]; 10 byte[] bMoren = { 255, 255, 255, 255, 255, 255 }; 11 lbl = getLabel();//Label集 12 lblTips.Text = ""; 13 for (int sector = 0; sector < 16; sector++) 14 { 15 int checkNum = dc_authentication_passaddr(_icdev, 4, (sector * 4 + 3),bMoren); 16 if (checkNum == 0) 17 { 18 befEncrypt[sector] = cardID + "ZMATRIX" + sector.ToString(); 19 aftEncrypt[sector] = MD5.MD5Encrypt(befEncrypt[sector], 32).Substring(0, 12); 20 bKey = BCDEncrypt.str2Bcd(aftEncrypt[sector]); 21 int writeOk = dc_write(_icdev, (sector * 4 + 3), CombineKey(bKey)); 22 if (writeOk == 0) 23 { 24 tip[sector] = 0; 25 }else{ 26 tip[sector] = 1; 27 } 28 }else{ 29 tip[sector] = 1; 30 lblTips.Text = "失败:【该卡可能已加密】"; 31 lblTips.ForeColor = Color.Red; 32 } 33 } 34 Set_Text = new set_Text(set_LalColor); 35 thread1 = new Thread(new ThreadStart(run)); 36 thread1.Start(); 37 }
源码下载:关注公众号『几行简码』,回复【读卡】即可获取免费下载链接。
原创文章,转载请务必先查阅公众号内【转载须知】。