Ithy Logo

Comprehensive Guide to Integrating YubiKey with C#

Enhance Your C# Applications with Secure YubiKey Integration

YubiKey with computer C# code

Key Takeaways

  • Seamless Integration: Utilize the Yubico .NET SDK to effortlessly incorporate YubiKey functionalities into your C# applications.
  • Enhanced Security: Implement robust authentication and data signing mechanisms using YubiKey's PIV module.
  • Comprehensive Management: Manage YubiKey devices, certificates, and cryptographic operations effectively within your development environment.

Introduction to YubiKey Integration in C#

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.


Getting Started with YubiKey in C#

1. Prerequisites

  • Development Environment: Ensure you have a development environment set up with Visual Studio or any preferred C# IDE.
  • YubiKey Device: Have a YubiKey device connected to your computer.
  • .NET SDK: Install the Yubico .NET SDK to facilitate interaction with the YubiKey.

2. Installing the Yubico .NET SDK

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

Detecting and Connecting to YubiKey Devices

Before performing any operations, your application needs to detect connected YubiKey devices. The Yubico .NET SDK simplifies this process.

Example: Enumerating Connected YubiKeys

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.

Understanding YubiKey Properties

Each YubiKey device provides specific properties that can be accessed using the SDK:

  • SerialNumber: Unique identifier for the YubiKey.
  • FirmwareVersion: Indicates the firmware version installed on the device.
  • FormFactor: Describes the physical form of the YubiKey, such as USB-A, USB-C, or NFC-enabled.

Authenticating with YubiKey Using the PIV Module

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.

Step-by-Step Guide to PIV Authentication

  1. Initialize the PIV Session: Establish a session with the YubiKey's PIV module.
  2. Verify the User PIN: Ensure that the user is authenticated by verifying the PIN.
  3. Perform Cryptographic Operations: Execute operations such as signing data.

Example: Authenticating and Signing Data

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:

  1. Finding the YubiKey: The application searches for all connected YubiKey devices and selects the first one.
  2. Initializing PIV Session: A PIV session is established with the selected YubiKey.
  3. PIN Verification: The default PIN ("123456") is used to authenticate the user. If verification fails, the process terminates.
  4. Signing Data: The specified data is signed using the YubiKey's PIV module, specifically the Authentication slot.

Managing YubiKey Certificates and Keys

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

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:

  • PivSlot.Authentication: Specifies the slot where the key pair is stored.
  • PivAlgorithm.Rsa2048: Indicates the RSA 2048-bit algorithm for key generation.
  • PivPinPolicy.Once: Allows the PIN to be used once.
  • PivTouchPolicy.Never: Disables touch policy for this operation.

Retrieving Public Keys

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.

Handling Certificates

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:

  • Use the ImportCertificate method to load a certificate into a specific PIV slot.
  • Ensure certificates are in the correct format (e.g., DER or PEM) before importing.

Advanced Features and Best Practices

1. Managing Multiple YubiKey Devices

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.

2. Handling Errors and Exceptions

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}");
}
    

3. Secure PIN Management

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.

4. Implementing FIDO2 Authentication

Beyond PIV, YubiKey supports FIDO2 for passwordless authentication. Integrating FIDO2 can enhance security by reducing reliance on traditional passwords.

5. Utilizing Touch Policies

Enhancing security by requiring physical touch on the YubiKey for certain operations can prevent unauthorized use. Configure touch policies based on your security requirements.


Sample Application: Secure Data Signing

To illustrate the integration of YubiKey with C#, let's develop a sample application that authenticates a user and signs data securely.

Project Setup

  • Create a new C# Console Application in your IDE.
  • Install the Yubico .NET SDK via NuGet.
  • Ensure a YubiKey device is connected to your system.

Implementing the Signing Functionality

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:

  1. The application begins by detecting a connected YubiKey device.
  2. It then establishes a PIV session with the device.
  3. The user is prompted to enter the YubiKey PIN, which is verified.
  4. Upon successful authentication, the application signs a predefined data array using the Authentication slot.
  5. The resulting signature is displayed in hexadecimal format.

Executing the Application

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.


Best Practices for YubiKey Integration

Adhering to best practices ensures that your integration is secure, efficient, and maintainable.

1. Secure Pin Handling

Always handle PINs securely. Avoid logging PINs or exposing them in error messages. Consider using secure input methods to capture PINs.

2. Error Handling

Implement comprehensive error handling to manage exceptions, such as device disconnection, invalid PINs, or failed cryptographic operations.

3. Device Management

Manage multiple YubiKey devices effectively, especially in environments where many users or devices are involved. Implement device selection mechanisms if necessary.

4. Keep SDK Updated

Regularly update the Yubico .NET SDK to benefit from the latest features, security patches, and improvements.

5. Documentation and References

Leverage official documentation and community resources to stay informed about best practices, updates, and advanced usage scenarios.


Conclusion

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.


References

These resources provide additional examples, documentation, and community insights to further assist you in integrating YubiKey with your C# applications.


Last updated January 24, 2025
Search Again