Fazer chamadas Https usando HttpClient


157

Eu tenho usado HttpClientpara fazer chamadas WebApi usando c #. Parece maneira limpa e rápida em comparação com WebClient. No entanto, estou atolado ao fazer Httpschamadas.

Como posso fazer o código abaixo para fazer Httpschamadas?

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: O código acima funciona bem para fazer chamadas http. Mas quando eu mudo o esquema para https, ele não funciona. Aqui está o erro obtido:

A conexão subjacente foi fechada: Não foi possível estabelecer relação de confiança para o canal seguro SSL / TLS.

EDIT 2: Alterar o esquema para https é: etapa um.

Como forneço certificado e chave pública / privada, juntamente com a solicitação de C #.


2
você está fazendo chamadas https apenas especificandonew Uri("https://foobar.com/");
— felickz

2
Estou confuso. Isso ainda não funciona? Você está recebendo um erro? (Editar: publicado antes do OP alterar o URI de https para http)
— Tim

Respostas:


215

Se o servidor suportar apenas uma versão TLS superior, como o TLS 1.2, ele ainda falhará, a menos que o PC cliente esteja configurado para usar uma versão TLS superior por padrão. Para superar esse problema, adicione o seguinte no seu código.

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

Modificando seu código de exemplo, seria

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 Estou feliz e de nada. Se, por algum motivo, você não puder modificar o código na configuração de produção, mas puder executar algum administrador no servidor, eu uso esse utilitário para configurar o TLS 1.2 como padrão. nartac.com/Products/IISCrypto
— Ronald Ramos

SecurityProtocolType.Tls12não poderia encontrar esses valores de enumeração que você mencionou
— JobaDiniz

1
O @JobaDiniz usa o .NET 4.5 ou superior e inclui o espaço para nome System.Net.
— Ronald Ramos

A configuração SecurityProtocol só precisa ocorrer uma vez? Como na inicialização do aplicativo?
— CamHart

Isso funciona muito bem. Obrigado. Também descobri que não era necessário limpar nenhum cabeçalho usado neste cliente HttpClient = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; Resposta HttpResponseMessage = aguardar client.GetAsync ("https: // ...");
— AtLeastTheresToast

127

Simplesmente especifique HTTPS no URI.

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

Foobar.com precisará ter um certificado SSL confiável ou suas chamadas falharão com erro não confiável.

EDIT Answer: ClientCertificates with HttpClient

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

EDIT Answer2: Se o servidor ao qual você está se conectando tiver desativado o SSL, TLS 1.0 e 1.1 e ainda estiver executando o .NET framework 4.5 (ou abaixo), será necessário fazer uma escolha

  1. Atualize para .Net 4.6+ ( suporta TLS 1.2 por padrão )
  2. Adicione alterações no registro para instruir o 4.5 a conectar-se através do TLS1.2 (consulte: redação do salesforce para compat e chaves para alterar OU faça check-out do IISCryp para ver Ronald Ramos responder comentários )
  3. Adicione código de aplicativo para configurar manualmente o .NET para conectar-se através do TLS1.2 (consulte a resposta de Ronald Ramos )

@felickz ótima resposta. existe algum equivalente à biblioteca WebRequestHandler para windows phone 8?
— Billatron

@Billatron diferentes bibliotecas para WP / Win8 .. veja
— felickz

6
Ao desenvolver ou lidar com certificados autoassinados, você pode ignorar erros de certificação não confiáveis ​​com o seguinte: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
— desenvolvedor

6
O que é GetMyX509Certificate?
— Fandi Susanto 14/10

6
@developer Deixa apenas a esperança de que o código não escorregar em sua compilação de produção :)
— felickz

15

Seu código deve ser modificado desta maneira:

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

Você precisa apenas usar o https:esquema de URI. Há uma página útil aqui no MSDN sobre as conexões HTTP seguras. De fato:

Use o esquema https: URI

O protocolo HTTP define dois esquemas de URI:

http: usado para conexões não criptografadas.

https: usado para conexões seguras que devem ser criptografadas. Essa opção também usa certificados digitais e autoridades de certificação para verificar se o servidor é quem afirma ser.

Além disso, considere que as conexões HTTPS usam um certificado SSL. Verifique se sua conexão segura possui este certificado, caso contrário, as solicitações falharão.

EDITAR:

O código acima funciona bem para fazer chamadas http. Mas quando altero o esquema para https, não funciona, deixe-me postar o erro.

O que significa que não funciona? Os pedidos falham? Uma exceção é lançada? Esclareça sua pergunta.

Se as solicitações falharem, o problema deverá ser o certificado SSL.

Para corrigir o problema, você pode usar a classe HttpWebRequeste, em seguida, sua propriedade ClientCertificate. Além disso, você pode encontrar aqui uma amostra útil sobre como fazer uma solicitação HTTPS usando o certificado.

Um exemplo é o seguinte (conforme mostrado na página do MSDN vinculada anteriormente):

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

Normalmente, o truque está no envio de certificados junto com a solicitação. Como fazer isso?
— Abhijeet

Onde obter "C: \\ mycert.cer"? Como gerar o arquivo mycert.cer?
— masiboo

@masiboo O que escrevi responde como fazer chamadas HTTPS. Pergunte ao google sobre como gerar um arquivo * .cer e você encontrará a resposta.
— Alberto Solano

9

Ao conectar- httpsme, também recebi esse erro, adiciono esta linha antes HttpClient httpClient = new HttpClient();e conecto com sucesso:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Eu conheço esta resposta e outra resposta similar e o comentário menciona:

Este é um truque útil no desenvolvimento, portanto, colocar uma instrução #if DEBUG #endif em torno dele é o mínimo que você deve fazer para tornar isso mais seguro e impedir que isso acabe na produção

Além disso, não tentei o método em Outra resposta que usa new X509Certificate()ou new X509Certificate2()para fazer um certificado, não tenho certeza de que simplesmente criar por new()funcionará ou não.

EDIT: Algumas referências:

Criar um certificado de servidor autoassinado no IIS 7

Importar e exportar certificados SSL no IIS 7

Converter .pfx em .cer

Práticas recomendadas para usar ServerCertificateValidationCallback

Acho que o valor da impressão digital é igual a x509certificate.GetCertHashString():

Recuperar a impressão digital de um certificado


6

Eu tive o mesmo problema ao conectar-me ao GitHub, que requer um agente de usuário. Portanto, é suficiente fornecer isso em vez de gerar um certificado

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
você deve revogar o token que está compartilhando com a internet, acho que o GitHub pode ter feito isso automaticamente.
— Amias 10/10

21
você realmente acha que meu token começa com 123456789??
— Carlo V. Dango

5

Há uma configuração não global no nível de HttpClientHandler:

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

var client = new HttpClient(handler);

Assim, é possível ativar as versões mais recentes do TLS.

Observe que o valor padrão SslProtocols.Defaulté realmente SslProtocols.Ssl3 | SslProtocols.Tls(verificado para .Net Core 2.1 e .Net Framework 4.7.1).


Obrigado pela resposta. Uma pequena observação: conforme esta documentação: docs.microsoft.com/en-us/dotnet/api/… Está exigindo pelo menos a estrutura 4.7.1.
— GELR 29/08/19

@ GELR sim, você está certo. O código acima gera um erro de tempo de execução no .Net 4.6.1. Corrigida a resposta.
— stop-cran

Nas versões anteriores do .NET Framework, essa propriedade é privada
— JotaBe 02/10/19

No Net Core 3.1, é tudo o que funcionou para mim. Definir o System.Net.ServicePointManager.SecurityProtocol global = xxx - teve efeito absolutamente zero em um rastreamento de pacote.
— Rowan Smith

3

Apenas especificar HTTPS no URI deve fazer o truque.

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

