PC/SC Programming Guide

{# Answer-first lead — frontend-gold-standard §1.1. Expects `lead` (plain text, already resolved by apps.content.leads). Rendered directly under the h1 so the self-contained answer is the first prose a reader, a SERP snippet, or an AI extractor meets. No curated-data gate: the view always supplies either curated content_summary or a computed fallback. #}

Programming with the PC/SC API for smart card communication: connecting readers, transmitting APDUs, and error handling. It covers PC/SC Programming Guide, PC/SC Architecture, C / Native API, Python — pyscard, and Java — javax.smartcardio. The guide is part of the การเขียนโปรแกรม series and about 4 minutes of reading.

Embed This Widget

Theme


      
    

Widget powered by . Free, no account required.

| 4 min read

PC/SC Programming Guide

PC/SC (Personal Computer/Smart Card) is the middleware standard that abstracts smart card reader hardware behind a portable API. Defined in the PC/SC Workgroup specifications (v2.0), it is implemented natively on Windows (WinSCard), macOS (CryptoTokenKit + PCSC framework), and Linux (pcsc-lite). This guide shows how to exchange APDU commands from C, Python, and Java.

PC/SC Architecture

Application
    │ SCard* API (WinSCard / PCSC/PCSC-lite)
    ▼
Resource Manager (SCardSvr / pcscd)
    │ IFD Handler (per-reader driver)
    ▼
Reader hardware (USB CCID)
    │ ISO 7816-3 electrical interface
    ▼
Smart card chip

The Resource Manager serialises access to shared readers; multiple applications can open sessions but only one transmits at a time per card.

C / Native API

#include <winscard.h>   // Windows
// #include <PCSC/winscard.h>  // macOS / Linux

SCARDCONTEXT hContext;
SCARDHANDLE  hCard;
DWORD        dwActiveProtocol;

// 1. Establish context
SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);

// 2. Connect to card
SCardConnect(hContext, "Gemalto PC Twin Reader", SCARD_SHARE_SHARED,
             SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
             &hCard, &dwActiveProtocol);

// 3. Transmit APDU — SELECT by AID (A0 00 00 00 03 = Visa)
SCARD_IO_REQUEST ioRequest = { dwActiveProtocol, sizeof(SCARD_IO_REQUEST) };
BYTE cmdAPDU[] = { 0x00, 0xA4, 0x04, 0x00, 0x07,
                   0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00 };
BYTE respBuf[256];
DWORD respLen = sizeof(respBuf);

SCardTransmit(hCard, &ioRequest, cmdAPDU, sizeof(cmdAPDU),
              NULL, respBuf, &respLen);

// respBuf[respLen-2], respBuf[respLen-1] = SW1, SW2
printf("SW: %02X %02X\n", respBuf[respLen-2], respBuf[respLen-1]);

// 4. Disconnect
SCardDisconnect(hCard, SCARD_LEAVE_CARD);
SCardReleaseContext(hContext);

Python — pyscard

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

# List available readers
reader_list = readers()
print(reader_list)  # ['Gemalto PC Twin Reader 0']

conn = reader_list[0].createConnection()
conn.connect()

# ATR
print("ATR:", toHexString(conn.getATR()))

# SELECT AID (Visa)
SELECT = [0x00, 0xA4, 0x04, 0x00, 0x07,
          0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00]
data, sw1, sw2 = conn.transmit(SELECT)
print(f"SW: {sw1:02X} {sw2:02X}")
print("Response:", toHexString(data))

# READ BINARY (first 16 bytes from EF.COM in ePassport)
READ_BINARY = [0x00, 0xB0, 0x00, 0x00, 0x10]
data, sw1, sw2 = conn.transmit(READ_BINARY)

Java — javax.smartcardio

import javax.smartcardio.*;
import java.util.List;

TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
CardTerminal terminal = terminals.get(0);

Card card = terminal.connect("*");  // auto-negotiate T=0 or T=1
CardChannel channel = card.getBasicChannel();

