簡體   English   中英

有沒有辦法在沒有Internet連接的情況下從獨立的C#應用​​程序驗證YubiKey?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM