PC/SC Programming Guide

Programming with the PC/SC API for smart card communication: connecting readers, transmitting APDUs, and error handling.

| 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 AIDAIDProtocolUnique identifier for card applications.Click to view → / 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=0T=0ProtocolCharacter-oriented smart card protocol.Click to view → 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 APDUAPDUProtocolCommunication unit between card and reader.Click to view → standard underlying every command in this guide, see ISO 7816 Parts Guide.

अक्सर पूछे जाने वाले प्रश्न

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.