Effettua la chiamata Https utilizzando HttpClient


157

Ho usato HttpClientper effettuare chiamate WebApi utilizzando C #. Sembra pulito e veloce rispetto a WebClient. Tuttavia sono bloccato durante le Httpschiamate.

Come posso fare il codice qui sotto per fare Https chiamate?

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

MODIFICA 1: il codice sopra funziona bene per effettuare chiamate http. Ma quando cambio lo schema in https non funziona. Ecco l'errore ottenuto:

La connessione sottostante è stata chiusa: impossibile stabilire una relazione di trust per il canale sicuro SSL / TLS.

MODIFICA 2: Cambiare lo schema in https è: passo uno.

Come posso fornire certificato e chiave pubblica / privata insieme alla richiesta C #.


2
stai chiamando https semplicemente specificandonew Uri("https://foobar.com/");
felickz

2
Non ho capito bene. Non funziona già? stai ricevendo un errore? (Modifica: pubblicato prima dell'OP ha cambiato l'URI da https a http)
Tim

Risposte:


215

Se il server supporta solo versioni TLS superiori come TLS 1.2, fallirà comunque a meno che il PC client non sia configurato per utilizzare versioni TLS superiori per impostazione predefinita. Per ovviare a questo problema aggiungi quanto segue nel tuo codice.

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

Modificare il codice di esempio, sarebbe

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

@DerekS Sono contento e prego. Se per qualche motivo, non è possibile modificare il codice nell'impostazione di produzione ma in grado di eseguire un po 'di amministrazione sul server, utilizzo questa utility per configurare TLS 1.2 come predefinito. nartac.com/Products/IISCrypto
Ronald Ramos,

SecurityProtocolType.Tls12non è stato possibile trovare i valori enum che hai citato
JobaDiniz,

1
@JobaDiniz usa .NET 4.5 o versioni successive e include lo spazio dei nomi System.Net.
Ronald Ramos,

L'impostazione di SecurityProtocol deve avvenire una sola volta? Come all'avvio dell'applicazione?
CamHart,

Funziona benissimo. Grazie. Ho anche scoperto che non avevo bisogno di cancellare le intestazioni utilizzate questo client HttpClient = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage response = await client.GetAsync ("https: // ...");
AtLeastTheresToast

127

Basta specificare HTTPS nell'URI.

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

Foobar.com dovrà avere un certificato SSL attendibile o le tue chiamate falliranno con errori non attendibili.

Risposta EDIT: ClientCertificates con HttpClient

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

EDIT Answer2: se il server a cui ti stai connettendo ha disabilitato SSL, TLS 1.0 e 1.1 e stai ancora eseguendo .NET framework 4.5 (o precedente) devi fare una scelta

  1. Aggiorna a .Net 4.6+ ( Supporta TLS 1.2 per impostazione predefinita )
  2. Aggiungi le modifiche al registro per indicare a 4.5 di connettersi tramite TLS1.2 (Vedi: writeforce di salesforce per compat e chiavi per cambiare OPPURE controlla IISCryp per vedere Ronald Ramos rispondere ai commenti )
  3. Aggiungi il codice dell'applicazione per configurare manualmente .NET per la connessione su TLS1.2 (vedi la risposta di Ronald Ramos )

@felickz ottima risposta. esiste un equivalente alla libreria WebRequestHandler per Windows Phone 8?
Billatron,

@Billatron librerie diverse per WP / Win8 .. vedi
felickz,

6
Durante lo sviluppo o la gestione di certificati autofirmati è possibile ignorare gli errori cert non attendibili con quanto segue: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
sviluppatore

6
Che cosa è GetMyX509Certificate?
Fandi Susanto,

6
@developer lascia solo sperare che il codice non si
inserisca

15

Il tuo codice dovrebbe essere modificato in questo modo:

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

Devi solo usare lo https:schema URI. C'è una pagina utile qui su MSDN sulle connessioni HTTP sicure. Infatti:

Utilizzare lo schema https: URI

Il protocollo HTTP definisce due schemi URI:

http: utilizzato per connessioni non crittografate.

