Java Card and eSIM Applet Development for Secure Identity
Java Card is a small, deliberately-constrained subset of Java designed to run inside Secure Elements. It is the most common environment in which eSIM and eUICC identity logic actually lives. Understanding the way Java Card shapes applet design — what it makes easy, what it makes hard, and where the trust boundaries actually fall — is the difference between an applet that passes review and one that doesn't.
Scope note: this article is about Java Card as it is used inside a SIM or eUICC — identity applets that live on the same secure element as the operator profile. Full-cycle Java Card applet development across secure elements generally, outside the SIM context, is an AmbiSecure capability.
Why Java Card?
Java Card exists because Secure Element manufacturers needed a portable way to host vendor-supplied logic without exposing the underlying hardware to arbitrary native code. The Java Card runtime gives applets a stable, narrow API; the Java Card Virtual Machine enforces type safety; the Global Platform layer manages applet lifecycle and secure messaging. This combination is what makes it possible to ship identity applets onto millions of Secure Elements without writing custom firmware for every chip family.
What an eSIM identity applet does
At its core, an identity applet on an eSIM/eUICC has a small set of responsibilities:
- Hold the device's identity keys in SE-resident storage.
- Sign assertions using those keys, never exposing the keys themselves.
- Authenticate counterparts — the relying party, the provisioning service, the back-end.
- Implement a state machine — initialized, provisioned, in-service, retired.
- Report state — to firmware, to provisioning workflows, to attestation logic.
Every one of these is a place where applet design choices can either narrow the trust surface or widen it. The discipline is in keeping the applet's job small.
Trust boundaries that matter
Inside the applet vs outside the SE
The most important boundary is the one between applet code (inside the SE, inside the JCVM) and everything else. Crossing this boundary means going through the Secure Element's command/response interface, which should be the only way the outside world interacts with the applet.
Between applets
Java Card and Global Platform define how applets share resources — Shareable Interfaces, Security Domains, package isolation. An applet should be designed to assume that other applets on the same SE are not in its trust boundary unless explicitly configured.
Between persistent and transient state
Java Card distinguishes persistent and transient objects. Secrets that should survive power loss live in persistent storage; per-session intermediate values do not. Getting this wrong is one of the most common applet bugs.
The atomicity boundary
Java Card has a transaction model for atomic updates. Identity-affecting state changes — provisioning a new credential, transitioning the applet to a new lifecycle phase — should happen inside a transaction, never as a sequence of individual writes.
The isolation model, and what it does not cover
"Applets are isolated" is true and, on its own, misleading. Java Card isolation is a specific runtime mechanism with a specific scope, and the eUICC context both extends and weakens the assumptions it was designed under. Being precise about the mechanism is what lets you tell a design decision from a hope.
What the applet firewall actually enforces
The Java Card runtime assigns every applet instance to a context, and every object on the card is owned by the context that created it. The virtual machine checks ownership on access: code running in one context cannot read or write an object owned by another, and the check happens at runtime rather than being something the compiler can be talked out of. This is the firewall, and it is a genuinely strong boundary — it is why two mutually distrusting applications can share a card at all.
It rests on a precondition that is easy to forget: the firewall assumes type safety. It is bytecode verification — off-card, on-card, or both, depending on the platform — that guarantees a CAP file cannot forge a reference or confuse the VM about an object's type. The firewall enforces a policy; the verifier is what makes the policy meaningful. A deployment model that weakens or skips verification does not weaken it a little.
The paths that legitimately cross it
The firewall has documented openings, and every one of them is a place where a design assumption can quietly become an exposure. They are not flaws — they are the mechanisms that make a multi-application card useful — but they are the surface worth enumerating explicitly at design time.
- Shareable Interface Objects. An applet can deliberately expose an interface for another context to invoke. This is the sanctioned channel, and the security argument lives entirely in what the interface accepts and what it reveals. A shareable method that returns a reference to internal state has handed the boundary away.
- The JCRE context. The runtime environment itself is privileged and can reach any object. Anything it exposes is exposed to everything on the card.
- Global and entry point objects. The APDU buffer is the important one: it is a global array, reachable across contexts by design because every applet needs it. Key material that lingers in it after use is no longer behind the boundary, and this is the single most common way an otherwise correct applet leaks.
- Static fields. Statics are not owned by a context. A reference parked in a static field is reachable in a way the object it points to was not intended to be.
What the firewall never claimed to do
The firewall separates applications from each other inside the card. It says nothing about several boundaries that matter as much or more.
| Boundary | Enforced by | What legitimately crosses | What it does not stop |
|---|---|---|---|
| Applet to applet | Java Card firewall, resting on bytecode verification | Shareable interfaces, JCRE entry points, global arrays, statics | A shareable interface that gives away more than it intended |
| Who may load or install | GlobalPlatform Security Domain keys and privileges | Authenticated load, install and delete under an SD's keys | Anything the holder of those keys chooses to do. This is a commercial boundary as much as a cryptographic one |
| Card to host | The APDU command interface — the only path in | Whatever the applet's command handler accepts | A hostile host. Assume the caller is untrusted and may replay, reorder, truncate or abandon a sequence mid-way |
| Applet to physical attacker | The chip's countermeasures, not the runtime | Nothing by design | Fault injection and side-channel work, which the firewall has no visibility of and no defence against |
| Between profiles on an eUICC | Profile isolation under the GSMA architecture, above the firewall | Platform-level operations, which the profile does not control | Anything performed by the platform's own privileged domains |
How the eUICC context changes the threat model
A Java Card applet on a conventional card and the same applet on an eUICC face different adversaries, and three of the differences are structural rather than incidental.
- The loading path became a network path. On a classic card, load and install were factory-floor operations behind physical control. Under remote provisioning they happen over the air, which means the authorisation model that gates them — secure channel establishment, key custody, the correctness of the party holding those keys — is now a live, remote attack surface rather than a manufacturing procedure.
- The host is large, connected and outside the boundary. The LPA, the modem stack and the device OS all sit on the untrusted side of the APDU interface, and all of them are network-facing. The applet's assumption about its caller has to be weaker than it would be behind a dedicated reader.
- You are a tenant, not the landlord. The eUICC platform's own privileged domains can perform profile-level operations your applet cannot observe, prevent or appeal. Whoever holds the platform keys holds authority that no amount of applet-side design will constrain. Establishing who that is, for a given deployment, is a prerequisite to writing a threat model rather than a detail to settle later.
The practical consequence is that an eSIM identity applet has to be written defensively about its own environment in a way a payment applet on a dedicated card does not. Sequences will be interrupted by a modem reset rather than a card removal. State transitions will be attempted by a host that has been compromised. Both are ordinary operating conditions, not edge cases, and the atomicity boundary above and the state-machine discipline below are how they are survived.
Patterns that survive review
Explicit state machine
Identity applets should have a documented, finite state machine. Each state lists the operations it permits. Each transition has a precondition. Reviewers should be able to read the state machine and understand the applet's behavior without reading every line of code.
Single-purpose keys
Each key in the applet should have one job — signing, key wrapping, attestation, etc. Reusing keys across roles makes the security argument harder and the review surface larger.
Defense-in-depth on lifecycle commands
Commands that change state — provisioning, lifecycle transitions, reset — should require authenticated invocation. Authentication should not depend on the applet's own state being intact.
Observable failure
When something goes wrong, the applet should fail in a way that surrounding firmware can detect and a back-end can interpret. Silent failure is one of the most common ways identity logic breaks in the field.
No key material in transient buffers
Decrypted or unwrapped key material should never sit in transient buffers longer than necessary. Java Card provides primitives for this; using them is non-negotiable.
Common mistakes
- Storing identity keys in transient memory by accident, breaking lifecycle continuity.
- Treating applet state as authoritative without authenticating the caller.
- Mixing role-bearing keys (one key for signing and wrapping and authentication).
- Skipping the transaction boundary on lifecycle changes.
- Using vendor-specific Java Card extensions without documenting the dependency, which makes the applet harder to port and review.
Building for review
An eSIM identity applet that's never going to be reviewed is a hypothetical artifact. Anything serious gets reviewed — by an internal security team, by an operator, by an ecosystem partner. Designing for review is not a bonus; it is the work.
- Write the state machine before writing the code.
- Maintain an explicit key inventory: name, lifecycle, role, where it sits.
- Document trust boundaries and what assumptions each one makes.
- Maintain test vectors that exercise lifecycle transitions, not just happy-path operations.
- Be honest about deviations from a reference architecture, and explain them.
Where this fits in eSIM/eUICC engineering
Java Card is the layer where identity logic lives, but it sits inside a larger picture: the Secure Element's OS underneath, the eUICC platform around it, the host firmware on the other side, and the operator infrastructure outside the device. AmbiSecure works on the applet layer with a clear view of the layers above and below — see the architecture page for the surrounding picture, and eUICC Applet Development for the eUICC-specific considerations.
An identity applet is a state machine that holds keys. Almost every applet bug is a state machine bug, a key-role bug, or a transaction bug. Almost everything else is a symptom.
Conclusion
Java Card is the environment where eSIM identity actually happens. Designing applets that respect its constraints, document their trust boundaries, and survive review is not a stretch goal — it is the basic work of building credible embedded identity. The teams that get this right earn the kind of credibility operators and ecosystem partners can act on.
FAQ
Is Java Card the same as Java?
No. Java Card is a constrained subset of Java optimized for Secure Element execution: limited types, limited APIs, explicit transient/persistent semantics.
Can applets be updated in the field?
Yes, through Global Platform mechanisms — but only under controlled lifecycle conditions, and update paths should be designed before the first applet ships.
Do all eUICCs run Java Card?
The vast majority do. Some newer designs use other languages or formal-methods–oriented runtimes, but Java Card remains the most common environment.
Where do an applet's keys live?
Inside the secure element, generated on-chip and non-exportable, so cryptographic operations happen in hardware rather than in device memory.
What isolates one applet from another?
The Java Card applet firewall — a hardware-enforced boundary between co-resident applications on the same secure element.
Related capability: Secure Elements & Java Card · eSIM / eUICC