Yubico / Yubico.NET.SDK

A YubiKey SDK for .NET developers
Apache License 2.0
96 stars 48 forks source link

How to generate a self signed cert like we can in Ykman? #7

Closed DSBloom closed 2 years ago

DSBloom commented 2 years ago

With ykman we can do this ykman.exe piv certificates generate.

We currently use ykman to generate a self signed cert for slot 9D.

I don't see a high level command to do the same with the SDK. Do we have to generate our own self signed cert and import using ImportCertificate from PivSession?

gnida-rada commented 2 years ago

@nadcraker , does sample code https://github.com/Yubico/Yubico.NET.SDK/blob/831161f903e9e1ece60cefdf6627bdcc96a579c1/Yubico.YubiKey/examples/PivSampleCode/CertificateOperations/SampleCertificateOperations.cs help help? It has GetSelfSignedCert. To avoid any user prompts, you could use your own key collector delegate with PIN/PUK coming from the caller of your wrapper (you are probably already doing it for other commands).

DSBloom commented 2 years ago

This looks very promising. Thank you @gnida-rada!

gnida-rada commented 2 years ago

@nadcraker , if you want it, I can share code for customizable promptless key collector (basically, you init it with pin, puk or both and then supply those values in delegate callback.

DSBloom commented 2 years ago

Yes please post it. I have my own key collector but I would love to see what you have. Maybe I can learn something from it.

gnida-rada commented 2 years ago

using System; using Yubico.YubiKey;

public class YubikeyPivKeyCollector
{
    private static readonly byte[] DefaultPin = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 };
    private static readonly byte[] DefaultMgmtKey = {
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };

    //      or "0102030405060708" three times
    public const string DefaultMgmtKeyString = "010203040506070801020304050607080102030405060708";

    public const string DefaultPukString = "12345678";

    private byte[] _pin;
    private byte[] _newPin;
    private byte[] _puk;
    private byte[] _newPuk;
    private byte[] _mgmtKey;
    private byte[] _newMgmtKey;

    private YubikeyPivKeyCollector(byte[] pin, byte[] mgmtKey)
    {
        _pin = pin;
        _mgmtKey = mgmtKey;
    }

    public YubikeyPivKeyCollector()
    {
    }

    private static readonly Lazy<YubikeyPivKeyCollector> lazyFactoryPresetKeyCollector =
        new Lazy<YubikeyPivKeyCollector>(() => new YubikeyPivKeyCollector(
            DefaultPin, DefaultMgmtKey));
    public static YubikeyPivKeyCollector FactoryPresetKeyCollector
    {
        get
        {
            return lazyFactoryPresetKeyCollector.Value;
        }
    }

    public void SetPinString(string pinString)
    {
        if (pinString == null)
        {
            throw new ArgumentNullException(nameof(pinString));
        }

        _pin = YubikeyPivUtils.ConvertPinStringToByteArray(pinString);
    }

    public void SetNewPinString(string newPinString)
    {
        if (newPinString == null)
        {
            throw new ArgumentNullException(nameof(newPinString));
        }

        _newPin = YubikeyPivUtils.ConvertPinStringToByteArray(newPinString);
    }

    public void SetPukString(string inString)
    {
        if (inString == null)
        {
            throw new ArgumentNullException(nameof(inString));
        }

        _puk = YubikeyPivUtils.ConvertPinStringToByteArray(inString);
    }

    public void SetNewPukString(string inString)
    {
        if (inString == null)
        {
            throw new ArgumentNullException(nameof(inString));
        }

        _newPuk = YubikeyPivUtils.ConvertPinStringToByteArray(inString);
    }

    public void SetMgmtKeyString(string mgmtKeyString)
    {
        if (mgmtKeyString == null)
        {
            throw new ArgumentNullException(nameof(mgmtKeyString));
        }

        _mgmtKey = YubikeyPivUtils.ConvertMgmtKeyStringToByteArray(mgmtKeyString);
    }

    public void SetNewMgmtKeyString(string mewMgmtKeyString)
    {
        if (mewMgmtKeyString == null)
        {
            throw new ArgumentNullException(nameof(mewMgmtKeyString));
        }

        _newMgmtKey = YubikeyPivUtils.ConvertMgmtKeyStringToByteArray(mewMgmtKeyString);
    }

    // This is the callback. When the SDK needs a PIN, PUK, or management
    // key, this is the method that will be called.
    public bool KeyCollectorDelegate(KeyEntryData keyEntryData)
    {
        if (keyEntryData is null)
        {
            return false;
        }

        if (keyEntryData.IsRetry == true)
        {
            // this is automatic key collector. If we don't know the values
            // we are not promting the user for retry, just bail.
            return false;
        }

        byte[] currentValue;
        byte[] newValue = null;

        switch (keyEntryData.Request)
        {
            default:
                throw new NotImplementedException($"Unsupported keyEntryData.Request {keyEntryData.Request}!");

            case KeyEntryRequest.Release:
                return true;

            case KeyEntryRequest.VerifyPivPin:
                currentValue = _pin;
                break;

            case KeyEntryRequest.ChangePivPin:
                if ((_pin == null) || (_newPin == null))
                {
                    return false;
                }
                currentValue = _pin;
                newValue = _newPin;
                break;

            case KeyEntryRequest.ChangePivPuk:
                if ((_puk == null) || (_newPuk == null))
                {
                    return false;
                }
                currentValue = _puk;
                newValue = _newPuk;
                break;

            case KeyEntryRequest.AuthenticatePivManagementKey:
                if (keyEntryData.IsRetry == true)
                {
                    return false;
                }
                currentValue = _mgmtKey;
                break;

            case KeyEntryRequest.ChangePivManagementKey:
                if (keyEntryData.IsRetry == true)
                {
                    return false;
                }
                if ((_mgmtKey == null) || (_newMgmtKey == null))
                {
                    return false;
                }
                currentValue = _mgmtKey;
                newValue = _newMgmtKey;
                break;
        }

        if (newValue is null)
        {
            keyEntryData.SubmitValue(currentValue);
        }
        else
        {
            keyEntryData.SubmitValues(currentValue, newValue);
        }

        return true;
    }

}
DSBloom commented 2 years ago

