Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Thai National ID Card reader in python
#!/usr/bin/env python
# bouroo<bouroo@gmail.com>
# 07.04.2019
# sudo apt-get -y install pcscd python-pyscard python-pil
import binascii
import io
import os
import sys
from PIL import Image
from smartcard.System import readers
from smartcard.util import HexListToBinString, toHexString, toBytes
# Thailand ID Smartcard
# tis-620 to utf-8
def thai2unicode(data):
result = ''
if isinstance(data, list):
for d in data:
if (sys.version_info.major < 3):
result += unicode(chr(d),"tis-620")
else :
result += chr(d).decode("tis-620").encode("utf-8")
else :
result = data.decode("tis-620").encode("utf-8")
return result.strip();
def getData(cmd, req = [0x00, 0xc0, 0x00, 0x00]):
data, sw1, sw2 = connection.transmit(cmd)
data, sw1, sw2 = connection.transmit(req + [cmd[-1]])
return [data, sw1, sw2];
# define the APDUs used in this script
# https://github.com/chakphanu/ThaiNationalIDCard/blob/master/APDU.md
# Check card
SELECT = [0x00, 0xA4, 0x04, 0x00, 0x08]
THAI_CARD = [0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01]
# CID
CMD_CID = [0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d]
# TH Fullname
CMD_THFULLNAME = [0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64]
# EN Fullname
CMD_ENFULLNAME = [0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64]
# Date of birth
CMD_BIRTH = [0x80, 0xb0, 0x00, 0xD9, 0x02, 0x00, 0x08]
# Gender
CMD_GENDER = [0x80, 0xb0, 0x00, 0xE1, 0x02, 0x00, 0x01]
# Card Issuer
CMD_ISSUER = [0x80, 0xb0, 0x00, 0xF6, 0x02, 0x00, 0x64]
# Issue Date
CMD_ISSUE = [0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08]
# Expire Date
CMD_EXPIRE = [0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08]
# Address
CMD_ADDRESS = [0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64]
# Photo_Part1/20
CMD_PHOTO1 = [0x80, 0xb0, 0x01, 0x7B, 0x02, 0x00, 0xFF]
# Photo_Part2/20
CMD_PHOTO2 = [0x80, 0xb0, 0x02, 0x7A, 0x02, 0x00, 0xFF]
# Photo_Part3/20
CMD_PHOTO3 = [0x80, 0xb0, 0x03, 0x79, 0x02, 0x00, 0xFF]
# Photo_Part4/20
CMD_PHOTO4 = [0x80, 0xb0, 0x04, 0x78, 0x02, 0x00, 0xFF]
# Photo_Part5/20
CMD_PHOTO5 = [0x80, 0xb0, 0x05, 0x77, 0x02, 0x00, 0xFF]
# Photo_Part6/20
CMD_PHOTO6 = [0x80, 0xb0, 0x06, 0x76, 0x02, 0x00, 0xFF]
# Photo_Part7/20
CMD_PHOTO7 = [0x80, 0xb0, 0x07, 0x75, 0x02, 0x00, 0xFF]
# Photo_Part8/20
CMD_PHOTO8 = [0x80, 0xb0, 0x08, 0x74, 0x02, 0x00, 0xFF]
# Photo_Part9/20
CMD_PHOTO9 = [0x80, 0xb0, 0x09, 0x73, 0x02, 0x00, 0xFF]
# Photo_Part10/20
CMD_PHOTO10 = [0x80, 0xb0, 0x0A, 0x72, 0x02, 0x00, 0xFF]
# Photo_Part11/20
CMD_PHOTO11 = [0x80, 0xb0, 0x0B, 0x71, 0x02, 0x00, 0xFF]
# Photo_Part12/20
CMD_PHOTO12 = [0x80, 0xb0, 0x0C, 0x70, 0x02, 0x00, 0xFF]
# Photo_Part13/20
CMD_PHOTO13 = [0x80, 0xb0, 0x0D, 0x6F, 0x02, 0x00, 0xFF]
# Photo_Part14/20
CMD_PHOTO14 = [0x80, 0xb0, 0x0E, 0x6E, 0x02, 0x00, 0xFF]
# Photo_Part15/20
CMD_PHOTO15 = [0x80, 0xb0, 0x0F, 0x6D, 0x02, 0x00, 0xFF]
# Photo_Part16/20
CMD_PHOTO16 = [0x80, 0xb0, 0x10, 0x6C, 0x02, 0x00, 0xFF]
# Photo_Part17/20
CMD_PHOTO17 = [0x80, 0xb0, 0x11, 0x6B, 0x02, 0x00, 0xFF]
# Photo_Part18/20
CMD_PHOTO18 = [0x80, 0xb0, 0x12, 0x6A, 0x02, 0x00, 0xFF]
# Photo_Part19/20
CMD_PHOTO19 = [0x80, 0xb0, 0x13, 0x69, 0x02, 0x00, 0xFF]
# Photo_Part20/20
CMD_PHOTO20 = [0x80, 0xb0, 0x14, 0x68, 0x02, 0x00, 0xFF]
# Get all the available readers
readerList = readers()
print "Available readers:"
for readerIndex,readerItem in enumerate(readerList):
print(readerIndex, readerItem)
# Select reader
readerSelectIndex = int(input("Select reader[0]: ") or "0")
reader = readerList[readerSelectIndex]
print "Using:", reader
connection = reader.createConnection()
connection.connect()
atr = connection.getATR()
print "ATR: " + toHexString(atr)
if (atr[0] == 0x3B & atr[1] == 0x67):
req = [0x00, 0xc0, 0x00, 0x01]
else :
req = [0x00, 0xc0, 0x00, 0x00]
# Check card
data, sw1, sw2 = connection.transmit(SELECT + THAI_CARD)
print "Select Applet: %02X %02X" % (sw1, sw2)
# CID
data = getData(CMD_CID, req)
cid = thai2unicode(data[0])
print "CID: " + cid
# TH Fullname
data = getData(CMD_THFULLNAME, req)
print "TH Fullname: " + thai2unicode(data[0])
# EN Fullname
data = getData(CMD_ENFULLNAME, req)
print "EN Fullname: " + thai2unicode(data[0])
# Date of birth
data = getData(CMD_BIRTH, req)
print "Date of birth: " + thai2unicode(data[0])
# Gender
data = getData(CMD_GENDER, req)
print "Gender: " + thai2unicode(data[0])
# Card Issuer
data = getData(CMD_ISSUER, req)
print "Card Issuer: " + thai2unicode(data[0])
# Issue Date
data = getData(CMD_ISSUE, req)
print "Issue Date: " + thai2unicode(data[0])
# Expire Date
data = getData(CMD_EXPIRE, req)
print "Expire Date: " + thai2unicode(data[0])
# Address
data = getData(CMD_ADDRESS, req)
print "Address: " + thai2unicode(data[0])
# PHOTO
photo = getData(CMD_PHOTO1, req)[0]
photo += getData(CMD_PHOTO2, req)[0]
photo += getData(CMD_PHOTO3, req)[0]
photo += getData(CMD_PHOTO4, req)[0]
photo += getData(CMD_PHOTO5, req)[0]
photo += getData(CMD_PHOTO6, req)[0]
photo += getData(CMD_PHOTO7, req)[0]
photo += getData(CMD_PHOTO8, req)[0]
photo += getData(CMD_PHOTO9, req)[0]
photo += getData(CMD_PHOTO10, req)[0]
photo += getData(CMD_PHOTO11, req)[0]
photo += getData(CMD_PHOTO12, req)[0]
photo += getData(CMD_PHOTO13, req)[0]
photo += getData(CMD_PHOTO14, req)[0]
photo += getData(CMD_PHOTO15, req)[0]
photo += getData(CMD_PHOTO16, req)[0]
photo += getData(CMD_PHOTO17, req)[0]
photo += getData(CMD_PHOTO18, req)[0]
photo += getData(CMD_PHOTO19, req)[0]
photo += getData(CMD_PHOTO20, req)[0]
data = HexListToBinString(photo)
f = open(cid + ".jpg", "wb")
f.write(data)
f.close
# Exit program
sys.exit()
@thanhlaing

