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. #}PC/SC — Personal Computer/Smart Card — is the middleware standard that hides reader hardware behind a portable API, specified by the PC/SC Workgroup at version 2.0 and implemented natively as WinSCard on Windows, CryptoTokenKit with the PCSC framework on macOS, and pcsc-lite on Linux. The stack runs from the application through the SCard API to a Resource Manager (SCardSvr or pcscd), then to a per-reader IFD handler, USB CCID hardware, and finally the ISO 7816-3 electrical interface to the chip. The Resource Manager serialises access, so several applications may hold sessions on a shared reader while only one transmits to a given card at a time. Application code needs only three calls — SCardEstablishContext, SCardConnect, and SCardTransmit — to send any APDU and read back the response together with its SW1-SW2 status word.
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_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 APDU standard underlying every command in this guide, see ISO 7816 Parts Guide.
(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.
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.