Repository navigation
Home
Composable Java 17+ library for working with smart cards via APDU command-response pairs.
See the README for quick start, architecture overview, code examples, and Maven coordinates.
- Apdulette - the mental model for composable smart card interactions with Recipe, Chef, and Cookbook
- Architecture - BIBO transport, BIBOSA middleware, Apdulette recipes, Preferences system, and how they compose
- JSON Remote EMV Terminal - remote card interaction with pluggable kernels, modeled on the EMV process architecture
- Mobile APDU Driver - unified smart card access for Android and iOS with web bridge (MOPP-1 + MOPP-2)
- Comparison with other Java smart card libraries - OCF, SCUBA, intarsys, jnasmartcardio and how apdu4j differs
The core abstraction. An interface that sends a bag of bytes and receives a bag of bytes (or a BIBOException) as a
reply. Command-response. Synchronous.
byte[] transceive(byte[] bytes) throws BIBOException;Implementations can be PC/SC, Android NFC, OpenMobileAPI, a network socket, a mock for testing - anything that moves
bytes. The interface lives in apdu4j-core which has zero javax.smartcardio dependency.
APDU is an ancient convention for formatting bags of bytes (CLA, INS, P1, P2, LC, LE, SW...). CommandAPDU and
ResponseAPDU provide convenience methods for constructing and parsing those byte arrays. APDUBIBO wraps a raw BIBO
with typed transmit:
ResponseAPDU transmit(CommandAPDU apdu) throws BIBOException;which is equivalent to:
new ResponseAPDU(bibo.transceive(new CommandAPDU(...).getBytes()));All transport concerns stack via then():
var bibo = transport
.then(GetResponseWrapper::wrap) // auto GET RESPONSE on 61xx
.then(b -> LoggingBIBO.wrap(b, System.out)) // human-readable trace
.then(b -> DumpingBIBO.wrap(b, dumpStream)); // machine-readable replay
var apdu = new APDUBIBO(bibo); // typed APDU accessProtocol handlers (GetResponseWrapper, GetMoreDataWrapper, RetryWithRightLengthWrapper) are also BIBO
implementations - they compose the same way. StatefulBIBO<S> handles secure channel state - see
Architecture.
Preferences is a typed, immutable bag of cross-cutting configuration. Two kinds: Preference.Default has a sensible
default value (tunable by any layer), Preference.Parameter has no default (a discovered fact, readonly once set).
var BLOCK_SIZE = Preference.of("gp.block.size", Integer.class, 255, false);
var SCP_VERSION = Preference.parameter("gp.scp.version", GPSecureChannelVersion.class, true);External configuration resolves through PreferenceProvider - environment variables, system properties, or code. See
Architecture for the full treatment.
BIBOSA wraps a BIBO with a Preferences sidecar. It implements BIBO, so it drops in anywhere a plain transport
is expected, but also carries typed metadata that accumulates as you build the stack.
var stack = new BIBOSA(transport)
.then(GetResponseWrapper::wrap) // Function<BIBO, BIBO> - preferences unchanged
.then(secureChannelMiddleware); // BIBOMiddleware - wraps transport AND adds preferencesA BIBOMiddleware receives the entire BIBOSA and returns a new one with potentially different transport AND enriched
preferences. For runtime-conditional composition, adapt() reads accumulated preferences to decide which middleware to
apply. See Architecture.
A Recipe<T> is a pure description of a card interaction that produces a value of type T. No I/O happens until a
Chef executes it. SousChef executes over a real BIBO, MiseEnPlaceChef dry-runs with expected responses.
var recipe = Cookbook.selectFCI(aid)
.then(fci -> authenticate(keys))
.then(auth -> load(capFileBytes));
var dish = chef.serve(recipe, prefs);KitchenManager is the chef factory - PCSCKitchenManager binds recipes to PC/SC readers. See
Architecture.
A session is a series of APDU transmits. A technical session breaks when BIBOException is thrown. A logical session is
the application's responsibility (interpretation of APDU-s).
A channel is logical multiplexing on top of the physical BIBO. SE access is still serialized to a single
command-response channel. CardBIBO in apdu4j-pcsc manages javax.smartcardio logical channels transparently by
inspecting the CLA byte.
PC/SC is the ages-old desktop C API for enumerating smart card readers and talking to cards. javax.smartcardio is the
Java adaptation. jnasmartcardio is the JNA re-implementation that
fixes the many bugs in SunPCSC.
apdu4j uses jnasmartcardio as the preferred provider and adds per-reader single-threaded executors on top, so PC/SC
operations for a given reader never race. The fluent reader API (Readers.select().withCard().run(bibo -> ...)) handles
all the ceremony.
Modern environments like Android NFC have no notion of PC/SC, but the "reader" concept carries on to API-s
like OpenMobileAPI. apdu4j-core works
in all of these - PC/SC is optional via apdu4j-pcsc.
apdu4j-apdulette-pcsc PCSCKitchenManager (Java 21+)
| \
apdu4j-apdulette apdu4j-pcsc Recipe/Chef + PC/SC readers
| | \
| apdu4j-pcsc-sim \ Synthesized card terminals
| | /
apdu4j-core BIBO, APDUBIBO, BIBOSA, decorators, StatefulBIBO
|
apdu4j-prefs Preference, Preferences, PreferenceProvider
apdu4j-tool Command line interface
apdu4j-report Aggregate test coverage
Use apdu4j-pcsc for desktop applications (includes apdu4j-core and apdu4j-prefs transitively). Use apdu4j-core
alone for Android, embedded, or test-only scenarios. Add apdu4j-apdulette for composable recipe-based card interaction
(requires Java 21+). Add apdu4j-apdulette-pcsc to bind recipes to PC/SC readers via KitchenManager.
There is also apdu4j-tool which provides a command line interface -
see README.