PC/SC Programming Guide
Programming with the PC/SC API for smart card communication: connecting readers, transmitting APDUs, and error handling.
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_scanto 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_SMARTCARDis 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.