Se a solicitação funcionar com HTTP, mas falhar com HTTPS, isso certamente é um problema de certificado . Verifique se o chamador confia no emissor do certificado e se o certificado não está vencido. Uma maneira rápida e fácil de verificar isso é tentar fazer a consulta em um navegador.

Você também pode verificar no servidor (se é seu e / ou se puder) se está configurado para atender às solicitações HTTPS corretamente.


: Eu tenho um problema semelhante: fiz a consulta no navegador e o certificado é válido (a passagem é efetuada, é autorizada como nível de serviço e tudo funciona como um encanto), mas programaticamente não funciona. O que mais pode ser? se eu usar exatamente o mesmo certificado no navegador "chamando manualmente o serviço" do que no aplicativo Web chamando o srvice programaticamente?
— Carlos

2

Eu também estava recebendo o erro:

A conexão subjacente foi fechada: Não foi possível estabelecer relação de confiança para o canal seguro SSL / TLS.

... com um aplicativo de segmentação do Xamarin Forms Android tentando solicitar recursos de um provedor de API que exigia o TLS 1.3.

A solução foi atualizar a configuração do projeto para trocar o cliente http "gerenciado" (.NET) do Xamarin (que não suporta o TLS 1.3 a partir do Xamarin Forms v2.5) e, em vez disso, usar o cliente nativo do Android.

É um projeto simples de alternância no visual studio. Veja a captura de tela abaixo.

  • Propriedades do Projeto
  • Opções do Android
  • Avançado
  • Item da lista
  • Altere "Implementação HttpClient" para "Android"
  • Altere a implementação SSL / TLS para "Native TLS 1.2+"

insira a descrição da imagem aqui


1

Adicione as declarações abaixo à sua classe:

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

Depois de:

var client = new HttpClient();

E:

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

Feliz? :)


1

Eu tive esse problema e, no meu caso, a solução era estupidamente simples: abra o Visual Studio com direitos de administrador. Eu tentei todas as soluções acima e não funcionou até fazer isso. Espero que poupa a alguém um tempo precioso.


Como você é novo no site, talvez não saiba que, se uma resposta tiver uma verificação verde ao lado, isso significa que ela foi aceita pelo OP como a solução correta para o seu problema. A menos que você possa dar uma resposta melhor e mais completa, provavelmente é melhor não enviar outra resposta, especialmente em uma pergunta tão antiga.
— Sam W

4
Bem, eu também tive o problema, e a resposta verde marcada não ajudou em nada. Pensei em dar uma solução alternativa que me ajudou, mas, ei, não vou fazer da próxima vez. Obrigado pelos pontos negativos;) tenha um ótimo dia.
— Lucian Satmarean

0

Você pode tentar usar o pacote Nuget ModernHttpClient : Depois de baixar o pacote, você pode implementá-lo assim:

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


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

Infelizmente, isso não funcionou comigo. A solução foi ativar o TLS 1.3 alternando do cliente http gerenciado pelo xamarin para o cliente http nativo do Android. Veja minha resposta abaixo.
— MSC

0

Concordo com felickz, mas também quero adicionar um exemplo para esclarecer o uso em c #. Eu uso o SSL no serviço Windows da seguinte maneira.

    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 eu vou usá-lo em um aplicativo Web, estou apenas alterando a implementação no lado do proxy assim:

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

-4

Para erro:

A conexão subjacente foi fechada: Não foi possível estabelecer relação de confiança para o canal seguro SSL / TLS.

Eu acho que você precisa aceitar certificado incondicionalmente com o seguinte código

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

como Oppositional escreveu em sua resposta à pergunta cliente .NET conexão com SSL Web API .


4
Isso está ignorando a validação de certificado. Nunca faça isso na produção.
— John Korsnes 03/02/19

Bem, @JohnKorsnes, se Abhijeet estiver acessando algum servidor WebAPI público, não acho que ele possa fazer muito a respeito. Este foi o meu caso.
— Nemanja Simović
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.