YubiKey, developed by Yubico, is a versatile hardware authentication device that provides secure and convenient means for authentication, encryption, and digital signing. Integrating YubiKey into C# applications enhances security by leveraging its hardware-based cryptographic operations. This guide offers a step-by-step approach to integrating YubiKey with C#, covering installation, device detection, authentication, data signing, and advanced features.
The Yubico .NET SDK is essential for interacting with YubiKey devices in C#. It provides a comprehensive set of tools and libraries to manage YubiKey functionalities.
Install the SDK using the NuGet package manager with the following command:
dotnet add package Yubico.YubiKey
Alternatively, in Visual Studio, you can install the package via the Package Manager Console:
Install-Package Yubico.YubiKey
Before performing any operations, your application needs to detect connected YubiKey devices. The Yubico .NET SDK simplifies this process.
The following example demonstrates how to enumerate all connected YubiKey devices and retrieve their basic information:
using System;
using System.Collections.Generic;
using Yubico.YubiKey;
class YubiKeyExample
{
static void Main(string[] args)
{
// Enumerate all YubiKeys connected to the system
IEnumerable<IYubiKeyDevice> devices = YubiKeyDevice.FindAll();
// Check if any YubiKey devices are found
if (devices != null && devices.Any())
{
Console.WriteLine("YubiKey devices found:");
foreach (var device in devices)
{
Console.WriteLine($"Serial Number: {device.SerialNumber}");
Console.WriteLine($"Firmware Version: {device.FirmwareVersion}");
Console.WriteLine($"Form Factor: {device.FormFactor}");
Console.WriteLine();
}
}
else
{
Console.WriteLine("No YubiKey devices found.");
}
}
}
This code searches for all connected YubiKey devices and prints out their serial numbers, firmware versions, and form factors. If no devices are found, it notifies the user accordingly.
Each YubiKey device provides specific properties that can be accessed using the SDK:
The Personal Identity Verification (PIV) module on YubiKey enables secure authentication by handling PIN verification and cryptographic operations. Integrating PIV with C# applications allows for robust security features.
The following example demonstrates authenticating with the YubiKey and signing data using the PIV module:
using System;
using System.Linq;
using Yubico.YubiKey;
using Yubico.YubiKey.Piv;
class YubiKeySigner
{
static void Main(string[] args)
{
// Step 1: Find the first connected YubiKey
var yubiKey = YubiKeyDevice.FindAll().FirstOrDefault();
if (yubiKey == null)
{
Console.WriteLine("No YubiKey found.");
return;
}
// Step 2: Initialize a PIV session
using (var pivSession = new PivSession(yubiKey))
{
// Step 3: Verify the PIN (default PIN is "123456")
bool isPinVerified = pivSession.TryVerifyPin("123456");
if (!isPinVerified)
{
Console.WriteLine("PIN verification failed.");
return;
}
// Step 4: Sign data using the Authentication slot
byte[] dataToSign = new byte[] { 0x01, 0x02, 0x03, 0x04 };
byte[] signature = pivSession.Sign(PivSlot.Authentication, dataToSign);
Console.WriteLine("Data signed successfully.");
Console.WriteLine("Signature: " + BitConverter.ToString(signature).Replace("-", ""));
}
}
}
Explanation of the Code:
The YubiKey's PIV module can store multiple certificates and keys, enabling comprehensive management within your C# applications. This section covers generating key pairs, retrieving public keys, and handling certificates.
Generating key pairs on the YubiKey ensures that private keys never leave the device, enhancing security. Here's how to generate an RSA 2048-bit key pair:
using System;
using Yubico.YubiKey;
using Yubico.YubiKey.Piv;
class KeyPairGenerator
{
static void Main(string[] args)
{
var yubiKey = YubiKeyDevice.FindAll().FirstOrDefault();
if (yubiKey == null)
{
Console.WriteLine("No YubiKey found.");
return;
}
using (var pivSession = new PivSession(yubiKey))
{
// Generate RSA 2048 key pair in Authentication slot
pivSession.GenerateKeyPair(
PivSlot.Authentication, // Slot number
PivAlgorithm.Rsa2048, // Algorithm
PivPinPolicy.Once, // PIN policy
PivTouchPolicy.Never // Touch policy
);
Console.WriteLine("RSA 2048 key pair generated in Authentication slot.");
}
}
}
Key Points:
Accessing the public key stored on the YubiKey is essential for verifying signatures or encrypting data.
using System;
using Yubico.YubiKey.Piv;
class PublicKeyRetriever
{
static void Main(string[] args)
{
var yubiKey = YubiKeyDevice.FindAll().FirstOrDefault();
if (yubiKey == null)
{
Console.WriteLine("No YubiKey found.");
return;
}
using (var pivSession = new PivSession(yubiKey))
{
// Retrieve the public key from Authentication slot
PivPublicKey publicKey = pivSession.GetPublicKey(PivSlot.Authentication);
Console.WriteLine("Public Key Retrieved:");
Console.WriteLine($"Algorithm: {publicKey.Algorithm}");
if (publicKey.Algorithm.IsRsa())
{
Console.WriteLine($"Modulus: {BitConverter.ToString(publicKey.RsaModulus).Replace("-", "")}");
Console.WriteLine($"Exponent: {BitConverter.ToString(publicKey.RsaExponent).Replace("-", "")}");
}
else if (publicKey.Algorithm.IsEcc())
{
Console.WriteLine($"Public Point: {BitConverter.ToString(publicKey.EccPoint).Replace("-", "")}");
}
}
}
}
Explanation: This code retrieves the public key from the Authentication slot and prints its details, including the algorithm, modulus, and exponent for RSA keys, or the public point for ECC keys.
Certificates can be loaded into or retrieved from the YubiKey's PIV slots. Managing certificates ensures that your application can perform tasks like certificate-based authentication.
using System;
using System.IO;
using Yubico.YubiKey.Piv;
class CertificateManager
{
static void Main(string[] args)
{
var yubiKey = YubiKeyDevice.FindAll().FirstOrDefault();
if (yubiKey == null)
{
Console.WriteLine("No YubiKey found.");
return;
}
using (var pivSession = new PivSession(yubiKey))
{
// Load a certificate from a file
byte[] certificateBytes = File.ReadAllBytes("path_to_certificate.cer");
// Store the certificate in the Authentication slot
pivSession.ImportCertificate(PivSlot.Authentication, certificateBytes);
Console.WriteLine("Certificate imported to Authentication slot.");
}
}
}
Key Points:
ImportCertificate
method to load a certificate into a specific PIV slot.In environments with multiple YubiKey devices, it's crucial to manage and identify each device effectively. You can extend the device enumeration example to handle multiple devices as needed.
Robust error handling ensures that your application can gracefully manage issues such as device disconnection, incorrect PIN entries, or failed cryptographic operations.
try
{
// YubiKey operations
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Never hard-code PINs or sensitive information in your source code. Implement secure methods for PIN input, such as prompting the user or using secure storage mechanisms.
Beyond PIV, YubiKey supports FIDO2 for passwordless authentication. Integrating FIDO2 can enhance security by reducing reliance on traditional passwords.
Enhancing security by requiring physical touch on the YubiKey for certain operations can prevent unauthorized use. Configure touch policies based on your security requirements.
To illustrate the integration of YubiKey with C#, let's develop a sample application that authenticates a user and signs data securely.
Below is the complete code for a console application that authenticates with the YubiKey and signs a sample data array:
using System;
using System.Linq;
using Yubico.YubiKey;
using Yubico.YubiKey.Piv;
namespace YubiKeySignExample
{
class Program
{
static void Main(string[] args)
{
try
{
// Step 1: Detect YubiKey
var yubiKey = YubiKeyDevice.FindAll().FirstOrDefault();
if (yubiKey == null)
{
Console.WriteLine("No YubiKey found.");
return;
}
// Step 2: Initialize PIV Session
using (var pivSession = new PivSession(yubiKey))
{
// Step 3: Verify PIN
Console.Write("Enter YubiKey PIN: ");
string pin = Console.ReadLine();
bool isPinVerified = pivSession.TryVerifyPin(pin);
if (!isPinVerified)
{
Console.WriteLine("PIN verification failed.");
return;
}
// Step 4: Sign Data
byte[] dataToSign = new byte[] { 0x10, 0x20, 0x30, 0x40 };
byte[] signature = pivSession.Sign(PivSlot.Authentication, dataToSign);
Console.WriteLine("Data signed successfully.");
Console.WriteLine("Signature: " + BitConverter.ToString(signature).Replace("-", ""));
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception occurred: {ex.Message}");
}
}
}
}
Explanation:
Run the application. When prompted, enter your YubiKey PIN. The application will then display the signed data.
dotnet run
Enter YubiKey PIN: <b></b>**
Data signed successfully.
Signature: AB-CD-EF-...
This simple application demonstrates the fundamental steps required to integrate YubiKey with C# for secure data signing.
Adhering to best practices ensures that your integration is secure, efficient, and maintainable.
Always handle PINs securely. Avoid logging PINs or exposing them in error messages. Consider using secure input methods to capture PINs.
Implement comprehensive error handling to manage exceptions, such as device disconnection, invalid PINs, or failed cryptographic operations.
Manage multiple YubiKey devices effectively, especially in environments where many users or devices are involved. Implement device selection mechanisms if necessary.
Regularly update the Yubico .NET SDK to benefit from the latest features, security patches, and improvements.
Leverage official documentation and community resources to stay informed about best practices, updates, and advanced usage scenarios.
Integrating YubiKey with C# applications significantly enhances security by leveraging hardware-based authentication and cryptographic operations. By following the steps outlined in this guide, developers can implement robust authentication mechanisms, secure data signing, and effective device management within their applications. Adhering to best practices ensures that the integration remains secure, efficient, and scalable.
These resources provide additional examples, documentation, and community insights to further assist you in integrating YubiKey with your C# applications.