Awesome, thanks! Where can I find the YubikeyPivUtils class?

gnida-rada commented 2 years ago
using System;

public static class YubikeyPivUtils
{

    // for MgmtKeyString, we convert a hex string to a byte array.
    // so "010203" becomes { 0x01, 0x02, 0x03 }
    public static byte[] ConvertMgmtKeyStringToByteArray(string mgmtKeyString)
    {
        if (mgmtKeyString == null)
        {
            throw new ArgumentNullException(nameof(mgmtKeyString));
        }

        char[] valueChars = mgmtKeyString.ToCharArray();
        byte[] keyArray = new byte[24];
        int inIndex = 0;
        int outIndex = 0;
        while (inIndex < valueChars.Length)
        {
            byte firstDigit = (byte)Convert.ToInt32(valueChars[inIndex].ToString(), 16);
            byte secondDigit = (byte)Convert.ToInt32(valueChars[inIndex + 1].ToString(), 16);
            keyArray[outIndex] = (byte)((firstDigit * 16) + secondDigit);
            inIndex += 2;

            outIndex++;
            if (outIndex >= 24)
            {
                break;
            }
        }

        return keyArray;
    }

    // for PIN string, convert each character to its associated ASCII byte.
    public static byte[] ConvertPinStringToByteArray(string pinString)
    {
        if (pinString == null)
        {
            throw new ArgumentNullException(nameof(pinString));
        }

        char[] valueChars = pinString.ToCharArray();
        byte[] pinArray = new byte[valueChars.Length];
        for (int index = 0; index < valueChars.Length; index++)
        {
            pinArray[index] = (byte)valueChars[index];
        }

        return pinArray;
    }
}