https: utilizzato per connessioni sicure che devono essere crittografate. Questa opzione utilizza anche i certificati digitali e le autorità di certificazione per verificare che il server sia chi afferma di essere.

Inoltre, considera che le connessioni HTTPS utilizzano un certificato SSL. Assicurati che la tua connessione sicura abbia questo certificato, altrimenti le richieste falliranno.

MODIFICARE:

Il codice sopra funziona bene per effettuare chiamate http. Ma quando cambio lo schema in https non funziona, fammi pubblicare l'errore.

Cosa significa che non funziona? Le richieste falliscono? Viene generata un'eccezione? Chiarisci la tua domanda

Se le richieste falliscono, il problema dovrebbe essere il certificato SSL.

Per risolvere il problema, è possibile utilizzare la classe HttpWebRequeste quindi la sua proprietà ClientCertificate. Inoltre, puoi trovare qui un utile esempio su come effettuare una richiesta HTTPS utilizzando il certificato.

Un esempio è il seguente (come mostrato nella pagina MSDN collegata in precedenza):

//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();

In pratica, il trucco sta nell'invio di certificati insieme alla richiesta. come farlo?
Abhijeet

Dove ottenere "C: \\ mycert.cer"? Come generare il file mycert.cer?
Masiboo,

@masiboo Quello che ho scritto risponde come effettuare chiamate HTTPS. Chiedi a Google come generare un file * .cer e troverai tu stesso la risposta.
Alberto Solano,

9

Quando mi connetto httpsho ricevuto anche questo errore, aggiungo prima questa linea HttpClient httpClient = new HttpClient();e mi connetto correttamente:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Lo so da questa risposta e da un'altra risposta simile e il commento menziona:

Questo è un trucco utile nello sviluppo, quindi mettere un'istruzione #if DEBUG #endif intorno è il minimo che dovresti fare per renderlo più sicuro e impedire che finisca in produzione

Inoltre, non ho provato il metodo in un'altra risposta che usa new X509Certificate()o new X509Certificate2()per fare un certificato, non sono sicuro semplicemente di crearlonew() funzionerà o meno.

EDIT: Alcuni riferimenti:

Creare un certificato server autofirmato in IIS 7

Importa ed esporta certificati SSL in IIS 7

Converti .pfx in .cer

Best practice per l'utilizzo di ServerCertificateValidationCallback

Trovo che il valore di Thumbprint sia uguale a x509certificate.GetCertHashString():

Recupera l'identificazione personale di un certificato


6

Ho avuto lo stesso problema durante la connessione a GitHub, che richiede un agente utente. Pertanto è sufficiente fornire questo anziché generare un certificato

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

2
dovresti revocare quel token che hai condiviso con Internet, penso che GitHub potrebbe averlo fatto automaticamente.
Amias,

21
pensi davvero che il mio token inizi con 123456789??
Carlo V. Dango,

5

Esiste un'impostazione non globale a livello di HttpClientHandler:

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

var client = new HttpClient(handler);

Pertanto, si abilitano le ultime versioni TLS.

Si noti che il valore predefinito SslProtocols.Defaultè effettivamente SslProtocols.Ssl3 | SslProtocols.Tls(verificato per .Net Core 2.1 e .Net Framework 4.7.1).


Grazie per la risposta. Una piccola nota: come da questa documentazione: docs.microsoft.com/en-us/dotnet/api/… Richiede almeno il framework 4.7.1.
GELR,

@GELR si, hai ragione. Il codice sopra riportato genera un errore di runtime in .Net 4.6.1. Risolto il problema.
stop-cran,

Nelle versioni precedenti di .NET Framework, questa proprietà è privata
JotaBe

Su Net Core 3.1 questo è tutto ciò che ha funzionato per me. L'impostazione del System.Net.ServicePointManager.SecurityProtocol globale = xxx - ha avuto un effetto assolutamente nullo in una traccia di pacchetto.
Rowan Smith,

3

È sufficiente specificare HTTPS nell'URI.

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

Se la richiesta funziona con HTTP ma non riesce con HTTPS, questo è sicuramente un problema di certificato . Assicurarsi che il chiamante si fidi dell'emittente del certificato e che il certificato non sia scaduto. Un modo rapido e semplice per verificare è provare a eseguire la query in un browser.