// SELECT AID
byte[] selectAID = {0x00, (byte)0xA4, 0x04, 0x00, 0x07,
                    (byte)0xA0,0x00,0x00,0x00,0x03,0x10,0x10, 0x00};
CommandAPDU cmd = new CommandAPDU(selectAID);
ResponseAPDU resp = channel.transmit(cmd);

System.out.printf("SW: %04X%n", resp.getSW());
System.out.println("Data: " + bytesToHex(resp.getData()));

card.disconnect(false);

Handling Common Status Words

SW1 SW2 Meaning Action
90 00 Success Process response data
61 xx xx bytes available — send GET RESPONSE 00 C0 00 00 xx
6C xx Wrong Le — resend with Le=xx Retry with correct Le
6A 82 File not found Verify AID / file path
69 82 Security status not satisfied Verify or authenticate first
63 Cx PIN wrong, x tries remaining Prompt user
69 83 Authentication method blocked Unlock with PUK
6F 00 Unspecified error Card may need reset

The 61 xx case is common on T=0 cards — the card signals that more data is ready:

while sw1 == 0x61:
    GET_RESPONSE = [0x00, 0xC0, 0x00, 0x00, sw2]
    more_data, sw1, sw2 = conn.transmit(GET_RESPONSE)
    data.extend(more_data)

Reader Discovery and Event Monitoring

from smartcard.CardMonitor import CardMonitor
from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest

# Wait for card insertion (5 second timeout)
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=5, cardType=cardtype)
cardservice = cardrequest.waitforcard()
cardservice.connection.connect()

Extended Length APDUs

For large data transfers (PQC keys, certificate chains):

# Extended length command: Lc encoded as 00 [Lc_high] [Lc_low]
#                          Le encoded as [Le_high] [Le_low]
def build_extended_apdu(cla, ins, p1, p2, data=None, le=None):
    header = [cla, ins, p1, p2]
    if data:
        header += [0x00, len(data) >> 8, len(data) & 0xFF] + list(data)
    if le is not None:
        header += [le >> 8, le & 0xFF]
    return header

Debugging Tips

  • Use pcsc_scan to capture raw ATR and reader names
  • The ATR Parser decodes historical bytes and protocol bits
  • The APDU Builder constructs and explains command/response pairs
  • Wrap all SCard calls in error checking — SCARD_E_NO_SMARTCARD is common when a card is removed mid-session

For reader hardware setup, see Smart Card Reader Setup Guide. For the ISO 7816 APDU standard underlying every command in this guide, see ISO 7816 Parts Guide.

{# FAQ block + FAQPage JSON-LD. Expects `faq_items` = [{"question", "answer"}]. Native
/ (frontend-gold-standard §1.7): the answer text is in the initial HTML and stays reachable with JavaScript disabled, so crawlers and AI extractors read answer-shaped chunks without depending on Alpine. #}

Frequently Asked Questions

Which functions are required to send an APDU over PC/SC?

Three calls cover the minimum path: SCardEstablishContext opens a context against the resource manager, SCardConnect attaches to a card in a named reader and negotiates T=0 or T=1, and SCardTransmit sends the command APDU and returns the response with its SW1-SW2 status word in the final two bytes. SCardDisconnect releases the card.

Is the PC/SC API the same on Windows, macOS, and Linux?

The API surface is the same; the implementation and header differ. Windows provides WinSCard through winscard.h, macOS provides CryptoTokenKit with the PCSC framework, and Linux provides pcsc-lite; on macOS and Linux the header is PCSC/winscard.h. PC/SC Workgroup specification v2.0 defines the shared contract.

Can two applications use the same reader at once?

The Resource Manager — SCardSvr on Windows, pcscd on Linux and macOS — serialises access. Multiple applications may open sessions against a shared reader, but only one transmits to a given card at a time.

Who are the smart card guides written for?

Our guides cover a range of experience levels. Getting Started guides introduce smart card fundamentals. Security guides address Common Criteria certification and key management. Programming guides target developers working with APDU commands, JavaCard applets, and GlobalPlatform card management.