说说读卡应用那点事儿,以SCL010为例
前一阵子的项目, 跟读卡应用有关,这篇博客算是我学习智能卡方面知识的而一个总结,也可以看作这个领域的一个很简单的简介,他写得很不书面,更像是沿着我自己认识过程的总结。所以这里面有很多我自己理解的地方,但是还好,我自己发挥的,可能不靠谱的地方,我都已经明确的标明了出来。
下面咱们正是开始~
说道读卡应用, 自然会涉及到”IC卡”和”读卡器”这两个概念。
下面就让我们以SCL010读卡器为例, 从读卡器说起。
一 读卡器
SCL010是SCM公司推出的一款contactless读卡器
http://www.scmmicro.com/products-services/smart-card-readers-terminals/contactless-dual-interface-readers/scl010.html
他是通过USB接口与PC相连。别小看这个读卡器,它支持的卡的种类很多,包括Sony的Felica,还有Mifare的各种卡,还有ISO 14443 type A & B卡。这一点也是我们项目选择他的原因之一。
二 APDU & ESCAPE & CCID
接下来是读卡,为了读卡通常会包括两件事儿,一件事是关于读卡的命令,另一件事儿是关于设置的命令。
读写卡的数据和信息时,通常使用的是APDU。
而做一些关于读写卡的设置的时候,通常使用的是ESCAPE命令。
至于我们使用的电脑如何把这两个命令传达给读卡器与卡,那是CCID负责的事情。
是的,APDU和ESCAPE都被包含在CCID报文之中。(注:在真实的CCID协议中,包装APDU和ESCAPE的,是两个不同格式的CCID命令)
这就好比网络七层协议一样。为了发送HTTP报文,他会被包在TCP报文里面,为了发送TCP报文,会把它包裹在IP报文中一样。
所以仍以SCL010为例,为了读卡,PC上面做的最本质的工作就是下面这几个步骤
1. PC把包含了APDU或者ESCAPE命令的CCID命令,通过USB驱动,写入读卡器
2. 读卡器通过解析CCID命令,得到了用户所要执行的APDU或者ESCAPE命令,自然也就知道了它应该做哪些事情。
3. 读卡器执行这些APDU或者ESCAPE命令,并生成相应的处理结果,这些结果有可能是用户想要的数据,也有可能是用户操作成功或者失败的标志位,但这些结果都会使用CCID报文的格式来描述。
4. PC的Driver这时候便可以从这个设备中读入结果,因为结果是CCID报文的形式,因此为了得到最终的结果,还需要对CCID报文数据进行解析 (这里通常的都是Driver去读卡器的缓冲区里面读结果, 详细可以参照”五 细说USB驱动,还有读卡器的USB结构。”)
三 PC/SC
如前所述,APDU,ESCAPE相当于HTTP,是更面向用户更高抽象层次的问题。而CCID则好像TCP协议,他们会保证APDU以及ESCAPE命令能够正确的下达给读卡器,进而读卡器可以解析出这些APDU,ESCAPE命令并执行之。
那么PC/SC的价值就在于此,他的目的是帮助开发人员,使之从组织以与解析CCID报文的工作中解放出来。有了PC/SC之后,用户只需要把他想执行的APDU,ESCAPE命令,作为参数,传递给相应的PC/SC函数即可。这些PC/SC函数,会自动为这些APDU,ESCAPE命令包装成相应的CCID报文,并利用驱动,发送给读卡器,在PC/SC得到了读卡器传递回来的CCID格式的结果之后,也会自动的对其进行解析,然后将APDU,ESCAPE命令的最终执行结果返回给用户。
这整个的PC/SC框架,需要微软以及各厂商来共同来完成,微软提供上层的API结构。各厂商提供底层的实实现以及驱动。
另外多说一嘴,往白了说,微软与读卡器厂商之间借以通信的协议便是CCID,这也不奇怪,我查看CCID标准的时候也发现,CCID的主要贡献者就是微软以及这些读卡器厂商们。学习到这里,我们也可以意识到,CCID协议之所以被提出,我理解目的其实就在于为各个厂商所生产的读卡设备提供统一的抽象,而享受这种抽象带来的好处的最直接的方法之一,便是使用忽略细节,直接使用PS/SC与读卡器打交道。
四 如果我们的设备上没有PS/SC也没有厂商的驱动,我们要怎么办?
这种现象是存在的,如果读卡器厂商不愿意支持我们的设备,我们还想使用这款读卡器的话,那就只有我们自己来做这件事情啦。这种可能是存在的, 我们目前的情况便是这样的, 我们要为我们的打印机接一个USB读卡器, 然而这些嵌入式驱动的事情要我们自己从头做起.
我们仍以SCL010为例,如果没有了PC/SC,我们便需要像下面这样从头搞起。首先,不管我们是去下载也好,自己搞一个也好,总之首先我们要搞一个USB驱动(的确有一些usb class driver是可以下载到的,我们曾下载过kashiwano masahiro 的Universal USB Device Driver, uusbd )。然后再用C或者C++整个API,自己处理处理APDU,ESCAPE这些命令,并使用CCID,将这些命令下达给读卡器,以及解析读卡器的执行结果。
五 细说USB驱动,还有读卡器的USB结构。
标题虽然叫细说,但其实还是很粗略。我不知道是不是所有的usb设备都是这样,但是至少SCL010是这样,SCM公司的另一款USB读卡器SDI010也是这样。他们都有三个口:中断口,bulk in , bulk out。
下面还是以SCL010为例,说一说这三个口。
Bulk out: 我们需要向他写入我们的命令。
Bulk in: 我们需要从这里面读回我们的命令的执行结果。
中断口: 标示有人刷卡,或者是有人把卡取走。如果有这两个事件发生,这个口中会有数据。所以PC短底层的做法就是不断的读这个中断口,没有数据就阻塞,有数据的话就说明有人刷卡,或者有人取走了卡。
至此发送命令,取得返回值,检测是否有人刷卡都可以完成了。目前只还剩下两个我们关心的事件:读卡器的插拔。这个我想不是读卡器的问题,而是系统应该感知的问题。交给系统判断吧。 从上面的内容抽出关键字进行搜索,发现很多事usb设备的概念,我目前只了解这些,需要的话就还需要进一步的调查。当然,如果大家不关心这些细节,那么可以默念操作系统的咒语,一切设备都是文件,然后当做浮云忽略他们啦。
六 再说APDU,以及PC/SC之上的API
首相让我们说一说,APDU从哪里来的问题。
原谅我目前只玩过SCL010,所以我还用他来说事儿. 当我们想利用SCL010从卡上面读写一些信息的时候,很显然我需要一条APDU命令,而这条命令我需要从scl010的manual上面来查找,因此我猜测,如果我使用的是别的读卡器,读同样的一张卡,我们所使用的apdu命令可能就会有所不同的(这点已经求证完毕,不同的读卡器可能会支持不同的apdu命令集合,相同的apdu命令也可能因为读卡器firm的实现不同,而存在细节的差异)。如果有多个读卡器对应的话,建立的apdu之上的应用是不稳定的。加之,apdu本省就是一些二进制数,不是很好理解,所以如果能在基于apdu的ps/sc API的上面,建立更高层次的api,供业务逻辑使用,通常会更好。我们的项目需要在打印机上面支持各种不同的读卡器,因此这一层抽象对我们的项目而言是必要的。
七 其他智能卡Framework
OpenCard Framework
http://www.openscdp.org/ocf/download.html
pyscard - python smart card library
http://pyscard.sourceforge.net/
目前这两个项目,我都已经下载试用。因为他们的抽象层次要高于PC/SC,所以我更喜欢他们。以后随着了解的深入,我也想再对他们进行总结。