Puoi anche verificare sul server (se è tuo e / o se puoi) che è impostato per servire correttamente le richieste HTTPS.


: Ho un problema simile: ho effettuato la query nel browser e il certificato è valido (la chiamata passa, è autorizzata come livello di servizio e tutto funziona come un incantesimo) ma a livello di programmazione non funziona. Cos'altro può essere? se uso esattamente lo stesso certificato dal browser "chiamando manualmente il servizio" che dall'applicazione Web chiamando programmaticamente srvice?
Carlos,

2

Stavo anche ricevendo l'errore:

La connessione sottostante è stata chiusa: impossibile stabilire una relazione di trust per il canale sicuro SSL / TLS.

... con un'applicazione di targeting per Android Xamarin Forms che tenta di richiedere risorse da un provider API che ha richiesto TLS 1.3.

La soluzione era aggiornare la configurazione del progetto per scambiare il client http "gestito" (.NET) di Xamarin (che non supporta TLS 1.3 a partire da Xamarin Forms v2.5) e utilizzare invece il client nativo Android.

È un semplice progetto che attiva / disattiva in Visual Studio. Vedi screenshot di seguito.

  • Proprietà del progetto
  • Opzioni Android
  • Avanzate
  • Voce di elenco
  • Modifica "Implementazione di HttpClient" in "Android"
  • Cambia l'implementazione SSL / TLS in "Native TLS 1.2+"

inserisci qui la descrizione dell'immagine


1

Aggiungi le seguenti dichiarazioni alla tua classe:

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

Dopo:

var client = new HttpClient();

E:

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

Contento? :)


1

Ho avuto questo problema e nel mio caso la soluzione era stupidamente semplice: aprire Visual Studio con diritti di amministratore. Ho provato tutte le soluzioni di cui sopra e non ha funzionato fino a quando non ho fatto questo. Spero che risparmi tempo prezioso a qualcuno.


Dato che sei nuovo nel sito, potresti non sapere che se una risposta ha il segno di spunta verde accanto ad esso, significa che è stata accettata da OP come la soluzione corretta per il suo problema. A meno che tu non possa dare una risposta migliore e più completa, probabilmente è meglio non inviare un'altra risposta, specialmente su una domanda così vecchia.
Sam W,

4
Bene, ho avuto anche il problema e la risposta con il segno di spunta verde non ha aiutato affatto. Pensavo di dare una soluzione alternativa che mi ha aiutato, ma ehi, non lo farò la prossima volta. Grazie per i punti negativi;) buona giornata.
Lucian Satmarean,

0

Puoi provare a utilizzare il pacchetto ModernHttpClient Nuget: dopo aver scaricato il pacchetto, puoi implementarlo in questo modo:

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


 handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
 handler.PreAuthenticate = true;
 HttpClient client = new HttpClient(handler);

Sfortunatamente, questo non ha funzionato da parte mia. La soluzione era abilitare TLS 1.3 passando dal client http gestito da xamarin al client http nativo Android. Vedi la mia risposta qui sotto.
MSC

0

Sono d'accordo con Felickz ma voglio anche aggiungere un esempio per chiarire l'utilizzo in c #. Uso SSL nel servizio Windows come segue.

    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;

Se lo userò in un'applicazione Web, sto solo cambiando l'implementazione sul lato proxy in questo modo:

public partial class GatewayService : System.Web.Services.Protocols.SoapHttpClientProtocol // to => Microsoft.Web.Services2.WebServicesClientProtocol

-4

Per errore:

La connessione sottostante è stata chiusa: impossibile stabilire una relazione di trust per il canale sicuro SSL / TLS.

Penso che devi accettare il certificato incondizionatamente con il seguente codice

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

come Opposizionale ha scritto nella sua risposta alla domanda client .NET che si collega all'API Web SSL .


4
Questo sta aggirando la convalida del certificato. Non farlo mai in produzione.
John Korsnes,

Bene @JohnKorsnes, se Abhijeet sta accedendo ad un server WebAPI pubblico, non penso che possa fare molto al riguardo. Questo è stato il mio caso.
Nemanja Simović,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.