dependencies {
implementation 'com.yubico.yubikit:android:(insert version here)'
}
This module contains the main implementation of the SDK for Android, and is required for all Android development.
To add the Android module as a dependency to your project, add the following to your gradle configuration:
dependencies {
implementation 'com.yubico.yubikit:android:(insert version here)'
}
This module depends on the core module, which will automatically be added as a transitive dependency to your project.
Note
|
YubiKit 2.3.0 deprecated core Logger class in favor of slf4j logging abstraction. Logger will be removed in YubiKit 3.0.0. Migration steps are described in Logging Migration |
Logging is provided through the slf4j
interface. Because YubiKit does not provide logger implementation, applications must provide an implementation to enable logging.
logback-android
is an open-source implementation of slf4j
which can be simply added to an existing Android project to enable YubiKit logging. To do so:
Add required dependencies:
dependencies {
implementation 'com.github.tony19:logback-android:3.0.0'
}
Add assets/logback.xml
. This file configures the logger behaviour. A Logger which outputs everything to logcat
may look as follows:
<configuration xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd" >
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<encoder>
<pattern>%msg</pattern>
</encoder>
</appender>
<!-- Write TRACE (and higher-level) messages to logcat -->
<root level="TRACE">
<appender-ref ref="logcat" />
</root>
</configuration>
Read more about logback configuration here: https://github.com/tony19/logback-android/wiki
Note
|
slf4j is licensed under MIT license and logback-android is licensed under Apache License 2.0 |
Using slf4j
adds more flexibility for apps to describe precisely which events should or should not be logged. Depending on the used implementation, one can redirect logging to different output streams based on various criteria such as log level, logger name and similar. For detailed information about logging through slf4j
see SLF4J Homepage.
YubiKit for Android uses slf4j
log levels for following events:
Level |
Event |
|
Successfully Completed events, such as Configuration written, Private key generated and similar. |
|
Detailed information about what is happening, useful for finding errors during development. No sensitive information is logged. |
|
Unexpected states which do not prevent execution continuation. |
|
Unrecoverable errors in communication, which prevent YubiKit to continue it’s flows. |
|
Detailed information about data which are being being read from/written to devices. This log level may contain sensitive information and should not be enabled for production use. |
Accessing a YubiKey is done with an instance of the YubiKitManager. It provides access over both USB and NFC, and allows discovery of new YubiKeys.
To create an instance of YubikitManager
you will need an Android Context
,
such as an Activity. You can instantiate the class in your Activity’s
onCreate()
method.
YubiKitManager yubiKitManager = new YubiKitManager(context);
Note
|
To make debugging over USB easier, we recommend setting up ADB to connect using WiFi, as described here: https://developer.android.com/studio/command-line/adb#wireless |
To connect to a YubiKey over USB, you need to call startUsbDiscovery
with a callback to handle
attached YubiKeys. This callback will be invoked whenever a new YubiKey is detected over USB.
Again, the onCreate()
method of your Activity is a good place for this.
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
}))
});
To stop listening for USB events, call the stopUsbDiscovery()
method. This can
be done in your Activity’s onDestroy()
method:
yubiKitManager.stopUsbDiscovery();
To connect to a YubiKey over NFC, you need to call startNfcDiscovery
with a callback to handle NFC
YubiKey events. This callback will be invoked whenever a new YubiKey is detected over NFC.
NFC requires an Activity in the foreground, and you should stop listening for NFC events when the
Activity goes into the background. You can use the Activity’s onResume()
method to start listening,
and its onPause()
method to stop:
@Override
public void onResume() {
super.onResume();
try {
yubiKitManager.startNfcDiscovery(new NfcConfiguration(), this, device -> {
// A YubiKey was brought within NFC range
});
} catch (NfcNotAvailableException e) {
if (e.isDisabled()) {
// show a message that user needs to turn on NFC for this feature
} else {
// NFC is not available so this feature does not work on this device
}
}
}
@Override
public void onPause() {
yubiKitManager.stopNfcDiscovery(this);
super.onPause();
}
NFC connection times out after timeout
value of NfcConfiguration()
object. Some operations which run on the YubiKey, for example generation of RSA keys, can take long time to complete. To avoid timeouts, start NFC discovery with configuration with reasonable timeout value.
...
yubiKitManager.startNfcDiscovery(
new NfcConfiguration().setTimeout(25000), // 25 seconds
...
Regardless of if you are using USB or NFC, you need to open a connection to the YubiKey to do anything with it. A YubiKeyDevice manages its own worker thread in which all communication with the Connection should be done. Interaction with a Connection is done within a Callback, and the Connection is automatically closed once the Callback completes.
There are different types of connections, which can be used for different applications. Here is an example of opening a SmartCardConnection and doing some low-level communication with it. In practice you will more likely use one of the Session classes defined in the other modules.
// Request a new SmartCardConnection:
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
// The SmartCardProtocol offers a the ability of sending APDU-based smartcard commands
SmartCardProtocol protocol = new SmartCardProtocol(connection);
byte[] aid = new byte[] {0xA0, 0x00, 0x00, 0x03, 0x08};
protocol.select(aid); // Select a smartcard application (this may throw an ApplicationNotAvailableException)
protocol.sendAndReceive(new Apdu(0x00, 0xA4, 0x00, 0x00)));
} catch(ApplicationNotAvailableException | IOException e) {
// Handle errors
}
});
For more control of the connection instance, YubiKit 2.3.0 adds YubiKeyConnection YubiKeyDevice.openConnection(type)
API. The caller of this method is responsible for closing the connection and for making sure that the object is accessed in a thread-safe way. Because YubiKeyConnection
derives from Closable
, it is possible to use try-with-resources Java statement to automatically close the connection. Example:
try {
try (SmartCardConnection connection = device.openConnection(SmartCardConnection.class)) {
SmartCardProtocol protocol = new SmartCardProtocol(connection);
byte[] aid = new byte[]{(byte) 0xA0, 0x00, 0x00, 0x03, 0x08};
protocol.select(aid);
protocol.sendAndReceive(new Apdu(0x00, 0xA4, 0x00, 0x00, null));
}
} catch(ApplicationNotAvailableException | IOException e) {
// Handle errors
}
The module provides some re-usable components which can be useful for implementing common functionality.
An Android Activity dialog which prompts the user to connect their YubiKey, and
performs some action with it. To use, implement a YubiKeyPromptAction
, and
specify it in an Intent to start the YubiKeyPromptActivity
. The action
returns a result which the Activity will pass back to the caller. Arguments to
the action can be passed as extras to the Activity. The
YubiKeyPromptConnectionAction
class can be used when a specific type of
connection is required:
//MyAction.java
public class MyAction extends YubiKeyPromptConnectionAction<SmartCardConnection>(SmartCardConnection.class) {
@Override
void onYubiKeyConnection(SmartCardConnection connection, Bundle extras, CommandState commandState, Callback<Pair<Integer, Intent>> callback) {
// Read out a certificate using the PIV module:
PivSession session = new PivSession(connection);
X509Certificate certificate = session.getCertificate(Slot.AUTHENTICATION);
Intent result = new Intent();
result.putExtra("EXTRA_CERTIFICATE", certificate.getEncoded());
callback.invoke(new Pair<>(Activity.RESULT_OK, result));
}
}
A specialized YubiKey Prompt Activity used to read out an OTP over the keyboard interface (or from the NFC NDEF payload). It does not require a separate Action.
startActivityForResult(new Intent(context, OtpActivity.class), requestCode);
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == Activity.RESULT_OK) {
String otp = data.getStringExtra(OtpActvity.EXTRA_OTP);
}
}
Client applications can override colors and string resources of the YubiKey Prompt and OTP activities.
For example, to change a background color of the yubikit prompt dialogs, add a color resource to your application:
<resources>
<color name="yubikit_dialog_background">#DEBF90</color>
</resources>
We don’t recommend overriding resources which are not marked as public
.