web-dev-qa-db-ja.com

WebApi HttpClientがクライアント証明書を送信しない

クライアント証明書を使用したsslおよびクライアント認証でRESTful WebApiサービスを保護しようとしています。

テストする;自己署名証明書を生成し、ローカルマシンの信頼されたルート証明機関フォルダーに配置し、「サーバー」および「クライアント」証明書を生成しました。サーバーへの標準のhttpsは問題なく機能します。

しかし、サーバーに証明書を検証するためのコードがいくつかあります。クライアント証明書を提供するテストクライアントを使用して接続すると、これが呼び出されず、テストクライアントに403 Forbiddenステータスが返されます。

これは、サーバーが検証コードに到達する前に証明書に失敗することを実装しています。ただし、フィドラーを起動すると、クライアント証明書が必要であることを認識し、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);
        }
    }

それがフィドラーでは機能するが、私のテストクライアントでは機能しない理由はありますか?

編集: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;
    }

テストコードがnull証明書を処理しないことを認めましたが、正しい証明書が配置されていることを確認するためにデバッグしています。

25
Tronneh

証明書には2つのタイプがあります。 1つ目は、サーバーの所有者から送信されるパブリック.cerファイルです。このファイルは、長い文字列です。 2つ目はキーストア証明書です。これは、作成した自己署名証明書であり、cerファイルを呼び出しているサーバーに送信して、インストールします。どれだけのセキュリティを確保しているかに応じて、これらの1つまたは両方をクライアント(この場合はハンドラー)に追加する必要があります。私は、セキュリティが非常に安全な1つのサーバーで使用されるキーストア証明書を見ただけです。このコードは、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;
    }
1
SprintFlunky

上記のコードは、Fiddlerが実行されているのと同じユーザーアカウントで実行されていますか?そうでない場合は、証明書ファイルにアクセスできますが、正しい秘密鍵はありません。同様に、GetClientCert()関数は何を返しますか?具体的には、PrivateKeyプロパティが設定されていますか?

1
EricLaw