Many classes have moved or changed from version 1.0 to 2.0. Many of these changes are simple method renames, or packages that have moved, and should be straightforward. This guide will focus on the larger structural changes and give a better understanding of how to adapt your app from using YubiKit 1.0 to 2.0.
In YubiKit 1.0 there was a yubikit
module, which contained most of "the
basics". This includes shared utility classes as well as core Android classes
used for low-level device communication. In 2.0 we’ve replaced this module with
two new ones:
-
core - Shared classes used by all other modules
-
android - Android-specific implementation of low-level device communication, and Android specific utilities.
All other modules have core
as a dependency, and none of them depend directly
on android
. You will need to explicitly add the android
module to your
project, but you will not need to add core
as it will be brought in as a
transitive dependency.
The following modules have been renamed:
-
The
mgmt
module has been renamed tomanagement
, to make the name more clear. -
The
otp
module has been renamed toyubiotp
, to clarify the scope of the module: It handles communication with the YubiKey OTP application specifically, whereas theoath
module is used for OATH TOTP and HOTP, etc.
Example build.gradle for YubiKit 1.0
dependencies {
// Core library, low-level communication with YubiKey
implementation 'com.yubico.yubikit:yubikit:1.0.0'
// Application specific classes
implementation 'com.yubico.yubikit:piv:1.0.0'
implementation 'com.yubico.yubikit:mgmt:1.0.0'
}
Equivalent build.gradle for YubiKit 2.0
dependencies {
// Low-level communication with YubiKey (core is also automatically pulled in)
implementation 'com.yubico.yubikit:android:2.0.0'
// Application specific classes
implementation 'com.yubico.yubikit:piv:2.0.0'
implementation 'com.yubico.yubikit:management:2.0.0'
}
The following functionality has been moved:
-
Android specific functionality from the
otp
module, such as theOtpActivity
, has been moved to theandroid
module and made more generic. -
Configuration and functionality related to the YubiKey OTP Application has been moved from the
mgmt
module into theyubiotp
module, as part of the newYubiOtpSession
. -
Exceptions have been moved from the
com.yubico.yubikit.exceptions
package to the various other packages where they are most relevant.
Several classes and interfaces have been renamed in accordance to the terminology used in the Concepts page.
The following have been renamed:
-
UsbSession
andNfcSession
are nowUsbYubiKeyDevice
andNfcYubiKeyDevice
. -
XYZApplication
is nowXYZSession
to better reflect its purpose: It represents an established session with an Application running on a YubiKey. -
Iso7816Connection
is nowSmartCardConnection
.
Just as before the YubiKitManager
class is used to listen for YubiKeys over
both USB and NFC, using the startUsbDiscovery
and startNfcDiscovery
methods. These methods used to take an instance of UsbSessionListener
or
NfcSessionListener
, respectively. These classes have been replaced with the
more generic Callback<T>
interface.
YubiKit 1.0:
yubiKitManager.startUsbDiscovery(new UsbConfiguration(), new UsbSessionListener() {
@Override
public void onSessionReceived(UsbSession session, boolean hasPermissions) {
// A YubiKey was plugged in
}
@Override
public void onSessionRemoved(UsbSession session) {
// Do something when the YubiKey is removed
}
@Override
public void onRequestPermissionsResult(UsbSession session, boolean isGranted) {
// Using the default UsbConfiguration this will never happen, as permission will automatically
// be requested by the YubiKitManager prior to invoking onSessionReceived.
}
});
YubiKit 2.0:
yubiKitManager.startUsbDiscovery(new UsbConfiguration(), device -> {
// A YubiKey was plugged in
if(!device.hasPermission()) {
// Using the default UsbConfiguration this will never happen, as permission will automatically
// be requested by the YubiKitManager, and this method won't be invoked unless it is granted.
}
device.setOnClosed(() -> {
// Do something when the YubiKey is removed
}))
});
When using the default configuration, permissions will be handled by the
YubiKitManager and the caller doesn’t need to check if the user has granted
permission for the device. If this handling is disabled then the
hasPermission
method can be used to check for this, and the standard Android
USB Host API can be used to request permission for the device.
For NFC the changes are more subtle. The NfcSessionListener is replaced with a
Callback<NfcYubiKeyDevice>, and NfcDisabledException
and
NfcNotFoundException
are replaced with a single NfcNotAvailableException
.
YubiKit 1.0:
try {
yubiKitManager.startNfcDiscovery(new NfcConfiguration(), activity, new NfcSessionListener() {
void onSessionReceived(NfcSession session) {
// A YubiKey was brought within NFC range
}
});
} catch (NfcDisabledException e) {
// NFC is available, but turned off
} catch (NfcNotFoundException e) {
// NFC is not available so this feature does not work on this device
}
YubiKit 2.0:
try {
yubiKitManager.startNfcDiscovery(new NfcConfiguration(), activity, device -> {
// A YubiKey was brought within NFC range
});
} catch (NfcNotAvailableException e) {
if (e.isDisabled()) {
// NFC is available, but turned off
} else {
// NFC is not available so this feature does not work on this device
}
}
In YubiKit 1.0 opening a Connection to a YubiKey was a synchronous operation. Due to the nature of how YubiKeys work, only a single Connection can be used at one time, and the user was responsible for ensuring Connection access never happened from multiple threads at once. For this reason it was recommended to place all such calls within the context of a single Thread, by using an ExecutorService:
// Set up and manage the lifecycle of an ExecutorService:
ExecutorService executorService = ...
executorService.execute {
//connect to the key / start the connection
try(Iso7816Connection connection = session.openIso7816Connection()) {
// Send commands to the connection, read responses, etc.
} catch (IOException e) {
// handle error that occurred during communication with key
}
}
In YubiKit 2.0 this has instead become an asynchronous operation, which you can invoke from any Thread. The callback will be run in a Thread managed by the YubiKitManager, so that the caller doesn’t need to worry about it:
// Request a new Connection. When available, the callback will be invoked in a worker thread.
device.requestConnection(SmartCardConnection.class, result -> {
// The result is a Result<SmartCardConnection, IOException>, which represents either a successful connection, or an error.
try {
SmartCardConnection connection = result.getValue(); // This may throw an IOException
// Send commands to the connection, read responses, etc.
} catch(IOException e) {
// Handle errors
}
});