This comment has been minimized.

Copy link

commented Oct 12, 2017

Hi,

I am getting the below issue , look like usb interface not found. please help me figure out.

Available readers: []
Traceback (most recent call last):
File "thai-id-card.py", line 128, in
reader = r[0]
IndexError: list index out of range

After I scan for usb from command , I found the reader is showing
Bus 001 Device 007: ID 0ca6:0010 Castles Technology Co., Ltd EZUSB PC/SC Smart Card Reader
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Thanks.

@thanhlaing

This comment has been minimized.

Copy link

commented Oct 12, 2017

Getting another issue. First issue solved after install driver.
Using: CASTLES EZ100PU 00 00
Traceback (most recent call last):
File "thai.py", line 132, in
connection.connect()
File "/usr/lib/python2.7/dist-packages/smartcard/CardConnectionDecorator.py", line 54, in connect
self.component.connect(protocol, mode, disposition)
File "/usr/lib/python2.7/dist-packages/smartcard/pcsc/PCSCCardConnection.py", line 128, in connect
SCardGetErrorMessage(hresult))
smartcard.Exceptions.CardConnectionException: Unable to connect with protocol: T0 or T1. Unknown reader specified.

@bouroo

This comment has been minimized.

Copy link
Owner Author

commented Dec 15, 2017

