web-dev-qa-db-fra.com

WebApi HttpClient n'envoie pas de certificat client

J'essaie de sécuriser mon service RESTful WebApi avec SSL et l'authentification client à l'aide de certificats client.

Tester; J'ai généré un certificat auto-signé et placé dans la machine locale, le dossier des autorités de certification racine de confiance et j'ai généré des certificats "serveur" et "client". Https standard vers le serveur fonctionne sans problème.

Cependant, j'ai du code sur le serveur pour valider le certificat, il n'est jamais appelé lorsque je me connecte à l'aide de mon client de test qui fournit mon certificat client et le client de test reçoit un statut interdit 403.

Cela implique que le serveur échoue mon certificat avant qu'il n'atteigne mon code de validation. Cependant, si je lance un violon, il sait qu'un certificat client est requis et me demande d'en fournir un à My Documents\Fiddler2. Je lui ai donné le même certificat client que j'utilise dans mon client de test et mon serveur fonctionne maintenant et a reçu le certificat client que j'attends! Cela implique que le client WebApi n'envoie pas correctement le certificat, mon code client ci-dessous est à peu près le même que les autres exemples que j'ai trouvés.

    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);
        }
    }

Des idées pourquoi cela fonctionnerait chez un violoneux mais pas mon client de test?

Edit: Voici le code de 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;
    }

Certes, le code de test ne gère pas un certificat nul, mais je débogue pour m'assurer que le bon certificat est localisé.

25
Tronneh

Il existe 2 types de certificats. Le premier est le fichier .cer public qui vous est envoyé par le propriétaire du serveur. Ce fichier n'est qu'une longue chaîne de caractères. Le second est le certificat de magasin de clés, c'est le certificat auto-signé que vous créez et envoyez le fichier cer au serveur que vous appelez et ils l'installent. Selon le niveau de sécurité dont vous disposez, vous devrez peut-être en ajouter un ou les deux au client (gestionnaire dans votre cas). Je n'ai vu que le certificat de magasin de clés utilisé sur un serveur où la sécurité est TRÈS sécurisée. Ce code obtient les deux certificats du dossier bin/déployé:

#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

En outre - vous devez gérer les erreurs de certificat (remarque: c'est MAUVAIS - il indique que TOUS les problèmes de certificat sont corrects), vous devez modifier ce code pour gérer des problèmes de certificat spécifiques tels que la non-correspondance de noms. c'est sur ma liste à faire. Il existe de nombreux exemples sur la façon de procéder.

Ce code en haut de votre méthode

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

Méthode quelque part dans votre classe

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

Votre code ci-dessus s'exécute-t-il dans le même compte d'utilisateur que Fiddler? Sinon, il peut avoir accès au fichier de certificat mais pas à la bonne clé privée. De même, que renvoie votre fonction GetClientCert()? Plus précisément, a-t-il le jeu de propriétés PrivateKey?

1
EricLaw