PC / SC:使用C-API调用时“卡未处理”

时间:2014-10-24 13:08:22

标签: c smartcard apdu pcsc

几周前,我开始尝试为大学项目使用智能卡。这项练习就像使用健康保险卡(德语版“eGK”)一样简单,并找出保存在其上的内容并读出“公共”部分,这些部分可以在没有事先验证的情况下以任何方式读出。

幸运的是,卡的结构已有详细记录,文档可以从互联网上轻松下载。我现在基本上想要的是选择EF(MF / DF.HCA / EF.VD)并读出数据。我想要读出的文件是二进制文件。根据文档,前2x4字节表示文件两部分的开始/结束偏移。每个部分都应该是gzip压缩的XML。

首先,我使用pcsc-lite工具中的“scriptor”来尝试一些命令,看它是否有效以及是否得到了一些合理的结果。这一切都很顺利:))

$ scriptor
No reader given: using Towitoko Chipdrive USB 00 00
Using T=1 protocol
Reading commands from STDIN
reset
> RESET
< OK: 3B DD 97 FF 81 B1 FE 45 1F 03 00 64 04 05 08 03 73 96 21 D0 00 90 00 C8 
00 a4 04 0c 06 d2 76 00 00 01 02    # Select DF
> 00 a4 04 0c 06 d2 76 00 00 01 02
< 90 00 : Normal processing.
00 a4 02 0c 02 d0 02                # Select EF
> 00 a4 02 0c 02 d0 02
< 90 00 : Normal processing.
00 b0 00 00 08                      # Read out start/end offsets
> 00 b0 00 00 08
< 00 08 01 AD 01 AE 02 7C 90 00 : Normal processing.