It's seem CASTLES EZ100PU not work with pcsc.

@northsom

This comment has been minimized.

Copy link

commented Aug 11, 2018

รบกวนสอบถามหน่อยครับ

i have problem about select photo from thai national card by use python3.6 language. My code is below.

Photo=[]
#Photo

Photo_Part1/20

CMD_PHOTO1 = [0x80, 0xb0, 0x01, 0x7B, 0x02, 0x00, 0xFF]
CMD_PHOTOREQ=[0x00, 0xc0, 0x00, 0x00, 0xFF]

Photo_Part2/20

CMD_PHOTO2 = [0x80, 0xb0, 0x02, 0x7A, 0x02, 0x00, 0xFF]

Photo_Part3/20

CMD_PHOTO3 = [0x80, 0xb0, 0x03, 0x79, 0x02, 0x00, 0xFF]

Photo_Part4/20

CMD_PHOTO4 = [0x80, 0xb0, 0x04, 0x78, 0x02, 0x00, 0xFF]

Photo_Part5/20

CMD_PHOTO5 = [0x80, 0xb0, 0x05, 0x77, 0x02, 0x00, 0xFF]

Photo_Part6/20

CMD_PHOTO6 = [0x80, 0xb0, 0x06, 0x76, 0x02, 0x00, 0xFF]

Photo_Part7/20

CMD_PHOTO7 = [0x80, 0xb0, 0x07, 0x75, 0x02, 0x00, 0xFF]

Photo_Part8/20

CMD_PHOTO8 = [0x80, 0xb0, 0x08, 0x74, 0x02, 0x00, 0xFF]

Photo_Part9/20

CMD_PHOTO9 = [0x80, 0xb0, 0x09, 0x73, 0x02, 0x00, 0xFF]

Photo_Part10/20

CMD_PHOTO10 = [0x80, 0xb0, 0x0A, 0x72, 0x02, 0x00, 0xFF]

Photo_Part11/20

CMD_PHOTO11 = [0x80, 0xb0, 0x0B, 0x71, 0x02, 0x00, 0xFF]

Photo_Part12/20

CMD_PHOTO12 = [0x80, 0xb0, 0x0C, 0x70, 0x02, 0x00, 0xFF]

Photo_Part13/20

CMD_PHOTO13 = [0x80, 0xb0, 0x0D, 0x6F, 0x02, 0x00, 0xFF]

Photo_Part14/20

CMD_PHOTO14 = [0x80, 0xb0, 0x0E, 0x6E, 0x02, 0x00, 0xFF]

Photo_Part15/20

CMD_PHOTO15 = [0x80, 0xb0, 0x0F, 0x6D, 0x02, 0x00, 0xFF]

Photo_Part16/20

CMD_PHOTO16 = [0x80, 0xb0, 0x10, 0x6C, 0x02, 0x00, 0xFF]

Photo_Part17/20

CMD_PHOTO17 = [0x80, 0xb0, 0x11, 0x6B, 0x02, 0x00, 0xFF]

Photo_Part18/20

CMD_PHOTO18 = [0x80, 0xb0, 0x12, 0x6A, 0x02, 0x00, 0xFF]

