Passer un appel Https à l'aide de HttpClient

157

J'utilise HttpClientpour faire des appels WebApi en utilisant C #. Semble moyen net et rapide par rapport à WebClient. Cependant, je suis coincé lorsque je passe des Httpsappels.

Comment puis-je créer le code ci-dessous pour passer des Httpsappels?

HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>(
                "api/SaveData", request);

EDIT 1: Le code ci-dessus fonctionne très bien pour passer des appels http. Mais lorsque je change le schéma en https, cela ne fonctionne pas. Voici l'erreur obtenue:

La connexion sous-jacente a été fermée: impossible d'établir une relation d'approbation pour le canal sécurisé SSL / TLS.

EDIT 2: Changer le schéma en https est: la première étape.

Comment puis-je fournir un certificat et une clé publique / privée avec une demande C #.

Abhijeet
la source
2
vous effectuez des appels https simplement en spécifiantnew Uri("https://foobar.com/");
felickz
2
Je suis confus. Cela ne fonctionne pas déjà? Avez-vous une erreur? (Edit: Posté avant que l'OP ait changé l'URI de https à http)
Tim

Réponses:

215

Si le serveur prend uniquement en charge la version TLS supérieure comme TLS 1.2 uniquement, il échouera toujours à moins que votre PC client ne soit configuré pour utiliser une version TLS supérieure par défaut. Pour surmonter ce problème, ajoutez ce qui suit dans votre code.

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

En modifiant votre exemple de code, ce serait

HttpClient httpClient = new HttpClient();   

//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>("api/SaveData", request);
Ronald Ramos
la source
@DerekS Je suis content et vous êtes les bienvenus. Si, pour une raison quelconque, vous ne pouvez pas modifier le code dans le paramètre de production mais que vous êtes capable de faire un certain administrateur sur le serveur, j'utilise cet utilitaire pour configurer TLS 1.2 par défaut. nartac.com/Products/IISCrypto
Ronald Ramos
SecurityProtocolType.Tls12Impossible de trouver ces valeurs d'énumération que vous avez mentionnées
JobaDiniz
1
@JobaDiniz utilise .NET 4.5 ou supérieur et inclut l'espace de noms System.Net.
Ronald Ramos
La configuration de SecurityProtocol ne doit-elle se produire qu'une seule fois? Comme au démarrage de l'application?
CamHart
Cela fonctionne très bien. Merci. J'ai également constaté que je n'avais pas besoin d'effacer les en-têtes utilisés ce client HttpClient = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage response = attendre client.GetAsync ("https: // ...");
AtLeastTheresToast
127

Spécifiez simplement HTTPS dans l'URI.

new Uri("https://foobar.com/");

Foobar.com devra avoir un certificat SSL de confiance ou vos appels échoueront avec une erreur non fiable.

EDIT Réponse: ClientCertificates avec HttpClient

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);

EDIT Answer2: Si le serveur auquel vous vous connectez a désactivé SSL, TLS 1.0 et 1.1 et que vous exécutez toujours .NET Framework 4.5 (ou inférieur), vous devez faire un choix

  1. Mettre à niveau vers .Net 4.6+ ( prend en charge TLS 1.2 par défaut )
  2. Ajoutez des modifications de registre pour indiquer à 4.5 de se connecter via TLS1.2 (voir: écriture de salesforce pour la compatibilité et les clés à modifier OU vérifier IISCrypt pour voir les commentaires de réponse de Ronald Ramos )
  3. Ajouter le code d'application pour configurer manuellement .NET pour se connecter via TLS1.2 (voir la réponse de Ronald Ramos )
Felickz
la source
@felickz excellente réponse. existe-t-il un équivalent à la bibliothèque WebRequestHandler pour Windows Phone 8?
Billatron
@Billatron différentes bibliothèques pour WP / Win8 .. voir
felickz
6
Lors du développement ou du traitement de certificats auto-signés, vous pouvez ignorer les erreurs de certificat non fiables avec les éléments suivants: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
développeur
6
C'est quoi GetMyX509Certificate?
Fandi Susanto
6
@developer laisse juste espérer que le code ne se glisse pas dans votre version de production :)
felickz
15

Votre code doit être modifié de cette manière:

httpClient.BaseAddress = new Uri("https://foobar.com/");

Vous n'avez qu'à utiliser le https:schéma URI. Il y a une page utile ici sur MSDN sur les connexions HTTP sécurisées. En effet:

Utilisez le schéma https: URI

Le protocole HTTP définit deux schémas d'URI:

http: utilisé pour les connexions non chiffrées.

https: utilisé pour les connexions sécurisées qui doivent être chiffrées. Cette option utilise également des certificats numériques et des autorités de certification pour vérifier que le serveur est bien celui qu'il prétend être.

De plus, considérez que les connexions HTTPS utilisent un certificat SSL. Assurez-vous que votre connexion sécurisée possède ce certificat, sinon les demandes échoueront.

ÉDITER:

Le code ci-dessus fonctionne très bien pour passer des appels http. Mais lorsque je change le schéma en https, cela ne fonctionne pas, laissez-moi publier l'erreur.

Qu'est-ce que cela signifie ne fonctionne pas? Les demandes échouent? Une exception est levée? Clarifiez votre question.

Si les demandes échouent, le problème doit être le certificat SSL.

Pour résoudre le problème, vous pouvez utiliser la classe HttpWebRequest, puis sa propriété ClientCertificate. De plus, vous pouvez trouver ici un exemple utile sur la façon de faire une requête HTTPS à l'aide du certificat.

Un exemple est le suivant (comme indiqué dans la page MSDN liée précédemment):

//You must change the path to point to your .cer file location. 
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\mycert.cer");
// Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = new CertPolicy();
// You must change the URL to point to your Web server.
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://YourServer/sample.asp");
Request.ClientCertificates.Add(Cert);
Request.UserAgent = "Client Cert Sample";
Request.Method = "GET";
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
Alberto Solano
la source
Précisément, l'astuce consiste à envoyer des certificats avec la demande. comment faire ça?
Abhijeet
Où trouver "C: \\ mycert.cer"? Comment générer un fichier mycert.cer?
masiboo
@masiboo Ce que j'ai écrit explique comment passer des appels HTTPS. Demandez à Google comment générer un fichier * .cer et vous trouverez vous-même la réponse.
Alberto Solano le
9

Lorsque httpsje me connecte à, j'ai également cette erreur, j'ajoute cette ligne avant HttpClient httpClient = new HttpClient();et je me connecte avec succès:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Je le sais grâce à cette réponse et à une autre anwser similaire et le commentaire mentionne:

C'est un hack utile en développement, donc mettre une instruction #if DEBUG #endif autour de lui est le moins que vous devriez faire pour le rendre plus sûr et empêcher que cela se termine en production.

De plus, je n'ai pas essayé la méthode dans Another Answer qui utilise new X509Certificate()ou new X509Certificate2()pour créer un certificat, je ne suis pas sûr que simplement créer par new()fonctionnera ou non.

EDIT: Quelques références:

Créer un certificat de serveur auto-signé dans IIS 7

Importer et exporter des certificats SSL dans IIS 7

Convertir .pfx en .cer

Meilleures pratiques d'utilisation de ServerCertificateValidationCallback

Je trouve que la valeur de Thumbprint est égale à x509certificate.GetCertHashString():

Récupérer l'empreinte d'un certificat

yu yang jian
la source
6

J'ai eu le même problème lors de la connexion à GitHub, qui nécessite un agent utilisateur. Il suffit donc de fournir cela plutôt que de générer un certificat

var client = new HttpClient();

client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.Add(
    "Authorization",
    "token 123456789307d8c1d138ddb0848ede028ed30567");