正如你所看到的,这将是超过400字节读出(并在之后解压缩),所以我决定我需要编写一些程序来为我读出这个。借助这篇博文:http://ludovicrousseau.blogspot.de/2010/04/pcsc-sample-in-c.html我很快就可以使用C将我的第一个命令发送到卡片。我的程序的基本流程是:

  1. 建立背景
  2. 获取一位读者(该程序打印出正在使用的读者,它是唯一且正确的读者;)
  3. 获取卡(协议为T = 1)
  4. 重置卡
  5. 告诉PC / SC我需要交易
  6. 传输命令
  7. 结束交易
  8. 以下是我发送给SC的字段:

    //                    CLA   INS    P1     P2            LEN_SEND
    BYTE cmdSelectDF[] = {0x00, 0xA4,  0x04,  0x0C,         0x06,
    //                    00    SELECT DF/AID first/noanswer
    //                    <- HEADER] ----- [DATA ->
    //                    D6    D5    D4     D3    D2    D1
                          0xD2, 0x76, 0x00,  0x00, 0x01, 0x02};
    
    //                    CLA   INS    P1     P2            LEN_SEND
    BYTE cmdSelectEF[] = {0x00, 0xA4,  0x02,  0x0C,         0x02,
    //                    00    SELECT EF/FID first/noanswer
    //                    <- HEADER] ----- [DATA ->
    //                    D2    D1
                          0xD0, 0x02};
    
    //                       CLA   INS         P1      P2      LEN_RECV
    BYTE cmdReadOffsets[] = {0x00, 0xB0,       0x00,   0x00,   0x08};
    //                       00    READ BINARY OFFSET1 OFFSET2 BYTES
    

    前两个命令传输得很好,我总是得到一个0x90 0x00。不幸的是,ReadOffsets-Transmit返回错误:“Transaction failed”。日志说明如下:

    00000011 winscard.c:1613:SCardTransmit() Send Protocol: T=1
    00042572 winscard.c:1658:SCardTransmit() UnrefReader() count was: 2
    00000030 winscard_svc.c:656:ContextThread() TRANSMIT rv=0x0 for client 6
    03000349 winscard_svc.c:356:ContextThread() Received command: TRANSMIT from client 6
    00000043 readerfactory.c:798:RFReaderInfoById() RefReader() count was: 1
    00000012 winscard.c:1613:SCardTransmit() Send Protocol: T=1
    00055517 ifdwrapper.c:553:IFDTransmit() Card not transacted: 612
    00000027 winscard.c:1638:SCardTransmit() Card not transacted: 0x80100016
    00000010 winscard.c:1658:SCardTransmit() UnrefReader() count was: 2
    00000031 winscard_svc.c:656:ContextThread() TRANSMIT rv=0x80100016 for client 6
    00000297 winscard_svc.c:348:ContextThread() Client die: 6
    00000029 winscard.c:230:SCardReleaseContext() Releasing Context: 0x420027B7
    

    有没有人知道这意味着什么以及它为什么会发生?我做错了什么?

    非常感谢你!

    编辑:我有一些新闻给你。我在towitoko驱动程序源代码中找到了一些#ifdefs用于调试目的。现在,我有一个更详细的日志文件供您使用。带[[[]]]的部分仅在错误日志文件中。如您所见,提交的字节数和收到的字节数没有任何区别!

    01942065 winscard_svc.c:356:ContextThread() Received command: TRANSMIT from client 6
    00000021 readerfactory.c:798:RFReaderInfoById() RefReader() count was: 1
    00000008 winscard.c:1613:SCardTransmit() Send Protocol: T=1
    IFD: Setting baudrate to 9600
    IFD: Transmit: 0 40 7 0 A4 2 C 2 D0 2 3D 
    IO: Sending: 6F B 5 5A 
    IO: Sending: 0 
    IO: Sending: 40 7 0 A4 2 C 2 D0 2 3D 
    IO: Receiving: 0 
    IO: Receiving: 40 2 90 
    IFD: Receive: 0 40 2 90 
    IO: Receiving: 0 D2 
    IFD: Receive: 0 D2 
    00042861 winscard.c:1658:SCardTransmit() UnrefReader() count was: 2
    00000011 winscard_svc.c:656:ContextThread() TRANSMIT rv=0x0 for client 6
    IO: Sending: 3 7 
    IO: Receiving: 42 87 
    IFD: Status = card / no change
    IO: Sending: 3 7 
    IO: Receiving: 40 83 
    IFD: Status = card / no change
    [...]
    02773187 winscard_svc.c:356:ContextThread() Received command: TRANSMIT from client 6
    00000032 readerfactory.c:798:RFReaderInfoById() RefReader() count was: 1
    00000009 winscard.c:1613:SCardTransmit() Send Protocol: T=1
    IFD: Setting baudrate to 9600
    IFD: Transmit: 0 0 5 0 B0 0 0 8 BD 
    IO: Sending: 6F 9 5 52 
    IO: Sending: 0 
    IO: Sending: 0 5 0 B0 0 0 8 BD 
    IO: Receiving: 0 
    IO: Receiving: 0 A 0 
    IFD: Receive: 0 0 A 0 
    IO: Receiving: 8 1 AD 1 AE 2 7C 90 0 EF 
    IFD: Receive: 8 1 AD 1 AE 2 7C 90 0 EF 
    [[[ 00054674 ifdwrapper.c:553:IFDTransmit() Card not transacted: 612
    00000011 winscard.c:1638:SCardTransmit() Card not transacted: 0x80100016 ]]]
    00054759 winscard.c:1658:SCardTransmit() UnrefReader() count was: 2
    00000011 winscard_svc.c:656:ContextThread() TRANSMIT rv=0x0 for client 6
    

    那么,这个错误信息来自哪里?

    我学到的另一件事是这个错误似乎不是读者特定的。我已经在朋友的内部读卡器上试过这个程序(Windows称它是Broadcom的读卡器),这会得到相同的结果。因为我能想到的唯一情况是我的C程序中的错误,这里是基本宏和连接部分的源代码:

    // Parts taken from http://ludovicrousseau.blogspot.de/2010/04/pcsc-sample-in-c.html
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <PCSC/pcsclite.h>
    #include <PCSC/winscard.h>
    #include <PCSC/wintypes.h>
    
    #define EGK_RECV_BUFSIZE 258
    
    #define PCSC_ERROR(debugmsg, retval) \
        if(retval != SCARD_S_SUCCESS) { \
            fprintf(stderr, "PC/SC Error at %s: %s\n", debugmsg, pcsc_stringify_error(retval)); \
            return 1; \
        }
    
    #define TRANS_RESPONSE \
        printf("Command response: "); \
        for(i = 0; i < dwRecvLen; i++) { \
            printf("%02X ", recvBuffer[i]); \
        } printf("\n");
    
    #define CHECK_SUCCESS \
        if(dwRecvLen >= 2) { \
            if(recvBuffer[dwRecvLen-2] == 0x90 && recvBuffer[dwRecvLen-1] == 0x00) { \
                printf("Command success!\n"); \
            } \
        }
    
    #define SCARD_TRANSMIT(cmd) \
        SCardTransmit(scHandle, &scSendProto, cmd, sizeof(cmd), NULL, recvBuffer, &dwRecvLen);
    
    int main(void)
    {
        LONG retval = 0;                            // = long
        SCARDCONTEXT scContext;                     // = LONG
        LPTSTR scReaders;                           // = LPSTR = char *
        SCARDHANDLE scHandle;                       // = LONG (specific smartcard)
        DWORD dwReaders, dwCurProto, dwRecvLen;     // = unsigned long
    
        SCARD_IO_REQUEST scSendProto;
        BYTE recvBuffer[EGK_RECV_BUFSIZE];
    
        // BYTE cmdXXX[] = {...}; ... SEE ABOVE!
    
        // Get Context
        retval = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scContext);
        PCSC_ERROR("1 Establish Context", retval);
    
        // Get Reader
    
        retval = SCardListReaders(scContext, NULL, NULL, &dwReaders);
        PCSC_ERROR("2.1 Get Readers", retval);
    
        scReaders = calloc(dwReaders, sizeof(DWORD));
    
        retval = SCardListReaders(scContext, NULL, scReaders, &dwReaders);
        PCSC_ERROR("2.2 Get Readers", retval);
    
        printf("Reader name: %s\n", scReaders);
    
        // Get Card
    
        retval = SCardConnect(scContext, scReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scHandle, &dwCurProto);
        PCSC_ERROR("3 SCardConnect", retval);
    
        // Reset
        retval = SCardReconnect(scHandle, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &dwCurProto);
        PCSC_ERROR("4 RESET Card", retval);
    
        dwRecvLen = EGK_RECV_BUFSIZE;
    
        if(dwCurProto == SCARD_PROTOCOL_T0) {
            scSendProto = *SCARD_PCI_T0;
        } else if(dwCurProto == SCARD_PROTOCOL_T1) {
            scSendProto = *SCARD_PCI_T1;
        } else {
            fprintf(stderr, "No known protocol selected\n");
            return 1;
        }
    

    每个命令的Transmit-Part看起来都一样。例如:

    printf("READ BINARY -> First 8 Bytes (Offsets)\n");
    retval = SCARD_TRANSMIT(cmdReadOffsets);
    PCSC_ERROR("8 Transmit READ BINARY", retval);
    
    TRANS_RESPONSE;
    CHECK_SUCCESS; printf("\n");
    

    编辑:抱歉这个编辑来得太晚了,我完全忘了这篇文章!我设法通过用Java重写我的程序来使它工作。我知道对于那些可能偶然发现这个问题的人来说,这并不是一个令人满意的答案。至少我们现在可以确定错误位于C代码内部。如果有人知道为什么程序不起作用,请随时回答,我真的很想知道我犯了哪个错误:)!

0 个答案:

没有答案