web-dev-qa-db-fra.com

La demande HTTPS échoue à l'aide de HttpClient

J'utilise le code suivant et j'obtiens l'exception HttpRequestException:

using (var handler = new HttpClientHandler())
{
    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    handler.SslProtocols = SslProtocols.Tls12;
    handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert.pfx"));

    // I also tried to add another certificates that was provided to https access 
    // by administrators of the site, but it still doesn't work.
    //handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert.crt"));
    //handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert_ca.crt"));

    using (var client = new HttpClient(handler))
    {
        var response = client.GetAsync("https://someurl.com/api.php?arg1=some&arg2=test").GetAwaiter().GetResult();
        // ^ HttpRequestException: An error occurred while sending the request.
    }
}

L'éxéption:

WinHttpException: A security error occurred
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
    System.Net.Http.WinHttpHandler+<StartRequest>d__105.MoveNext()

HttpRequestException: An error occurred while sending the request.
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
    System.Net.Http.HttpClient+<FinishSendAsync>d__58.MoveNext()
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    System.Runtime.CompilerServices.TaskAwaiter.GetResult()
    MyApp.Web.Controllers.HomeController.Test() in HomeController.cs
        var response = client.GetAsync("https://someurl.com/api.php?arg1=some&arg2=test").GetAwaiter().GetResult();
    lambda_method(Closure , object , Object[] )
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeActionMethodAsync>d__27.MoveNext()

J'ai également essayé d'exporter les mêmes certificats vers le magasin de certificats Windows et de les utiliser via Google Chrome et cela fonctionne bien (le navigateur m'a demandé de confirmer le certificat installé, puis a chargé la ressource).

Pourquoi ça ne marche pas dans mon code?

[~ # ~] mis à jour [~ # ~]

J'ai également essayé d'ajouter un rappel pour valider le certificat:

handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) =>
{
    // I set a breakpoint to this point but it is not catched.
    return true;
};

MISE À JOUR2

Le certificat est utilisé SHA-1. Neil Moss est mentionné dans les commentaires que le support des certificats SHA1 est retiré . Si c'est la vraie raison pour laquelle cela ne fonctionne pas, y a-t-il une solution?

[~ # ~] solution [~ # ~]

Merci Neil Moss pour la solution. Il a proposé d'utiliser le drapeau Tls pour le protocole SSL.

handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;

Mais cela exigeait également les éléments suivants:

handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;

Après avoir ajouté celui-ci, cela fonctionne bien.

22
hcp

Selon this SO post , vous devez activer TLS1.2 avec ServicePointManager.

System.Net.ServicePointManager.SecurityProtocol =
    SecurityProtocolType.Tls12 | 
    SecurityProtocolType.Tls11 | 
    SecurityProtocolType.Tls; // comparable to modern browsers

Il convient également de noter que la propriété documentation MSDN pour ServicePointManager.SecurityProtocols fait cette déclaration:

Le .NET Framework 4.6 inclut une nouvelle fonctionnalité de sécurité qui bloque les algorithmes de chiffrement et de hachage non sécurisés pour les connexions.

ce qui suggère qu'une certaine forme de bloc SHA1 pourrait être en place.

20
Neil Moss

C'était un document très utile. Pour ASP.NET Core 2.0, la réponse a été appliquée comme suit (le résultat a réussi):

using (var handler = new HttpClientHandler())
{
    handler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
    handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
    using (HttpClient client = new HttpClient(handler))
    {
        string requestObjJson = requestObj.ToJson();
        var address = new Uri($"https://yourcompany.com/");
        string token = GetToken();
        client.BaseAddress = address;
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var contentData = new StringContent(requestObjJson, System.Text.Encoding.UTF8, "application/json");
        using (var response = await client.PostAsync("yourcompany/new-employee", contentData))
        {
            var content = response.Content.ReadAsStringAsync();
            var taskResult = content.Result;
            JObject resultObj = JObject.Parse(taskResult);
            return resultObj;
        }
    }
}
3
Debro012