[英]Is there a way to authenticate YubiKey from stand-alone C# application without internet connection?
我需要某种方法来从一台空白PC上的YubiKey验证用户或密钥对(无Internet连接)。 最好来自C#/。NET应用程序。
本质上,我需要验证插入的YubiKey是否为用户提供了使用我的应用程序的适当授权。
我的第一个想法是生成一个RSA密钥对,将私有密钥存储在YubiKey上,并将公共密钥存储在我的应用程序中。 然后,我将使用gpg验证密钥对。 但是,这种方法不起作用:
C:\\ Program Files(x86)\\ GnuPG \\ bin> gpg --card-status
gpg:选择openpgp失败:没有这样的设备
gpg:OpenPGP卡不可用:没有这样的设备
然后,我使用YubiKey管理器生成密钥对和证书,并将其存储在设备上。 我可以通过Windows CertUtil命令查看证书,但是我不知道要传递给CertUtil -verifykeys [KeyContainerName CACertFile]的参数,因为我不知道容器名称。
经过一番搜索后,我发现了基于( 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 NEVER 9e“ c:\\ dev \\ License \\ ykeys \\ my_key.pub”
此命令创建公用/专用RSA密钥对。 私钥保存在设备上的9e插槽中,而公钥保存到“ my_key.pub”文件中
步骤2:使用ykman创建自签名证书
ykman piv生成证书-s“ my_key_test” -d 365 9e“ c:\\ dev \\ License \\ ykeys \\ my_key.pub”
此命令创建自签名X.509证书并将其保存到一台设备。
步骤3:出口证书
ykman piv出口证书-F PEM 9e“ c:\\ dev \\ License \\ ykeys \\ my_key_crt.pem”
此命令在my_key_crt.pem中从步骤2创建证书的副本。
步骤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 result;
}
}
}
U2F和Webauthn可用于在本地完全认证令牌。 Yubico在GitHub上有一个基于Java的Java,虽然没有二进制下载,但是您可以按照页面上的描述轻松地进行编译。 该服务器带有一个演示服务器 ,使您可以进行首次测试,尤其是在测试客户端实现时。 为此,您还可以在Yubico上找到Yubico提供的库 。 关于U2F和Webauthn的好处是,许多现代浏览器都提供了开箱即用的支持,因此通过做一些Javascript魔术(本质上是navigator.credentials.get(...)
以启动令牌认证过程),您可以获得工作正常。 演示服务器附带一个HTML页面,其中包含执行U2F或Webauthn所需的所有内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.