如何简单有效地访问智能卡
使用一组类来访问智能卡读卡器,通过脚本测试智能卡 - 甚至可以逐个发送 APDU。
引言
本文提供了两个演示项目,向您展示如何在 Windows 中简单有效地访问智能卡。如果您不熟悉该领域,这是一个学习的好机会。
基本演示非常简单,它可以向您展示如何列出系统中的读卡器、连接/断开连接、获取卡的 ATR,以及与卡进行一次 APDU 传输。
注意:基本演示只接受没有非数字字符的单个 APDU,例如:“0084000008”(从卡中获取 8 字节的挑战)。
高级演示展示了如何在基本层之上有效地使用智能卡。
- 有一个 APDU 处理器:它可以处理用户输入。
- 带空格分隔的单个 APDU:“0084 0000 08”
- 多个 APDU:“0084 0000 08;00A4 0000 02 3F;”
- 甚至是 APDU 的脚本文件:“c:\myapdu.txt”
- ATR 分析,我们演示了协议:T=0 或 T=1。
- 多 APDU 控制台:这个控制台对于这类测试很有用。
- 如果出现问题,您可以提示用户“按任意键继续”。
- 您可以通过按住左键并拖动矩形来选择控制台文本,释放左键 - 文本就会复制到您的剪贴板中 - 就像 Windows 的 cmd.exe 一样,但这也适用于 Win9x ;-)
如果您想知道它们是如何运行的,您应该准备一个智能卡读卡器或安装一个虚拟智能卡读卡器,以及至少一张智能卡。
但是,如果没有,还有多 APDU 控制台供您在项目中特定场合使用脚本,我想。
背景
自 2004 年以来,我已经在许多项目中使用了智能卡,但真的找不到有用的类来访问 CodeProject 或互联网上的其他地方的智能卡。因此,我决定编写几个类来向您展示我们通常的做法。
对于 Windows 中的智能卡子系统,我们应该知道:智能卡读卡器应该连接到系统,供应商应该提供 PC/SC 驱动程序,并且有一个所谓的“智能卡资源管理器”供 Win32 程序通过一组 API 访问所有类型的读卡器。
要使用这些 API,我们应该调用
SCardEstablishContext()
:建立智能卡上下文;SCardListReaders()
:从资源管理器获取读卡器列表;SCardConnect()
:通过提供读卡器名称连接读卡器;SCardStatus()
:获取“已连接读卡器”中选定卡片的 ATR;SCardTransmit()
:在卡和我们的程序之间传输 APDU;SCardDisconnect()
:断开已连接的读卡器;SCardReleaseContext()
:释放上下文。
其中,4 项是可选的。
使用代码
正如我们所见,基本演示具有核心类 CSCardmgr
。而且,它的接口非常简单。
// Get the readers' list
BOOL SCardGetPcscList();
// open the reader using the index
BOOL SCardOpen(int nInx);
// Get ATR from the card.
BOOL SCardReset(LPCTSTR strResp);
// Transmit APDU
BOOL SCardTransmit(LPCTSTR strApdu, LPCTSTR strResp, UINT *nSW);
// Close the connected reader
BOOL SCardClose();
这里有两个非常有用的函数,用于字符串和十六进制之间的转换,来自我的好领导之一 Yu 先生。
///////////////////////////////////////////////////////////////////////////////
// hex to asc: 0x22 -> "22"
int Hex2Asc(char *Dest,char *Src,int SrcLen)
{
int i;
for ( i = 0; i < SrcLen; i ++ )
{
sprintf(Dest + i * 2,"%02X",(unsigned char)Src[i]);
}
Dest[i * 2] = 0;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// asc to hex: "22" -> 0x22
int Asc2Hex(char *Dest,char *Src,int SrcLen)
{
int i;
for ( i = 0; i < SrcLen / 2; i ++ )
{
sscanf(Src + i * 2,"%02X",(unsigned char *)&Dest[i]);
}
return TRUE;
}
对于高级演示,我们改进了 ATR 处理和 APDU 处理 - 这些在智能卡应用中很有用。首先,ATR 处理:我们仅提取智能卡支持的协议,并在 ATR 字符串的末尾显示其协议。
///////////////////////////////////////////////////////////////////////////////
// Get the ATR information of the open Reader
void CSCardDemoDlg::OnReset()
{
CHAR szATR[ATR_MAX_SIZE * 2 +1] = {0};
BYTE byATR[ATR_MAX_SIZE] = {0};
CHAR szFullAtr[MAX_RESPONSE] = {0};
UINT np = 0; // Prot.
BYTE protocol = ATR_PROTOCOL_TYPE_T0;
ZeroMemory(szATR, sizeof(szATR));
if(m_SCard.SCardReset(szATR))
// Do Reset it
{
int nLenAtr = strlen(szATR);
// Convert ATR to Hex format
Asc2Hex((char*)byATR, szATR, nLenAtr);
CAtr objATR(byATR, nLenAtr/2);
// get prots supported.
objATR.ATR_GetNumberOfProtocols(&np);
INT nType = objATR.ATR_GetProtocolType(np, &protocol);
sprintf(szFullAtr, "%s (T=%d).", szATR, protocol);
// Display the ATR Information
SetDlgItemText(IDS_MSG, szFullAtr);
}
}
其次,我们提供了 CApduProcesser
和 CConsoleWindow
来改善 APDU 传输体验:用户输入更加灵活,如前所述,我们为此使用 CApduProcesser
。我们使用此代码块来去除非数字字符。
// Eat the non-numeric chars.
for(int i=0, j=0; i < strlen(szSLBuf); i++)
{
if(szSLBuf[i] >='0' && szSLBuf[i] <= '9')
{
szString[j] = szSLBuf[i];
j ++;
}
else if(szSLBuf[i] >='A' && szSLBuf[i] <= 'F')
{
szString[j] = szSLBuf[i];
j ++;
}
else if(szSLBuf[i] >='a' && szSLBuf[i] <= 'f')
{
szString[j] = szSLBuf[i];
j ++;
}
else
continue;
}
CConsoleWindow
用于控制台输出。它的亮点是“按任意键继续”和鼠标选择。
void CConsoleWindow::WaitForAnyKeyEx()
{
... ...
for(;;)
{
/* get an input event */
bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
PERR(bSuccess, "ReadConsoleInput");
switch (inputBuffer.EventType)
{
case KEY_EVENT:
... ...
break;
case MOUSE_EVENT:
... ...
break;
}
}
... ...
}
“KEY_EVENT
”用于“按任意键继续”,而“MOUSE_EVENT
”则用于鼠标选择。
关注点
什么是智能卡?我不知道,但我确定我的 iPhone 有一张 SIM 卡,它就是一张智能卡。^_^
如果我有一个智能卡读卡器,我就可以使用这个演示来读取我 SIM 卡上的联系人列表。
此外,智能卡不仅用于读取 SIM 卡,PKI 也需要它:CSP 和 PKCS#11 都需要它 - (但不强制 ^_^)。据我所知,许多供应商已经将读卡器和卡结合起来,这就是 USB 令牌;令牌中嵌入了指纹扫描仪或 OTP。所以,这个领域非常精彩。
顺便说一句,智能卡是接触式卡(受 ISO7816 限制)。还有所谓的非接触式卡(受 ISO14443 限制),例如 Mifare S50/S70/DES/UL 等。一些供应商还提供所谓的双模块卡 - 这些卡有两种接口,并同时支持 ISO7816 和 ISO14443。
历史
- 2008-01-19:初始版本。
- 2008-04-12:添加了虚拟 PC/SC 驱动程序(virtual-pcsc_drv.zip),用于使用/测试这些演示。
有关驱动程序的许可,请参阅 readme.txt。:: 建议在虚拟机中使用此驱动程序。