数週間前、大学のプロジェクトでスマート カードの実験を始めました。この演習は、健康保険証 (ドイツの「eGK」) を使用して保存されているものを見つけ、事前認証なしで読み取ることができる「公開」部分を読み取るのと同じくらい簡単でした。
幸いなことに、カードの構造は十分に文書化されており、文書はインターネットから簡単にダウンロードできます。基本的にやりたいことは、EF(MF / DF.HCA / EF.VD)を選択してデータを読み出すことです。読みたいファイルはバイナリファイルです。ドキュメントによると、最初の 2x4 バイトは、ファイルの 2 つの部分の開始/終了オフセットを表します。各パーツは gzip 圧縮された XML である必要があります。
最初に、pcsc-lite ツールの「スクリプト」を使用していくつかのコマンドを試し、それが機能するかどうか、妥当な結果が得られるかどうかを確認しました。これはすべてかなりうまくいきました:)
$ 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 を使用してカードに最初のコマンドをすぐに送信できました。プログラムは次のとおりです。
- コンテキストを確立する
- リーダーを取得します (プログラムは使用中のリーダーを出力し、それが唯一の適切なものです ;))
- カードを取得する (プロトコルは T=1)
- カードをリセットする
- PC/SC にトランザクションが必要であることを伝える
- コマンドを送信する
- 取引終了
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
最初の 2 つのコマンドは正常に送信され、常に 0x90 0x00 が返されます。残念ながら、ReadOffsets-Transmit は「トランザクションに失敗しました」というエラーを返します。ログには次のように書かれています。
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 ドライバーのソース コードに、デバッグ用の #ifdef がいくつか見つかりました。これで、より詳細なログ ファイルが作成されました。[[[]]]の部分はエラーログファイルのみです。ご覧のとおり、送信されたバイト数と受信されたバイト数にまったく違いはありません。
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
では、このエラー メッセージはどこから来たのでしょうか。
私が学んだもう 1 つのことは、このエラーは読者固有のものではないようだということです。このプログラムを友人の内蔵カード リーダー (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 コード内のどこかにあることを確認できます。プログラムが機能しない理由を誰かが知っている場合は、とにかく気軽に答えてください。私が犯した間違いを知りたいと思っています :) !