将缓冲区直接发送到智能卡

send buffer directly to a smartcard

我需要向智能卡发送消息。 首先,我使用 gpshell 发送它以测试正确性并得到答案:

send_apdu_nostop -sc 0 -APDU 802A80B05F87410061DA7A1E2F02602A255063713FD657797063C6C7ACC12072F5340B1C0126A616BC66C65F49132EED10AE071DC661AA1333BEA92F67A5BEFFDFA7A0F31FC8B3D981105D1EF96B000FD90098C7FF031906A1018E0897C5DA580059AD2900                             Command --> 802A80B05F87410061DA7A1E2F02602A255063713FD657797063C6C7ACC12072F5340B1C0126A616BC66C65F49132EED10AE071DC661AA1333BEA92F67A5BEFFDFA7A0F31FC8B3D981105D1EF96B000FD90098C7FF031906A1018E0897C5DA580059AD2900
Wrapped command --> 802A80B05F87410061DA7A1E2F02602A255063713FD657797063C6C7ACC12072F5340B1C0126A616BC66C65F49132EED10AE071DC661AA1333BEA92F67A5BEFFDFA7A0F31FC8B3D981105D1EF96B000FD90098C7FF031906A1018E0897C5DA580059AD2900
Response <-- 604001544F50434F4E31392020202020000000FF00FF0000FF00020000000000000000FF5D11DCAD000000005D11DCAD005D1EF96B9000

有了这个结果,我有信心使用 smartcardio 复制 java 中的行为。 以下是我写的代码:

    void testCard()
    {
        TerminalFactory factory = TerminalFactory.getDefault();
        List<CardTerminal> terminals;
        try {
            terminals = factory.terminals().list();
        } catch (CardException ex) {
            return;
        }
        CardTerminal cardTerm = terminals.get(0);
        Card card;
        try {
            card = cardTerm.connect("T=0");
        } catch (CardException ex) {           
            return;
        }
        CardChannel cach = card.getBasicChannel();
        ResponseAPDU r;
        try {
            CommandAPDU ca = new CommandAPDU(new byte[]{(byte)0x80,(byte)0x2A,(byte)0x80,(byte)0xB0,(byte)0x5F,(byte)0x87,(byte)0x41,(byte)0x00,(byte)0x61,(byte)0xDA,(byte)0x7A,(byte)0x1E,(byte)0x2F,(byte)0x02,(byte)0x60,(byte)0x2A,(byte)0x25,(byte)0x50,(byte)0x63,(byte)0x71,(byte)0x3F,(byte)0xD6,(byte)0x57,(byte)0x79,(byte)0x70,(byte)0x63,(byte)0xC6,(byte)0xC7,(byte)0xAC,(byte)0xC1,(byte)0x20,(byte)0x72,(byte)0xF5,(byte)0x34,(byte)0x0B,(byte)0x1C,(byte)0x01,(byte)0x26,(byte)0xA6,(byte)0x16,(byte)0xBC,(byte)0x66,(byte)0xC6,(byte)0x5F,(byte)0x49,(byte)0x13,(byte)0x2E,(byte)0xED,(byte)0x10,(byte)0xAE,(byte)0x07,(byte)0x1D,(byte)0xC6,(byte)0x61,(byte)0xAA,(byte)0x13,(byte)0x33,(byte)0xBE,(byte)0xA9,(byte)0x2F,(byte)0x67,(byte)0xA5,(byte)0xBE,(byte)0xFF,(byte)0xDF,(byte)0xA7,(byte)0xA0,(byte)0xF3,(byte)0x1F,(byte)0xC8,(byte)0xB3,(byte)0xD9,(byte)0x81,(byte)0x10,(byte)0x5D,(byte)0x1E,(byte)0xF9,(byte)0x6B,(byte)0x00,(byte)0x0F,(byte)0xD9,(byte)0x00,(byte)0x98,(byte)0xC7,(byte)0xFF,(byte)0x03,(byte)0x19,(byte)0x06,(byte)0xA1,(byte)0x01,(byte)0x8E,(byte)0x08,(byte)0x97,(byte)0xC5,(byte)0xDA,(byte)0x58,(byte)0x00,(byte)0x59,(byte)0xAD,(byte)0x29,(byte)0x00});   
            r = cach.transmit(ca);           
        } catch (CardException ex) {            
            return;
        }
    }        

