16 Oct 2026

Regardless whether you are on Android or desktop, you should at this point have an implementation of the CardChannel interface (be it NFCCardChannel or PCSCCardChannel). You can now start working with the card. The first thing to do is creating a KeycardCommandSet instance. This class gives access to all of the applet functionality, wrapping the low-level APDUs in easy to use methods. All other classes in the SDK are helper to format parameters and parse responses from the card. To create a command set, just do

// cardChannel is our CardChannel instance
KeycardCommandSet cmdSet = new KeycardCommandSet(cardChannel);

If you also need to accept cards from the Keycard test CA, or want to pin specific card identities, see Secure Channel.

Modern SmartCards can have several applications installed, so after connection with the card you need to select the Keycard applet. This is easily done with

// The checkOK method can be called on any APDUResponse object to confirm that the command succeeded
cmdSet.select().checkOK();

While this correctly selects the applet, it discards the card response, which contains information that can be useful to identify this specific card and its state. For this reason we could rewrite this as

ApplicationInfo info = new ApplicationInfo(cmdSet.select().checkOK().getData());
 
// Tells if the card is initialized, i.e. if a PIN and a PUK are set. If it is not, it must be initialized and no
// other operation is possible. Note that initialization sets up only the credentials used to authenticate the user,
// it does not create a wallet on the card
info.isInitializedCard();
 
// Returns the version of the applet, e.g. 0x0400 for 4.0. Also available as a string with getAppVersionString()
info.getAppVersion();
 
// Tells if the card has a wallet or not. If no wallet is available, you must create one before you can perform most
// operations on the card
info.hasMasterKey();
 
// Returns the UID of the master key of the wallet. The UID is a value generated from the public key and is useful to
// identify if the card has the expected wallet.
info.getKeyUID();
 
// Remaining PIN attempts, without having to open a secure channel. 4.0 cards only
info.getPINRetries();
 
// True if a LEE seed is loaded on the card. 4.0 cards only
info.isLEEMode();
 
// The raw 98-byte card identity certificate. 4.0 cards only
info.getCertData();

The application remains selected until another applet is explicitly selected, or the card is powered off (for example is removed from the field)

note
On 4.0 cards getInstanceUID() and getFreePairingSlots() have no meaning — the instance UID and the pairing slot count are no longer in the SELECT response, and pairing does not exist. Use getKeyUID() to identify the wallet and getCertData() for a card identifier that survives a reinstall. The SDK returns null/zero for the removed fields rather than failing, so the same code runs on both card generations.

This step is necessary to bring the initial credentials on the Keycard instance. When the card is not initialized, it cannot perform any operation. Initialization sets the PIN and PUK and requires no prior authentication. Once the card is initialized, it cannot be initialized again (though the credentials themselves can be changed later, with authentication).

// Usually, you want to check if the card is initialized before trying to initialize it, otherwise you will receive an
// error.
if (!info.isInitializedCard()) {
  // The PIN must be 6 digits and the PUK 12 digits, both as strings. The third argument is the pairing secret,
  // which 4.0 does not use - pass an empty array.
  cmdSet.init(pin, puk, new byte[0]).checkOK();
}

You can also specify the number of retries for PIN/PUK and a duress PIN

cmdSet.init(pin, duressPin, puk, new byte[0], (byte) 3, (byte) 5).checkOK();

The init overloads that take a pairing password instead of a secret are there for applet 3.x cards, which pair. On a 4.0 card the secret must be empty, otherwise the card rejects the payload length.

Last edited
16 Oct 2026