-
-
Save bouroo/8b34daf5b7deed57ea54819ff7aeef6e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
# Kawin Viriyaprasopsook<[email protected]> | |
# 2025-06-15 | |
# sudo apt-get -y install pcscd python-pyscard python-pil | |
from dataclasses import dataclass, field | |
from pathlib import Path | |
from smartcard.System import readers | |
from smartcard.util import toHexString | |
import sys | |
from typing import Callable, List, Optional, Tuple | |
# Common APDU constants | |
APDU_SELECT_COMMAND = [0x00, 0xA4, 0x04, 0x00, 0x08] | |
APDU_APPLET_ID = [0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01] | |
SW_SUCCESS = [0x90, 0x00] | |
# Decoder function | |
def thai2unicode(data: List[int]) -> str: | |
"""Decodes TIS-620 bytes to Unicode, replacing '#' with spaces and stripping whitespace.""" | |
return ( | |
bytes(data) | |
.decode('tis-620', errors='replace') | |
.replace('#', ' ') | |
.strip() | |
) | |
@dataclass(frozen=True) | |
class APDUCommand: | |
"""Represents an APDU command for reading a specific field from the card.""" | |
ins: List[int] | |
label: str | |
decoder: Callable[[List[int]], str] = thai2unicode | |
class SmartCardError(Exception): | |
"""Custom exception for smart card related errors.""" | |
pass | |
class SmartCardConnection: | |
"""Manages the low-level connection and communication with the smart card.""" | |
def __init__(self, connection): | |
self.conn = connection | |
self.get_response_apdu_prefix: List[int] = [] # Prefix for GET RESPONSE command | |
def connect(self) -> None: | |
"""Establishes connection to the card and determines the GET RESPONSE prefix.""" | |
self.conn.connect() | |
atr = self.conn.getATR() | |
print("ATR:", toHexString(atr)) | |
# Determine GET RESPONSE command based on ATR (e.g., for specific card types) | |
# 0x3B 0x67 is a common prefix for some Thai ID cards | |
self.get_response_apdu_prefix = [0x00, 0xC0, 0x00, 0x01] if atr[:2] == [0x3B, 0x67] else [0x00, 0xC0, 0x00, 0x00] | |
def transmit(self, apdu: List[int]) -> Tuple[List[int], int, int]: | |
"""Transmits an APDU command and returns (data, sw1, sw2).""" | |
return self.conn.transmit(apdu) | |
class SmartCard: | |
"""High-level interface for interacting with a Thai National ID card.""" | |
def __init__(self, connection: SmartCardConnection): | |
self.conn = connection | |
def initialize(self) -> None: | |
"""Selects the Thai ID card applet.""" | |
apdu = APDU_SELECT_COMMAND + APDU_APPLET_ID | |
_, sw1, sw2 = self.conn.transmit(apdu) | |
if [sw1, sw2] != SW_SUCCESS: | |
raise SmartCardError(f"Failed to select applet: {sw1:02X} {sw2:02X}") | |
print(f"Select Applet: {sw1:02X} {sw2:02X}") | |
def _get_data_with_get_response(self, command_apdu: List[int]) -> List[int]: | |
""" | |
Sends an APDU command and then issues a GET RESPONSE command to retrieve data. | |
This is common for commands that return SW1/SW2 first, indicating data is available. | |
""" | |
# Send the initial command | |
_, sw1, sw2 = self.conn.transmit(command_apdu) | |
if [sw1, sw2] != SW_SUCCESS: | |
raise SmartCardError(f"Command failed ({toHexString(command_apdu)}): {sw1:02X} {sw2:02X}") | |
# Request the actual data using GET RESPONSE | |
# The Le byte (last byte of the original command) indicates the expected length | |
get_response_apdu = self.conn.get_response_apdu_prefix + [command_apdu[-1]] | |
data, sw1, sw2 = self.conn.transmit(get_response_apdu) | |
if [sw1, sw2] != SW_SUCCESS: | |
raise SmartCardError(f"GET RESPONSE failed ({toHexString(get_response_apdu)}): {sw1:02X} {sw2:02X}") | |
return data | |
def read_field(self, cmd: APDUCommand) -> str: | |
"""Reads a specific field from the card using the provided APDUCommand.""" | |
data = self._get_data_with_get_response(cmd.ins) | |
result = cmd.decoder(data) | |
print(f"{cmd.label}: {result}") | |
return result | |
def read_photo(self, cid: str, segments: int = 20) -> None: | |
"""Reads the photo from the card and saves it as a JPEG file.""" | |
# Base command for reading photo segments. P2 (cmd[4]) is the segment index. | |
# Le (cmd[-1]) is the expected length of each segment. | |
base_photo_cmd = [0x80, 0xB0, 0x00, 0x78, 0x00, 0x00, 0xFF] | |
photo_data = bytearray() | |
for i in range(1, segments + 1): | |
current_cmd = base_photo_cmd.copy() | |
current_cmd[4] = i # Set P2 to the current segment index | |
try: | |
segment_data = self._get_data_with_get_response(current_cmd) | |
photo_data.extend(segment_data) | |
except SmartCardError as e: | |
print(f"Warning: Could not read photo segment {i}: {e}") | |
# Continue to next segment, or break if critical | |
break # Break if a segment fails, as subsequent ones might also fail | |
if photo_data: | |
filename = Path(f"{cid}.jpg") | |
try: | |
filename.write_bytes(photo_data) | |
print(f"Photo saved as {filename}") | |
except IOError as e: | |
print(f"Error saving photo to {filename}: {e}") | |
else: | |
print("No photo data retrieved.") | |
def select_reader() -> Optional[object]: | |
"""Prompts the user to select a smartcard reader.""" | |
rlist = readers() | |
if not rlist: | |
print("No smartcard readers found.") | |
return None | |
print("Available readers:") | |
for i, r in enumerate(rlist): | |
print(f" [{i}] {r}") | |
try: | |
choice_str = input("Select reader [0]: ") | |
choice = int(choice_str) if choice_str else 0 | |
except ValueError: | |
print("Invalid input. Defaulting to reader 0.") | |
choice = 0 | |
if not (0 <= choice < len(rlist)): | |
print(f"Invalid reader choice '{choice}'. Selecting default reader 0.") | |
choice = 0 | |
return rlist[choice] | |
def main(): | |
reader = select_reader() | |
if reader is None: | |
sys.exit(1) | |
conn = None # Initialize conn to None for finally block | |
try: | |
conn = SmartCardConnection(reader.createConnection()) | |
conn.connect() # Establish connection and determine GET RESPONSE prefix | |
card = SmartCard(conn) | |
card.initialize() # Select the applet | |
commands = [ | |
APDUCommand([0x80, 0xB0, 0x00, 0x04, 0x02, 0x00, 0x0D], "CID"), | |
APDUCommand([0x80, 0xB0, 0x00, 0x11, 0x02, 0x00, 0x64], "TH Fullname"), | |
APDUCommand([0x80, 0xB0, 0x00, 0x75, 0x02, 0x00, 0x64], "EN Fullname"), | |
APDUCommand([0x80, 0xB0, 0x00, 0xD9, 0x02, 0x00, 0x08], "Date of birth"), | |
APDUCommand([0x80, 0xB0, 0x00, 0xE1, 0x02, 0x00, 0x01], "Gender"), | |
APDUCommand([0x80, 0xB0, 0x00, 0xF6, 0x02, 0x00, 0x64], "Card Issuer"), | |
APDUCommand([0x80, 0xB0, 0x01, 0x67, 0x02, 0x00, 0x08], "Issue Date"), | |
APDUCommand([0x80, 0xB0, 0x01, 0x6F, 0x02, 0x00, 0x08], "Expire Date"), | |
APDUCommand([0x80, 0xB0, 0x15, 0x79, 0x02, 0x00, 0x64], "Address"), | |
] | |
cid = "" | |
for cmd in commands: | |
try: | |
result = card.read_field(cmd) | |
if cmd.label == "CID": | |
cid = result | |
except SmartCardError as e: | |
print(f"Error reading {cmd.label}: {e}") | |
except Exception as e: | |
print(f"Unexpected error reading {cmd.label}: {e}") | |
if cid: | |
card.read_photo(cid) | |
else: | |
print("CID not found; skipping photo extraction.") | |
except SmartCardError as e: | |
print(f"Smart Card Error: {e}") | |
sys.exit(1) | |
except Exception as e: | |
print(f"An unexpected error occurred: {e}") | |
sys.exit(1) | |
finally: | |
# Disconnect from the card if connection was established | |
if conn and conn.conn: | |
try: | |
conn.conn.disconnect() | |
print("Disconnected from smart card.") | |
except Exception as e: | |
print(f"Error during disconnect: {e}") | |
if __name__ == "__main__": | |
main() |
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.
It's seem CASTLES EZ100PU not work with pcsc.
รบกวนสอบถามหน่อยครับ
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 ?
@northsom f.write(Photo) need Photo variable as bytes not string
you have thai card id version NODEJS ?
สอบถามหน่อยครับ
ที่ 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
มีคำแนะนำมั้ยครับ
ขอบคุณครับ
รบกวนสอบถามหน่อยครับ
i have problem about select photo from thai national card by use python3.6 language. My code is below.
Photo=[]
#PhotoPhoto_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+dataPhoto 2
data, sw1, sw2 = connection.transmit(CMD_PHOTO2)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 3
data, sw1, sw2 = connection.transmit(CMD_PHOTO3)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 4
data, sw1, sw2 = connection.transmit(CMD_PHOTO4)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 5
data, sw1, sw2 = connection.transmit(CMD_PHOTO5)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 6
data, sw1, sw2 = connection.transmit(CMD_PHOTO6)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 7
data, sw1, sw2 = connection.transmit(CMD_PHOTO7)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 8
data, sw1, sw2 = connection.transmit(CMD_PHOTO8)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 9
data, sw1, sw2 = connection.transmit(CMD_PHOTO9)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 10
data, sw1, sw2 = connection.transmit(CMD_PHOTO10)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 11
data, sw1, sw2 = connection.transmit(CMD_PHOTO11)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 12
data, sw1, sw2 = connection.transmit(CMD_PHOTO12)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 13
data, sw1, sw2 = connection.transmit(CMD_PHOTO13)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 14
data, sw1, sw2 = connection.transmit(CMD_PHOTO14)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 15
data, sw1, sw2 = connection.transmit(CMD_PHOTO15)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 16
data, sw1, sw2 = connection.transmit(CMD_PHOTO16)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 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+dataPhoto 19
data, sw1, sw2 = connection.transmit(CMD_PHOTO19)
data, sw1, sw2 = connection.transmit(CMD_PHOTOREQ)
Photo=Photo+dataPhoto 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 ?
I'm having the same problem with you, how to fix it?
For python3 you can just do the following to get the photo writing correctly
Update the thai2unicode method
def thai2unicode(data):
if isinstance(data, list):
return bytes(data).decode('tis-620').strip().replace('#', ' ')
else :
return data
Replace the line with HexListToBinString with the following
data = bytes(photo)
@bouroo Thanks for this, it has been very helpful.
Thanks for your code.
I tried this on python3. it doesn't work on reading profile picture.
I changed some code of you then it works.
My code
https://github.com/pstudiodev1/lab-python3-th-idcard
Thanks
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.