client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add(
    "User-Agent",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
Carlo V. Dango
la source
2
vous devriez révoquer ce jeton que vous avez partagé avec Internet, je pense que GitHub l'a peut-être fait automatiquement.
Amias
21
pensez-vous vraiment que mon jeton commence par 123456789??
Carlo V.Dango
5

Il existe un paramètre non global au niveau de HttpClientHandler:

var handler = new HttpClientHandler()
{
    SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
};

var client = new HttpClient(handler);

Ainsi, on active les dernières versions de TLS.

Notez que la valeur par défaut SslProtocols.Defaultest en fait SslProtocols.Ssl3 | SslProtocols.Tls(vérifiée pour .Net Core 2.1 et .Net Framework 4.7.1).

stop-cran
la source
Merci d'avoir répondu. Une petite note: selon cette documentation: docs.microsoft.com/en-us/dotnet/api/ ... Cela nécessite au moins le framework 4.7.1.
GELR
@GELR oui, vous avez raison. Le code ci-dessus génère une erreur d'exécution dans .Net 4.6.1. Correction de la réponse.
stop-cran
Dans les versions précédentes du .NET Framework, cette propriété est privée
JotaBe
Sur Net Core 3.1, c'est tout ce qui a fonctionné pour moi. La définition de System.Net.ServicePointManager.SecurityProtocol = xxx global - n'avait absolument aucun effet dans une trace de paquet.
Rowan Smith le
3

Le simple fait de spécifier HTTPS dans l'URI devrait faire l'affaire.

httpClient.BaseAddress = new Uri("https://foobar.com/");

Si la requête fonctionne avec HTTP mais échoue avec HTTPS, il s'agit très certainement d'un problème de certificat . Assurez-vous que l'appelant fait confiance à l'émetteur du certificat et que le certificat n'a pas expiré. Un moyen rapide et facile de vérifier cela est d'essayer de faire la requête dans un navigateur.

Vous pouvez également vérifier sur le serveur (si c'est le vôtre et / ou si vous le pouvez) qu'il est configuré pour servir correctement les requêtes HTTPS.

Crono
la source
: J'ai un problème similaire: j'ai fait la requête dans le navigateur et le certificat est valide (l'appel passe, est autorisé comme niveau de service et tout fonctionne comme un charme) mais ne fonctionne pas par programme. Que peut être d'autre? si j'utilise exactement le même certificat du navigateur "appelant manuellement le service" que de l'application Web appelant le srvice par programme?
Carlos
2

J'obtenais également l'erreur:

La connexion sous-jacente a été fermée: impossible d'établir une relation d'approbation pour le canal sécurisé SSL / TLS.

... avec une application de ciblage Android Xamarin Forms qui tente de demander des ressources à un fournisseur d'API nécessitant TLS 1.3.

La solution consistait à mettre à jour la configuration du projet pour échanger le client http «managé» (.NET) Xamarin (qui ne prend pas en charge TLS 1.3 à partir de Xamarin Forms v2.5) et à utiliser à la place le client natif Android.

C'est un simple basculement de projet dans Visual Studio. Voir la capture d'écran ci-dessous.

  • Propriétés du projet
  • Options Android
  • Avancée
  • Élément de liste
  • Remplacez "Implémentation HttpClient" par "Android"
  • Remplacez l'implémentation SSL / TLS par "Native TLS 1.2+"

entrez la description de l'image ici

MSC
la source
1

Ajoutez les déclarations ci-dessous à votre classe:

public const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
public const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;

Après:

var client = new HttpClient();

Et:

ServicePointManager.SecurityProtocol = Tls12;
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 /*| SecurityProtocolType.Tls */| Tls12;

Heureux? :)

Greg Stawski
la source
1

J'ai eu ce problème et dans mon cas, la solution était stupidement simple: ouvrir Visual Studio avec les droits d'administrateur. J'ai essayé toutes les solutions ci-dessus et cela n'a pas fonctionné jusqu'à ce que je fasse cela. J'espère que cela fera gagner un temps précieux à quelqu'un.

Lucian Satmarean
la source
Puisque vous êtes nouveau sur le site, vous ne savez peut-être pas que si une réponse a la coche verte à côté, cela signifie qu'elle a été acceptée par OP comme la solution correcte pour son problème. À moins que vous ne puissiez donner une meilleure réponse plus complète, il est probablement préférable de ne pas soumettre une autre réponse, en particulier sur une question aussi ancienne.
Sam W
4
Eh bien, j'ai eu le problème aussi, et la réponse cochée en vert n'a pas du tout aidé. Je pensais donner une solution alternative qui m'a aidé, mais bon, je ne le ferai pas la prochaine fois. Merci pour les points négatifs;) passez une excellente journée.
Lucian Satmarean
0

Vous pouvez essayer d'utiliser le package ModernHttpClient Nuget: Après avoir téléchargé le package, vous pouvez l'implémenter comme ceci:

 var handler = new ModernHttpClient.NativeMessageHandler()
 {
     UseProxy = true,
 };


 handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
 handler.PreAuthenticate = true;
 HttpClient client = new HttpClient(handler);
Uchenna Nnodim
la source
Malheureusement, cela n'a pas fonctionné de moi. La solution consistait à activer TLS 1.3 en passant du client http géré par xamarin au client http natif Android. Voir ma réponse ci-dessous.
MSC
0

Je suis d'accord avec felickz mais je souhaite également ajouter un exemple pour clarifier l'utilisation en c #. J'utilise SSL dans le service Windows comme suit.

    var certificatePath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
    gateway = new GatewayService();
    gateway.PreAuthenticate = true;


    X509Certificate2 cert = new X509Certificate2(certificatePath + @"\Attached\my_certificate.pfx","certificate_password");
    gateway.ClientCertificates.Add(cert);

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    gateway.UserAgent = Guid.NewGuid().ToString();
    gateway.Timeout = int.MaxValue;

Si je vais l'utiliser dans une application Web, je change simplement l'implémentation côté proxy comme ceci:

public partial class GatewayService : System.Web.Services.Protocols.SoapHttpClientProtocol // to => Microsoft.Web.Services2.WebServicesClientProtocol
Hamit YILDIRIM
la source
-4

Pour erreur:

La connexion sous-jacente a été fermée: impossible d'établir une relation d'approbation pour le canal sécurisé SSL / TLS.

Je pense que vous devez accepter le certificat sans condition avec le code suivant

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

comme Oppositional l'a écrit dans sa réponse à la question .NET client se connectant à l'API Web ssl .

Nemanja Simović
la source
4
Ceci contourne la validation du certificat. Ne faites jamais cela en production.
John Korsnes
Eh bien @JohnKorsnes, si Abhijeet accède à un serveur WebAPI public, je ne pense pas qu'il puisse faire grand-chose à ce sujet. C'était mon cas.
Nemanja Simović