Photo_Part19/20

CMD_PHOTO19 = [0x80, 0xb0, 0x13, 0x69, 0x02, 0x00, 0xFF]

Photo_Part20/20

CMD_PHOTO20 = [0x80, 0xb0, 0x14, 0x68, 0x02, 0x00, 0xFF]

Photo 1

data, sw1, sw2 = connection.transmit(CMD_PHOTO1)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 2

data, sw1, sw2 = connection.transmit(CMD_PHOTO2)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 3

data, sw1, sw2 = connection.transmit(CMD_PHOTO3)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 4

data, sw1, sw2 = connection.transmit(CMD_PHOTO4)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 5

data, sw1, sw2 = connection.transmit(CMD_PHOTO5)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 6

data, sw1, sw2 = connection.transmit(CMD_PHOTO6)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 7

data, sw1, sw2 = connection.transmit(CMD_PHOTO7)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 8

data, sw1, sw2 = connection.transmit(CMD_PHOTO8)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 9

data, sw1, sw2 = connection.transmit(CMD_PHOTO9)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 10

data, sw1, sw2 = connection.transmit(CMD_PHOTO10)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 11

data, sw1, sw2 = connection.transmit(CMD_PHOTO11)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 12

data, sw1, sw2 = connection.transmit(CMD_PHOTO12)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 13

data, sw1, sw2 = connection.transmit(CMD_PHOTO13)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 14

data, sw1, sw2 = connection.transmit(CMD_PHOTO14)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 15

data, sw1, sw2 = connection.transmit(CMD_PHOTO15)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 16

data, sw1, sw2 = connection.transmit(CMD_PHOTO16)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 17

data, sw1, sw2 = connection.transmit(CMD_PHOTO17)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data
print ("Command14: %02X %02X" % (sw1, sw2))

Photo 18

data, sw1, sw2 = connection.transmit(CMD_PHOTO18)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 19

data, sw1, sw2 = connection.transmit(CMD_PHOTO19)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data

Photo 20

data, sw1, sw2 = connection.transmit(CMD_PHOTO20)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+data
print(Photo) # Output =[255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 1, 0, 96, 0, 96, 0, 0, 255, 219, 0, 67, 0, 15, 10, 11, 13, 11, 9, 15, 13, 12, 13, 17, 16, 15, 18, 23, 38, 24, 23, 21, 21, 23, 46, 33, 35, 27, 38, 55, 48, 57, 56, 54, 48, 53, 52, 60, 68, 86, 73, 60, 64, 82, 65, 52, 53, 75, 102 ......]
print(str(len(Photo))) # Output=5100
print(type(Photo)) # <class 'list'>
Photo=HexListToBinString(Photo)
print(Photo) #Output = ÿØÿà\000�JFIF\000���\000\000\000\000ÿÛ\000C\000�

print(str(len(Photo))) #Output is 5100
f=open("cid.jpg","wb")
f.write(Photo)
f.close

When Program run to command "f.write(Photo)" will have error "builtins.TypeError: a bytes-like object is required, not 'str'"

I should fix this error ?

@bouroo

This comment has been minimized.

Copy link
Owner Author

commented Sep 27, 2018

@northsom f.write(Photo) need Photo variable as bytes not string

@bossbojo

This comment has been minimized.

Copy link

commented Dec 6, 2018

you have thai card id version NODEJS ?

@bouroo

This comment has been minimized.

Copy link
Owner Author

commented Jan 3, 2019

you have thai card id version NODEJS ?

@bossbojo Currently No, Now have only pyhton and GO, but I'll looking for nodejs ASAP.

@inlovet

This comment has been minimized.

Copy link

commented Mar 28, 2019

สอบถามหน่อยครับ

ที่ line 21 result += unicode(chr(d),"tis-620")

ลองรันแล้วเจอปัญหานี้อะครับ
NameError: name 'unicode' is not defined

พยายามหาข้อมูลเองเขาบอกให้เปลี่ยนจาก unicode เป็น str เพราะใช้ python version 3
แต่พอลองเปลี่ยนก็เจออีกปัญหาอะครับ
TypeError: decoding str is not supported

มีคำแนะนำมั้ยครับ
ขอบคุณครับ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.