I'm wondering if anyone has had any success with using ignition scripting to read from a card reader. I built some code that can connect to a reader but doesn't return the data I am expecting. If anyone could provide some guidance for what the code should be, or if they've run into a similar issue.
import javax.smartcardio as smartcardio
from array import array
def bytes_to_hex(byte_array):
"""Convert byte array to a hexadecimal string representation."""
return ''.join(['{:02X}'.format(b) for b in byte_array])
def read_smart_card():
try:
# Step 1: Obtain a list of available card terminals
print("Retrieving list of card terminals...")
factory = smartcardio.TerminalFactory.getDefault()
terminals = factory.terminals().list()
# Check if any terminals are available
if not terminals:
print("No card terminals available.")
return
# Step 2: Connect to the first available card terminal
terminal = terminals.get(1) # You may need to adjust the index based on your setup
print("Using terminal: {}".format(terminal.getName()))
# Check if a card is present in the terminal
if terminal.isCardPresent():
print("Card detected. Attempting to connect...")
card = terminal.connect("*") # Connect to the card
print("Card connected successfully.")
# Obtain the basic communication channel with the card
channel = card.getBasicChannel()
# Step 3: Send an APDU command to the card
# This APDU command is an example and may need to be replaced with one specific to your card
print("Sending APDU command to the card...")
commandAPDU = smartcardio.CommandAPDU(array('B', [0x00, 0xB0, 0x00, 0x00, 0x00]))
response = channel.transmit(commandAPDU)
# Step 4: Process and display the response from the card
responseData = response.getData()
print("Response received.")
print("Response data: {}".format(responseData))
print("Response data (hex): {}".format(bytes_to_hex(responseData)))
# Close the connection
card.disconnect(False)
print("Card disconnected.")
else:
print("No card present in terminal.")
except Exception as e:
print("An error occurred:", e)
# Run the smart card reader
read_smart_card()
If you were curious about the output console, this is what I am seeing:
Retrieving list of card terminals...
Using terminal: SCM Microsystems Inc. SCR33x USB Smart Card Reader 0
Card detected. Attempting to connect...
Card connected successfully.
Sending APDU command to the card...
Response received.
Response data: array('b', [0, -80, 0])
Response data (hex): 00-5000
Card disconnected.
What data are you expecting? Do you have documentation about the command and response formats?
Maybe it would make some more sense if I provided some more background.
I have this Python code that uses c_types, and I can get it to run in Visual Studio. The issue comes from the fact that I can't use it in Jython, so I have been trying to convert it but am struggling to do so.
import ctypes
# Use this to generate error codes
def convert_to_error_code(result: int) -> str:
# Convert to unsigned 32 int
result = ctypes.c_uint32(result).value
# Convert to hex to get error code
result = hex(result)
return result
# Load Winscard DLL
winscard_dll = ctypes.WinDLL("C:\\Windows\\SysWOW64\\WinSCard.dll")
# Establish the resource manager context within which database operations are performed
SCARD_SCOPE_USER = ctypes.c_int(0)
phContext = ctypes.c_int(0)
result = winscard_dll.SCardEstablishContext(SCARD_SCOPE_USER, None, None, ctypes.byref(phContext))
print(f"OS Response of Establishing Context Was: {convert_to_error_code(result)}")
# Get list of smart card readers hooked up to the system
smart_card_readers = ctypes.create_string_buffer(800)
pcchReaders = ctypes.c_uint32(400)
result = winscard_dll.SCardListReadersA(phContext, None, smart_card_readers, ctypes.byref(pcchReaders))
print(f"OS Response of Listing Smart Card Readers Was: {convert_to_error_code(result)}")
# Examine returned list of smart card readers and parse out the individual smart card readers
smart_card_reader_list = smart_card_readers.raw.decode().split("\x00")
smart_card_reader_list = [entry for entry in smart_card_reader_list if entry.startswith("SCM")]
print(f"List of Smart Card Readers: {smart_card_reader_list}")
# Load Smart Card Reader DLL
mcscm_dll = ctypes.WinDLL("C:\\Windows\\System32\\MCSCM.dll") # NOTE: Only works with MCSCM.dll from TEMPOMES driver installer
for smart_card_reader_name in smart_card_reader_list:
# Initialize Smart Card Reader with M-CARD API
reader_name = ctypes.c_char_p(smart_card_reader_name.encode('utf-8'))
phMCardContext = ctypes.c_int(0)
pdwDllVersion = ctypes.c_uint32(0)
result = mcscm_dll.MCardInitialize(phContext, reader_name, ctypes.byref(phMCardContext), ctypes.byref(pdwDllVersion))
print(f"MCSCM.dll Response to Initializing Smart Card Readers Was: {convert_to_error_code(result)}")
# Connect to Smart Card
dwConnectMode = ctypes.c_int(0)
byCardType = ctypes.c_int(8)
phMCard = ctypes.c_int(0)
result = mcscm_dll.MCardConnect(phMCardContext, dwConnectMode, byCardType, ctypes.byref(phMCard))
print(f"MCSCM.dll Response to Connecting to Smart Card Reader Was: {convert_to_error_code(result)}")
# Read from SMart Card
bMemZone = ctypes.c_int(0)
dwOffset = ctypes.c_int(0)
pbReadBuffer = (ctypes.c_ubyte * 200)(0)
pbReadLen = ctypes.c_int(100)
result = mcscm_dll.MCardReadMemory(phMCard, bMemZone, dwOffset, ctypes.byref(pbReadBuffer), ctypes.byref(pbReadLen))
print(f"MCSCM.dll Response to Reading from Smart Card Reader Was: {convert_to_error_code(result)}")
data = ""
for character in pbReadBuffer:
if character < 255:
data = data + chr(character)
data = data.split("\x1f")[1:-1] # take care of leading and trailing boundary values
# Parse data
smart_card_info = {"First Name": "",
"Last Name": "",
"MI": "",
"User ID": "",
"Alt User ID": "",
"Index": "",
"Employee ID": ""
}
smart_card_info["First Name"] = data[0] if not data[0] == " " else "None"
smart_card_info["Last Name"] = data[1] if not data[1] == " " else "None"
smart_card_info["MI"] = data[2] if not data[2] == " " else "None"
smart_card_info["User ID"] = data[3] if not data[3] == " " else "None"
smart_card_info["Alt User ID"] = data[4] if not data[4] == " " else "None"
smart_card_info["Index"] = data[5] if not data[5] == " " else "None"
smart_card_info["Employee ID"] = data[6] if not data[6] == " " else "None"
# Print out data to console
print("\n")
print(smart_card_reader_name)
for key, value in smart_card_info.items():
smart_card_field_info = f"{key}: {value}"
print(smart_card_field_info)
print("\n")
# Disconnect from smart card
dwDisposition = ctypes.c_int(0)
result = mcscm_dll.MCardDisconnect(phMCard, dwDisposition)
print(f"MCSCM.dll Response of Disconnecting from Smart Card Reader Was: {convert_to_error_code(result)}")
# Shut down smart card reader
result = mcscm_dll.MCardShutdown(phMCardContext, dwDisposition)
print(f"MCSCM.dll Response of Shutting Donw the Smart Card Reader Was: {convert_to_error_code(result)}")
I am trying to use javax.smartcardio to try and act as the replacement for c_types, but is proving to be more difficult than expected.
From what I've found in the forum, trying to integrate a .dll is difficult to do, which is why I have been trying to find a workaround.