【问题标题】:Is there a way to authenticate YubiKey from stand-alone C# application without internet connection?有没有办法在没有互联网连接的情况下从独立的 C# 应用程序验证 YubiKey?
【发布时间】:2019-04-17 19:20:23
【问题描述】:

我需要某种方式来验证用户或密钥对,从 YubiKey 在气隙 PC(无互联网连接)上。最好来自 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] 的参数,因为我不知道容器名称.

【问题讨论】:

    标签: c# yubico


    【解决方案1】:

    经过一番搜索,我找到了基于 (https://www.codeproject.com/Articles/240655/Using-a-Smart-Card-Certificate-with-NET-Security-i) 的解决方案

    第一步:使用 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”文件中

    第二步:使用ykman创建自签名证书

    ykman piv generate-certificate -s "my_key_test" -d 365 9e "c:\dev\License\ykeys\my_key.pub"

    此命令创建自签名 X.509 证书并将其保存在一个设备上。

    第三步:出口证书

    ykman piv export-certificate -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;
        }
    }
    }
    

    【讨论】:

      【解决方案2】:

      U2F 和 Webauthn 可用于完全在本地验证令牌。 Yubico 有一个基于 GitHub 的 Java,虽然没有二进制下载,但您可以按照页面上的说明轻松编译它。服务器带有一个demo server,允许您进行第一次测试,尤其是在测试您的客户端实现时。为此,Yubico 还提供了一些库,您也可以在 Yubico 上找到它们。 U2F 和 Webauthn 的好处是它们都被许多现代浏览器开箱即用地支持,所以通过做一些 Javascript-magic(基本上是navigator.credentials.get(...) 来启动令牌认证过程)你可以让事情正常工作。演示服务器带有一个 HTML 页面,其中包含执行 U2F 或 Webauthn 所需的一切。

      【讨论】:

        猜你喜欢
        • 2018-07-08
        • 2016-01-17
        • 2015-05-24
        • 2017-11-05
        • 1970-01-01
        • 1970-01-01
        • 2017-07-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多