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 #.

Abhijeet
fonte
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);
Ronald Ramos
fonte
@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
fonte
@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();
Alberto Solano
fonte
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

yu yang Jian
fonte
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");
Carlo V. Dango
fonte
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).

stop-cran
fonte
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.

Crono
fonte
: 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

MSC
fonte
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? :)

Greg Stawski
fonte
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.

Lucian Satmarean
fonte
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);
Uchenna Nnodim
fonte
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
Hamit YILDIRIM
fonte
-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 .

Nemanja Simović
fonte
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ć