簡體   English   中英

WebApi HttpClient 不發送客戶端證書

[英]WebApi HttpClient not sending client certificate

我正在嘗試使用客戶端證書通過 ssl 和客戶端身份驗證來保護我的 RESTful WebApi 服務。

去測試; 我生成了一個自簽名證書並放置在本地機器、受信任的根證書頒發機構文件夾中,並且我生成了一個“服務器”和“客戶端”證書。 到服務器的標准 https 工作沒有問題。

但是,我在服務器中有一些代碼來驗證證書,當我使用提供我的客戶端證書的測試客戶端進行連接時,它永遠不會被調用,並且測試客戶端返回 403 Forbidden 狀態。

這意味着服務器在到達我的驗證代碼之前未能通過我的證書。 但是,如果我啟動 fiddler,它知道需要客戶端證書並要求我向 My Documents\\Fiddler2 提供一個證書。 我給了它與我在測試客戶端中使用的相同的客戶端證書,我的服務器現在可以工作並收到了我期望的客戶端證書! 這意味着 WebApi 客戶端沒有正確發送證書,我下面的客戶端代碼與我發現的其他示例幾乎相同。

    static async Task RunAsync()
    {
        try
        {
            var handler = new WebRequestHandler();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.ClientCertificates.Add(GetClientCert());
            handler.ServerCertificateValidationCallback += Validate;
            handler.UseProxy = false;

            using (var client = new HttpClient(handler))
            {
                client.BaseAddress = new Uri("https://hostname:10001/");

                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

                var response = await client.GetAsync("api/system/");
                var str = await response.Content.ReadAsStringAsync();

                Console.WriteLine(str);
            }
        } catch(Exception ex)
        {
            Console.Write(ex.Message);
        }
    }

任何想法為什么它可以在 fiddler 中工作而不是我的測試客戶端?

編輯:這是 GetClientCert() 的代碼

private static X509Certificate GetClientCert()
    {            
        X509Store store = null;
        try
        {
            store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);

            var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "Integration Client Certificate", true);

            if (certs.Count == 1)
            {
                var cert = certs[0];
                return cert;
            }
        }
        finally
        {
            if (store != null) 
                store.Close();
        }

        return null;
    }

授予測試代碼不處理空證書,但我正在調試以確保找到正確的證書。

有兩種類型的證書。 第一個是從服務器所有者發送給您的公共 .cer 文件。 這個文件只是一長串字符。 第二個是密鑰庫證書,這是您創建的自簽名證書並將 cer 文件發送到您正在調用的服務器並安裝它。 根據您擁有多少安全性,您可能需要將其中一個或兩個添加到客戶端(在您的情況下為 Handler)。 我只看到在安全性非常安全的一台服務器上使用的密鑰庫證書。 此代碼從 bin/deployed 文件夾中獲取兩個證書:

#region certificate Add
                // KeyStore is our self signed cert
                // TrustStore is cer file sent to you.

                // Get the path where the cert files are stored (this should handle running in debug mode in Visual Studio and deployed code) -- Not tested with deployed code
                string executableLocation = Path.GetDirectoryName(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory);

                #region Add the TrustStore certificate

                // Get the cer file location
                string pfxLocation = executableLocation + "\\Certificates\\TheirCertificate.cer";

                // Add the certificate
                X509Certificate2 theirCert = new X509Certificate2();
                theirCert.Import(pfxLocation, "Password", X509KeyStorageFlags.DefaultKeySet);
                handler.ClientCertificates.Add(theirCert);
                #endregion

                #region Add the KeyStore 
                // Get the location
                pfxLocation = executableLocation + "\\Certificates\\YourCert.pfx";

                // Add the Certificate
                X509Certificate2 YourCert = new X509Certificate2();
                YourCert.Import(pfxLocation, "PASSWORD", X509KeyStorageFlags.DefaultKeySet);
                handler.ClientCertificates.Add(YourCert);
                #endregion

                #endregion

此外 - 您需要處理證書錯誤(注意:這很糟糕 - 它說所有證書問題都可以)您應該更改此代碼以處理特定的證書問題,例如名稱不匹配。 這是我要做的事情。 有很多關於如何做到這一點的例子。

此代碼位於您的方法頂部

// Ignore Certificate errors  need to fix to only handle 
ServicePointManager.ServerCertificateValidationCallback = MyCertHandler;

類中某處的方法

private bool MyCertHandler(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors error)
    {
        // Ignore errors
        return true;
    }

上面的代碼是否在Fiddler運行的同一個用戶帳戶中運行? 如果沒有,它可能有權訪問證書文件,但不能訪問正確的私鑰。 同樣,你的GetClientCert()函數返回什么? 具體來說,它是否設置了PrivateKey屬性?

在您使用的代碼中store = new X509Store(StoreName.My, StoreLocation.LocalMachine); .

客戶端證書不是從LocalMachine ,您應該使用StoreLocation.CurrentUser

檢查MMC -> File -> Add or Remove Snap-ins -> Certificates -> My user account您將看到 fiddler 使用的證書。 如果您從My user account刪除它並且只在Computer account導入它,您將看到 Fiddler 也無法提取它。

旁注是在查找證書時,您還必須解決文化問題。

例子:

var certificateSerialNumber= "‎83 c6 62 0a 73 c7 b1 aa 41 06 a3 ce 62 83 ae 25".ToUpper().Replace(" ", string.Empty);

//0 certs
var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, certificateSerialNumber, true);

//null
var cert = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => x.GetSerialNumberString() == certificateSerialNumber);

//1 cert
var cert1 = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x =>
                x.GetSerialNumberString().Equals(certificateSerialNumber, StringComparison.InvariantCultureIgnoreCase));
 try this.

證書應與當前用戶存儲。 或者授予完全權限並從文件中讀取,因為它是一個控制台應用程序。

// Load the client certificate from a file.
X509Certificate x509 = X509Certificate.CreateFromCertFile(@"c:\user.cer");

從用戶存儲中讀取。

 private static X509Certificate2 GetClientCertificate()
    {
        X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        try
        {
            userCaStore.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certificatesInStore = userCaStore.Certificates;
            X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, "localtestclientcert", true);
            X509Certificate2 clientCertificate = null;
            if (findResult.Count == 1)
            {
                clientCertificate = findResult[0];
            }
            else
            {
                throw new Exception("Unable to locate the correct client certificate.");
            }
            return clientCertificate;
        }
        catch
        {
            throw;
        }
        finally
        {
            userCaStore.Close();
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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