开发者学堂课程【嵌入式之 RFID 开发与应用2020版:RFID 卡片充值扣款流程】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/665/detail/11125
RFID 卡片充值扣款流程
内容介绍:
一、前言
二、为什么重置扣款更加安全
三、充值的基本演示
四、扣款的基本操作
五、充值的原理
六、扣款和充值函数的说明
七、RFID 总结
一、前言
能够通过密码验证并且完成数据的读写,其实数据的读写它的可靠性并不是特别高,这里存在一个问题,如果通过卡片保存了个人信息,那么卡坏了怎么办?一张卡片价格是几毛钱一块钱,用户可以买 10 张卡,可以往 10 张卡里面存入个人信息。
另外,就是信息单纯的读写其实不是特别安全,真正的更安全的方式是通过充值和扣款的方式去操作卡片,接下来要考虑的充值扣款的问题。
二、为什么重置扣款更加安全
充值和扣款之前是直接读写,每一个 block 是 16 个字节,16 个字节可以随便用,但是充值扣款里面 16 个字节是没有用完的,它只用到 16 个字节里面的前 4 个字节,后面的 12 个字节全部拿来做了校验,所以它更安全的,因为它做了校验,数据有错误校验就会失败,写入会失败读取也会失败,所以它就更安全。
如果不需要这么高的安全性,直接读写没有问题的,所以充值扣款它有一个前提就是它的数据是要经过校验的,在充值和扣除之前,数据的格式必须要有一个约定
int main(void)
unsigned char type[2],ret,card_id[4];int i,ops;
int snr = 3;unsigned char b_data[ 16],block = 2;
unsigned char a_key[6] = {oxff,oxff,oxff,oxff,oxff,oxff};
unsigned char def_data[16] ={ 0x00,0x00,0x00,0x00,oxff ,oxff ,oxff ,oxff ,0x00,0x00,0x00,0xo0,block ,~block,block~block}
unsigned char inc_dec[4] = {ox0o,ox00,ox00,oxo1};
rfid_reset();
rfid_carda_init();
while(1){
ret = rfid_carda_request(PICC_REQALL,type);
if(ret == OK){
printf( "card type = ox%02×%02x\n",type[0],type[1]);
// waitcardoff();
}else continue;
ret = rfid_anticol1(card_id);
处理数据的默认格式必须满足校验的格式,校验格式如下前4个字节是保存的实际数据,接下来 4 个字节了,接下来 4 个字节是实际数据的按位取反。也就是说双重保障,比前面的某个 bit 位,比如它电子存储设备,它可能出现未翻转未来,可能里面某一位翻转为 1,但是它一直唯一,改不了它,那值不就出错了。在读写的时候它会跟后面的值进行比较,取反之后值不相等,数据就会有问题的,就不能进行充值和扣款的操作,所以它有一个取反操作。
然后再接下来 4 字节又是实际数据,第 1 遍是原始数据,第 2 遍取反,第 3 遍还是原始数据,最后 4 个字节是当前块的原值和取反值,就是当前的数据到底存在于哪一块,就是充值的金额,比如说是第 0 块,那 block 就是 0,取反后就是 1。但是需要注意的是这里的块 block 不是 0~63,而是 0~3,它是针对于当前扇区而不是整个扇区。之间读写数据的时候,它是 0~63block,现在是 0~3block,不要搞错了。有了之后就必须先符合代码的规定充值才会成功。可以去看一下之前的一读二写三充值四扣款五初始化,初始化其实就是把 before 通过 write 的方式写进去才能进行充值和扣款。
三、充值的基本演示
接下来演示,因为卡片刚才已经输入了一个 ABCD 进去,看一下能不能往里面充值扣款成功。依然是先退出,然后进入到最后一个程序
数据跑起来后把卡片贴近阅读器,选择 1read,结果输入为:
[root@qfedu 04_rfid_inc_dec]#lsMakefile
rfid lib.h
Untitled Project.si4project rfid_lib.odemo
uart _init.c
main.c
uart init.h
main.o
uart_init.o
rfid lib.c
[root@qfedu 04_rfid_inc_dec]#. / demoversion=fc
doing close !
version=88
card type = 0x040o
card ID:0x34 0xa9 0xa5 0x4d
1.read;2.write;3.Increment4.Decrement5.init
1
block data:0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ox00 0x00 0x00 0x00 0x00 0x00 0x00 ox00
结果是有问题的,read 后结果为 0,它的值应该是 ABCD,看一下是不是它程序的读的扇区跟之前不一样,int snr=3 ; unsigned char b_ data[16], block=2;
之前是第 3 个扇区的第 0 个 block,现里写的是第 2 个 block,把它改成 block=0,改成 0 就是把之前的数据读出来,重新编译一下再运行,输出结果为:
card type = 0x0400
card ID:0x34 0xa9 0xa5 0x4d
1. read;2. write ;3. Increment4. Decrement5.init
block data:0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x00 0
x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
abcdefg
这次可以把之前的 ABCD 读出来,读出来之后,把卡拿开,拿开了之后,重新再贴近阅读器来进行充值。充值的金额每次增长一块钱,1 代表的含义需要自己进行定义。重新贴上这张卡,然后输入一个3就是充值,输出结果为:
card type = 0x0400
card ID:0x34 0xa9 0xa5 0x4d
1. read;2. write;3. Increment4.Decrement5.init
Increment error !
card type = 0x0400
card ID:0x34 0xa9 0xa5 0x4d
1. read;2. write;3. I ncrement4. Decrement5.init
结果显示出错,第 1 步充值错误,它是充不进去的,重新把卡片贴近阅读器,先输入一个 5 写入成功初始化成功,拿开卡片重新贴上,然后输入一个 1 把数据读出来看一下,此时读到的数据如下
write success!
card type = 0x0400
card ID:0x34 0xa9 0xa5 0x4d
1. read;2.write; .3. I ncrement4. Decrement5.init
block data:0x00 0x00 0x00 0x00 0xff 0xff 0xff 0xff 0x00 0x00 0x00 0x00 0x00 0xff 0x00 0xff
因为操作是第 0 块,所以出现 0F。接下来充值充一块钱进去,卡片贴近阅读器,输入一个 3 进行充值,输入结果为:
card type = 0x0400
card ID:0x34 0xa9 0xa5 0x4d
1. read;2. write;3. Increment4. Decrement
5.init
Increment success !
Transfer success !
充值成功了,接下来看一下卡里面的金额,再把卡贴上去
通过第 1 个 read 可知里面的 0 现在变成了 1 就表示有充值进去,而且后面的校验也变了,之前的FF现在变成FE了,还可以继续充值,贴上卡片后拿开,充值成功再拿开再贴上。
四、扣款的基本操作
卡片贴近阅读器,输入 4,扣款之后再贴上去,输入 1 又变成了一块钱,这就是整个的充值扣款的整个过程。
五、充值的原理
接下来要看一下它到底是怎么充进去的,实际的数据保存的空间只有 4 个字节
case 3:
ret = PcdIncrement( snr*4+block,inc_ dec) ;
if(ret == CMD_ SUCCESS){
puts( " Increment success!");
ret = PcdTransfer( snr*4+block);
if(ret == CMD_ SUCCESS)
puts("Transfer success!");
else
puts("Transfer error!");
WaitCardoff();
}else
puts("Increment error!' ' ) ;
break;
首先输入充值的金额,然后确定金额被传递到哪一个扇区里面以及充到扇区具体的哪一块。
char P cdIncrement(unsigned char addr, unsigned char *pData)
char status = CMD_ SUCCESS;
unsigned int
unLen ;
unsigned char i, ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_ _INCREMENT;
ucComMF522Buf[1] = addr;
CalulateCRC(ucComMF522Buf,2 , &ucComMF522Buf[2]);
status = rfid_ cmd(PCD_ TRANSCEIVE , ucComMF522Buf ,4, ucComMF522Buf, &unLen);
if ((status != CMD_ SUCCESS) 11 (unLen = 4) 11 ((ucComMF522Buf[0] & 0x0F) != exBA))
{
status = CMD_ FAIL; }
if (status = CMD_ SUCCESS)
{
for (i=0; i<4; i++)
{
ucComMF522Buf[i] = *(pData+i);
CalulateCRC (ucComMF522Buf ,4, &ucComMF522Buf[4]);
rfid_ cmd(PCD_ TRANSCEIVE , ucComMF522Buf, 6, ucComMF522Buf, &unLen);
函数的主要作用,就是实现就是充值的初始化,充值并没有真正的把金额写入到的卡片里面去。先来看一下,首先是的块的地址,然后充的钱,就代表要对卡片进行充值操作,之后接下来要对它进行 crc 的校验。
CalulateCRC(ucComMF522Buf. , 2, &ucComMF522Buf[2]);
status = rfid_ .cmd(PCD_ TRANSCEIVE , ucComMF522Buf , 4, ucComMF522Buf , &unLen);
if ((status != CMD_ SUCCESS) |I (unLen != 4) II ((ucComMF522Buf[0] & 0x0F) != 0x0A) )
{ status = CMD_ FAIL; }
if (status = CMD_ SUCCESS)
{
for (i=0; i<4; i++)
ucComMF522Buf[i] = *(pData+i);
}
CalulateCRC(ucComMF522Buf , 4, &ucComMF522Buf[4]);
rfid_ cmd(PCD_ TRANSCEIVE , ucComMF522Buf , 6, ucComMF522Buf , &unLen);
return status ;
然后通过 rfid_cmd 传输命令把它写入到 buf,写完后会返回数据,对返回的数据再次做 crc 校验,PcdIncrement 的操作只是把钱通过 rfid_cmd,把它保存到了 fifodatareg 里面,钱并没有写到卡里面去。中间存在和卡片发生信息的交互的操作。
六、扣款和充值函数的说明
PcdIncrement () 和 PcdDecrement(), 这两个函数是做数据格式的合法性检测,也就是充的值符不符合卡片内部的数据格式,如果不符合是不会对块进行操作的。如果符合还需要调用另一个 PcdTransfer 接口。PcdTransfer 接口要下达传输指令才能将缓充区的充值或者扣款金额写入块中。
char PcdTransfer(unsigned char addr)
{
char status = CMD_ _SUCCESS;
unsigned int
unLen ;
unsigned char ucComMF 522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_ _TRANSFER;
ucComMF522Buf[1]
= addr;
CalulateCRC( ucComMF522Buf , 2 , &ucComMF522Buf[2]);
status = rfid_ .cmd(PCD_ _TRANSCEIVE , ucComMF522Buf , 4,ucComMF 522Buf, &unLen) ;
if (status != CMD_ _SUCCESS)
status = CMD_ FAIL;
return status ;
通过 PCDTRANSFER 指令才能真正的写入,上面的 PCDINCREMENT 是写不进去的,可以去测试一下,把后面代码给屏蔽掉,上面提示充值成功了,但是实际上卡里面是没有存储的,可以读出来看。所以整个充值扣款的过程就非常的清晰,从块的读写到块的充值扣款,就通过的标准的金融卡的指令
以及 PCP 的命令
pcd 的命令是由各个厂家提供的,命令可能厂家都不是特别一样,但是金融卡的指令都是规范的,至少中国是通用的。
七、RFID 总结
各个扇区的默认秘钥是多少?
如何对加密扇区的某一数据块进行读写?
如何修改扇区的秘钥?具体步骤是啥?
控制字有什么作用,如何进行修改?
通过上面的内容,这 4 个问题是不是能够进行回答,各个扇区的默认的密钥是多少,默认全是 1。如何对加密扇区的某一数据块进行读写?首先是对某一块进行读写,对块所在的扇区进行密码的认证,而且密码要看权限,就是权限里面验证密码之后允不允许对块进行读写。如果允许的情况下才能进入系统,如果不允许还要去修改它的权限,就是修改中间的 4 个字节。
如果不允许读写,中间的 4 个字节需要改一改,改成它允许为止。
另外就是如何修改扇区的密钥。首先要密码验证通过,其次就是权限允不允许修改A 密或者是 B 密,允不允许修改需要看表
如果表中不允许修改,还要再次回来修改权限,把权限改到它允许修改 A 密或者 B密的密码为止。
控制字的修改和的密码的修改是一样的,也就是说除了改密码以外,还可以修改控制字,控制字也依然遵循着以下的条件
A/B 密码到底对谁进行验证通过之后才可以进行改,读的权限其实很高,但是改的权限并不高。只要符合图中的三种情况就可以进修改。以上就是整个的 RFID 从基本的原理介绍、协议、阅读器、卡片做了一个详细的介绍。