当代码运行时,我总是收到错误 0x6E00,这意味着:"Class not supported"。

正在阅读 transmit 的 java 文档,我了解到 "The CLA byte of the command APDU is automatically adjusted to match the channel number of this CardChannel."

我怀疑由于某些原因 class 字节以某种方式被更改,因此卡回答错误。

有什么方法可以直接把信息发到java的卡上吗?

注意:我没有使用过智能卡,也没有智能卡来验证这个答案。但它可能会为您指明正确的方向

如果你看一下gshell的源代码。发送 APDU 如下所示

 else if (_tcscmp(token, _T("-APDU")) == 0)
        {
            token = strtokCheckComment(NULL);
            if (token == NULL)
            {
                _tprintf(_T("Error: option -APDU not followed by data\n"));
                rv = EXIT_FAILURE;
                goto end;
            }
            else
            {
                pOptionStr->APDULen = ConvertStringToByteArray(token, APDULEN, pOptionStr->APDU);
            }
        }

https://github.com/sigma/globalplatform/blob/7f1c8669c5991a60b6fada9b1187d2ee6d040223/gpshell/src/gpshell.c#L1648

所以你传递的是完整的APDU命令

if (platform_mode == PLATFORM_MODE_OP_201)
{
    status = OP201_send_APDU(cardContext, cardInfo,
                            (optionStr.secureChannel == 0 ? NULL : &securityInfo201),
                            (PBYTE)(optionStr.APDU), optionStr.APDULen,
                            recvAPDU, &recvAPDULen);
}

这又转到

https://github.com/sigma/globalplatform/blob/7f1c8669c5991a60b6fada9b1187d2ee6d040223/globalplatform/src/connection.c#L202

OPGP_ERROR_STATUS OPGP_send_APDU(OPGP_CARD_CONTEXT cardContext, OPGP_CARD_INFO cardInfo, GP211_SECURITY_INFO *secInfo, PBYTE capdu, DWORD capduLength, PBYTE rapdu, PDWORD rapduLength) {
    OPGP_ERROR_STATUS errorStatus;
    OPGP_ERROR_STATUS securityStatus;
    OPGP_ERROR_STATUS(*plugin_sendAPDUFunction) (OPGP_CARD_CONTEXT, OPGP_CARD_INFO, PBYTE, DWORD, PBYTE, PDWORD);
    BYTE apduCommand[261];
    DWORD apduCommandLength = 261;
    int i=0;

    OPGP_LOG_START(_T("OPGP_send_APDU"));
    plugin_sendAPDUFunction = (OPGP_ERROR_STATUS(*)(OPGP_CARD_CONTEXT, OPGP_CARD_INFO, PBYTE, DWORD, PBYTE, PDWORD)) cardContext.connectionFunctions.sendAPDU;

所以我认为需要在您的命令中附加一个额外的 cardContext,目前还没有

也看看几个实现,看看你是否能找出字节

https://github.com/handywings/tutorial_hdw_mc/blob/a1dceae1b243b0c79dda5c73218e3a9ff84c259c/src/main/java/com/hdw/mccable/utils/ThaiLandIdCard.java

https://www.javaworld.com/article/2076450/how-to-write-a-java-card-applet--a-developer-s-guide.html

https://github.com/arg3s/ClassAttendanceMaster/blob/a368b07e9b94bd387f91d7d6f57f12e402d58b32/src/main/java/com/classattendancemaster/SmartCard/SmartcardConnector.java

我找到了解决方案。似乎使用 "T=0" 不是发送第二个命令的合适方式。使用 "T=1" 或 "*" 解决问题。 更正如下:

card = cardTerm.connect("T=1");