#c# #yubico
#c# #yubico
Вопрос:
Мне нужен какой-то способ аутентификации пользователя или пары ключей из YubiKey на компьютере с воздушным зазором (без подключения к Интернету). Предпочтительно из приложения на C # / .NET.
По сути, мне нужно убедиться, что вставленный YubiKey предоставляет пользователю надлежащую авторизацию для использования моего приложения.
Моей первой идеей было сгенерировать пару ключей RSA, сохранить закрытый ключ на YubiKey и открытый ключ в моем приложении. Затем я бы проверил пару ключей с помощью gpg. Однако этот подход не работает:
C:Program Файлы (x86) GnuPGbin>gpg —состояние карты
gpg: ошибка выбора openpgp: такого устройства нет
gpg: карта OpenPGP недоступна: такого устройства нет
Затем я использовал YubiKey manager для генерации пары ключей и сертификата и сохранения их на устройстве. Я могу видеть сертификат с помощью команды Windows CertUtil, но я не знаю, какие аргументы передавать в CertUtil -verifykeys [keyContainerName CACertFile], поскольку я не знаю имя контейнера.
Ответ №1:
После некоторых поисков я нашел это решение, основанное на (https://www.codeproject.com/Articles/240655/Using-a-Smart-Card-Certificate-with-NET-Security-i )
Шаг 1. используйте ykman для настройки пары закрытых / открытых ключей
ykman piv generate-key -a RSA2048 -F PEM —touch-policy НИКОГДА НЕ 9e «c:devLicenseykeysmy_key.pub «
Эта команда создает пару открытых / закрытых ключей RSA. Закрытый ключ сохраняется на устройстве в слоте 9e, а открытый ключ сохраняется в файле «my_key.pub»
Шаг 2. используйте ykman для создания самозаверяющего сертификата
ykman piv generate-certificate -s «my_key_test» -d 365 9e «c:devLicenseykeysmy_key.pub «
Эта команда создает самозаверяющий сертификат X.509 и сохраняет его на одном устройстве.
Шаг 3: экспорт сертификата
ykman piv export-certificate -F PEM 9e «c:devLicenseykeysmy_key_crt.pem «
Эта команда создает копию сертификата с шага 2 в my_key_crt.pem
Шаг 4. используйте программу на C # для проверки пары открытых / закрытых ключей
using System;
using System.IO;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace TestCSPSmartCard
{
class Program
{
static unsafe void Main(string[] args)
{
//PKI provider name comes from system registry or the output
//of "certutil -scinfo" command
//The container name comes from the output of "certutil -scinfo" command
const string
pkiProvider = "Microsoft Base Smart Card Crypto Provider",
container = "b51a653f-f451-c1d4-0841-5ace955fc101";
try
{
//'123456' is the default
SecureString smartCardPin;
char[] scPwd = { '1', '2', '3', '4', '5', '6' };
fixed(char* pChars = scPwd)
{
smartCardPin = new SecureString(pChars, scPwd.Length);
}
//Construct CspParameters object.
//Omitting last two arguments will cause Windows to display a dialog
//prompting user for the SmartCard PIN.
CspParameters csp =
new CspParameters(1,
pkiProvider,
container,
new System.Security.AccessControl.CryptoKeySecurity(),
smartCardPin);
byte[] toSign = new byte[20];
Random rnd = new Random((int)DateTime.Now.Ticks);
rnd.NextBytes(toSign);
Console.WriteLine("Data to sign : " BitConverter.ToString(toSign));
RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(csp);
RSAPKCS1SignatureFormatter rsaSign = new RSAPKCS1SignatureFormatter(rsaCsp);
rsaSign.SetHashAlgorithm("SHA1");
byte[] signature = rsaSign.CreateSignature(toSign);
Console.WriteLine();
Console.WriteLine("Signature: " BitConverter.ToString(signature));
RSACryptoServiceProvider rsaCsp2 = FromPublicKey(args.FirstOrDefault());
RSAPKCS1SignatureDeformatter rsaVerify = new RSAPKCS1SignatureDeformatter(rsaCsp2);
rsaVerify.SetHashAlgorithm("SHA1");
bool verified = rsaVerify.VerifySignature(toSign, signature);
Console.WriteLine();
Console.WriteLine("Signature verified [{0}]", verified);
}
catch (Exception ex)
{
Console.WriteLine("Crypto error: " ex.Message);
}
Console.WriteLine("done!");
}
private static RSACryptoServiceProvider FromPublicKey(string keyFile = null)
{
//Generated from PEM public key file using https://superdry.apphb.com/tools/online-rsa-key-converter
const string xmlPubKey =
@"<RSAKeyValue><Modulus>2mdYz5yV59K0PMO6HCxBA7gVWtbmNY dwYOc14H5DTD7zQ64CHpxAQOAexFx5uQKaxIR8UjZOikOwO NWMvQ4/DCIHu3WwK2/M07JQ3LYeeJ8L28RSfb9S7CCMvJ7sDOmVMB4otfQwqYvMri9QWYVe/9jWIyp3LezAUyFTGnA2OeMiVaZa2gsI5 v4HkuY3ZD9KIdUgp3Wt0SFTe1jRKAaqJhp8f3Lh0CRaYoukeq0XAhhh9k55o7wLCp0XZgSZzOPeuNL at20Tx9BRcb/9odlmFoHn/0P0X57a1yKhKRGUIri3gfu2BJ2BnXOUy iSk1VNWRixuMsxee059Gg7Uw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
if (keyFile != null)
{
FileInfo cerFile = new FileInfo(keyFile);
if (cerFile.Exists)
{
X509Certificate2 cert = new X509Certificate2();
Console.WriteLine($"Importing public key from {cerFile.FullName}");
cert.Import(cerFile.FullName);
return (RSACryptoServiceProvider)cert.PublicKey.Key;
}
}
RSACryptoServiceProvider result = new RSACryptoServiceProvider();
result.FromXmlString(xmlPubKey);
return resu<
}
}
}
Ответ №2:
U2F и Webauthn могут использоваться для аутентификации токена полностью локально. У Yubico есть Java-файл на GitHub, и, хотя двоичная загрузка отсутствует, вы можете легко скомпилировать его, следуя описанию на странице. Сервер поставляется с демонстрационным сервером, который позволяет выполнять первые тесты, особенно когда речь идет о тестировании вашей реализации на стороне клиента. Для этого также существуют библиотеки, предоставляемые Yubico, которые вы также можете найти на Yubico. Хорошая особенность U2F и Webauthn в том, что оба они поддерживаются многими современными браузерами «из коробки», поэтому, выполнив некоторую магию Javascript (особенно navigator.credentials.get(...)
для запуска процесса аутентификации токена), вы можете заставить все работать. Демонстрационный сервер поставляется с HTML-страницей, которая содержит все необходимое для выполнения U2F или Webauthn.