08 December 2022

(Perl, Python) MIFARE ICカードのUID/FeliCa ICカードのIDm,PMmを読み取る

検証環境 : Ubuntu 22.04, ICカードリーダー ACR1251DI / SCM3310

データ読み出しAPI

20221208-api.jpg

ACR128U Dual- Interface Reader , API reference

Perlを使う場合

Perlモジュール リファレンス

Chipcard::PCSC - Smart card reader interface library

Chipcard::PCSC::Card - Smart card communication library

ソースコード

#!/usr/bin/perl
# Smart Card(MIFARE)よりUIDを / Smart Card(FeliCa)よりIDm PMmを読み出す
# サンプルスクリプト
#
# libpcsc_perl パッケージが必要(Ubuntu 22.04で動作確認済み)
#
# 次の4種類の読み込み方法サンプル
# (01) UID を Transmit で読取り、(02) UID を TransmitWithCheck で読取り
# (03) ATS を Transmit で読取り、(04) ATS を TransmitWithCheck で読取り
#
# 読み出しデータ(APDUフォーマット)の例
###  FeliCaの IDm 8 byte Recv UID = 01 02 03 04 05 06 07 08 90 00
###  FeliCaの PMm 8 byte Recv ATS = 09 0A 0B 0C 0D 0E 0F 10 90 00
###  MIFARE   UID 4 byte Recv UID = 01 02 03 04 90 00
###  MIFARE   UID 7 byte Recv UID = 01 02 03 04 05 06 07 90 00

use warnings;
use strict;
use Chipcard::PCSC;

print "Read UID/ATS from smart card ( use Chipcard::PCSC::Card )\n";

my $hContext = new Chipcard::PCSC();
die("Can't create the PCSC object\n") unless defined $hContext;

# カードリーダーの一覧を得る
my @ReadersList = $hContext->ListReaders();
die("Can't get readers' list: $Chipcard::PCSC::errno\n")
  unless ( defined( $ReadersList[0] ) );
print $#ReadersList + 1 . " reader detected.";
if   ( $#ReadersList > 0 ) { print "this script use first reader .\n"; }
else                       { print "\n"; }
print "target reader = " . $ReadersList[0] . "\n";

# 最初のカードリーダーを開く
my $hCard = new Chipcard::PCSC::Card( $hContext, $ReadersList[0] );
die("Can't connect: $Chipcard::PCSC::errno\n") unless defined $hCard;

# PICC command : Get Data
# 送信データ (APDU format) = Class : 0xff, INS : 0xca, P1 : 0x00(UID)/0x01(ATS), P2 : 0x00, Length : 0x00
# 受信データ (APDU format) = DATA bytes + 0x90 0x00 (読取成功), DATA bytes + 0x63 0x00/0x6a 0x81 (読取エラー)

### (01)
# 読取り成功/エラーのリザルトコードも含めて、UIDのバイト配列を取得する方法
my $APDU_Array = $hCard->Transmit( [ 0xff, 0xca, 0x00, 0x00, 0x00 ] );
print "  Recv UID = ";

foreach my $tmpVal ( @{$APDU_Array} ) {
    printf( "%02X ", $tmpVal );
}
print " : " . ( $#{$APDU_Array} + 1 ) . " byte ";
if ( $#{$APDU_Array} < 2 ) { print " : NO DATA "; }
print "\n";

### (02)
# 読取り成功/エラーのリザルトコードをチェックした後、UIDのバイト列をテキスト変換した文字列を得る方法
# (リザルトコードは出力されない)
( my $sw, my $APDU_Str ) = $hCard->TransmitWithCheck( "FF CA 00 00 00", "90 00" );

# リザルトコードが [0x90, 0x00] の場合に、$APDU_Str にUIDデータが格納される
if ( defined $APDU_Str ) {
    print "  Recv UID = " . $APDU_Str . "\n";    # テキスト形式のUIDをそのまま表示

    $APDU_Array = ();
    $APDU_Array = Chipcard::PCSC::ascii_to_array($APDU_Str);    # UIDのバイト列を配列に一旦格納
    print "  Recv UID = ";

    foreach my $tmpVal ( @{$APDU_Array} ) {
        printf( "%02X ", $tmpVal );
    }
    print "\n";
}
else {
    print "  Recv UID : NO DATA\n";
}

### (03)
# 読取り成功/エラーのリザルトコードも含めて、UID(ATS)のバイト配列を取得する方法
$APDU_Array = $hCard->Transmit( [ 0xff, 0xca, 0x01, 0x00, 0x00 ] );
print "  Recv ATS = ";

foreach my $tmpVal ( @{$APDU_Array} ) {
    printf( "%02X ", $tmpVal );
}
print " : " . ( $#{$APDU_Array} + 1 ) . " byte ";
if ( $#{$APDU_Array} < 2 ) { print " : NO DATA "; }
print "\n";

### (04)
# 読取り成功/エラーのリザルトコードをチェックした後、UID(ATS)のバイト列をテキスト変換した文字列を得る方法
# (リザルトコードは出力されない)
( $sw, $APDU_Str ) = $hCard->TransmitWithCheck( "FF CA 01 00 00", "90 00" );

# リザルトコードが [0x90, 0x00] の場合に、$APDU_Str にATSデータが格納される
if ( defined $APDU_Str ) {
    print "  Recv ATS = " . $APDU_Str . "\n";    # テキスト形式のUIDをそのまま表示

    $APDU_Array = ();
    $APDU_Array = Chipcard::PCSC::ascii_to_array($APDU_Str);    # UIDのバイト列を配列に一旦格納
    print "  Recv ATS = ";

    foreach my $tmpVal ( @{$APDU_Array} ) {
        printf( "%02X ", $tmpVal );
    }
    print "\n";
}
else {
    print "  Recv ATS : NO DATA\n";
}

$hCard->Disconnect();
$hContext = undef;

Pythonを使う場合

#!/usr/bin/python

# sudo apt install python3-pyscard

from smartcard.util import toHexString
from smartcard.System import readers


def main():
    print("Read UID/ATS from smart card ( use smartcard.System )")

    ReaderList = readers()
    if len(ReaderList) < 1:
        print("Can't get readers' list\n")
        return
    print(str(len(ReaderList)) + " reader detected . ", end="")
    if len(ReaderList) > 1:
        print("this script use first reader .")
    else:
        print("\n")
    print("target reader : " + str(ReaderList[0]))

    try:
        conn = ReaderList[0].createConnection()
        conn.connect()
    except Exception as message:
        print("Exception : " + str(message))
        return

    try:
        SendApduList = [0xFF, 0xCA, 0x00, 0x00, 0x00]
        RecvApduList, sw1, sw2 = conn.transmit(SendApduList)
        print("Recv UID = " + toHexString(RecvApduList) + " " + hex(sw1)[2:] + " " + hex(sw2)[2:])

        SendApduList = [0xFF, 0xCA, 0x01, 0x00, 0x00]
        RecvApduList, sw1, sw2 = conn.transmit(SendApduList)
        print("Recv ATS = " + toHexString(RecvApduList) + " " + hex(sw1)[2:] + " " + hex(sw2)[2:])
    except Exception as message:
        print("Exception : " + str(message))
    finally:
        conn.disconnect()

main()