Como ignorar a verificação de certificado quando ssl

132

Estou tentando encontrar uma maneira de ignorar a verificação do certificado ao solicitar um recurso Https. Até agora, encontrei algum artigo útil na Internet.

Mas ainda tenho algum problema. Por favor, revise meu código. Eu simplesmente não entendo o que o código ServicePointManager.ServerCertificateValidationCallbacksignifica.

Quando esse método delegado será chamado? E mais uma pergunta, em que lugar devo escrever esse código? Antes de ServicePointManager.ServerCertificateValidationCallbackexecutar ou antes Stream stream = request.GetRequestStream()?

public HttpWebRequest GetRequest()
{
    CookieContainer cookieContainer = new CookieContainer();

    // Create a request to the server
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_remoteUrl);

    #region Set request parameters

    request.Method = _context.Request.HttpMethod;
    request.UserAgent = _context.Request.UserAgent;
    request.KeepAlive = true;
    request.CookieContainer = cookieContainer;
    request.PreAuthenticate = true;
    request.AllowAutoRedirect = false;

    #endregion

    // For POST, write the post data extracted from the incoming request
    if (request.Method == "POST")
    {
        Stream clientStream = _context.Request.InputStream;
        request.ContentType = _context.Request.ContentType;
        request.ContentLength = clientStream.Length;

        ServicePointManager.ServerCertificateValidationCallback = delegate(
            Object obj, X509Certificate certificate, X509Chain chain, 
            SslPolicyErrors errors)
            {
                return (true);
            };

            Stream stream = request.GetRequestStream();

            ....
        }

        ....

        return request;
    }
}   
Joe.wang
fonte
Possível duplicata do C # Ignorar erros de certificado?
Matyas

Respostas:

67

Como existe apenas um ServicePointManager global , a configuração do ServicePointManager.ServerCertificateValidationCallback produzirá o resultado de que todas as solicitações subsequentes herdarão essa política. Por ser uma "configuração" global, seria preferível defini-la no método Application_Start em Global.asax .

A configuração do retorno de chamada substitui o comportamento padrão e você pode criar uma rotina de validação personalizada.

Sani Singh Huttunen
fonte
E o que dizer de um cliente que não possui Global.asax? Estou chamando um serviço REST em execução na rede local a partir de um dispositivo portátil.
B. Clay Shannon
3
A questão é específica para HttpWebRequest. Se você estiver usando outros meios, deverá procurar na documentação como fazer isso.
Sani Singh Huttunen
Estou usando o WebRequest, que é convertido para HttpWebRequest, como: ((HttpWebRequest) request) .Accept = contentType;
B. Clay Shannon
Conforme indicado na minha resposta: é PREFERIDO configurá-lo no Global.asax não é um requisito. Você pode até configurá-lo antes da chamada para o serviço REST.
Sani Singh Huttunen
3
Veja este link para uma possível solução.
Sani Singh Huttunen
174

Para qualquer pessoa interessada em aplicar esta solução por solicitação, esta é uma opção e usa uma expressão Lambda. A mesma expressão Lambda também pode ser aplicada ao filtro global mencionado pelo blak3r. Este método parece exigir o .NET 4.5.

String url = "https://www.stackoverflow.com";
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

No .NET 4.0, a Expressão Lambda pode ser aplicada ao filtro global como tal

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
Adam Venezia
fonte
2
Existe alguma solução por solicitação para FtpWebRequest?
Timo
Parece existir bem no meu 4.0
Nacht
Eu acho que é de longe uma solução melhor para a variante "global", embora, é claro, eu possa entender por que você a desejaria. Pessoalmente, sou a favor de uma fábrica de solicitações que gerencia esse retorno de chamada de validação. Obrigado Adam, boa solução.
O senador
2
Retornar trueé algo que você pode fazer ao experimentar durante o desenvolvimento, porém é inseguro. Deve ser condicional.
22415 Fred
1
O uso (HttpWebRequest)WebRequest.Create(url)é perfeitamente válido, mas, na minha caixa, HttpWebRequest.Create(url)ainda existe em um projeto direcionado ao .Net 4.6.2. Escolha do chef, mas neste momento HttpClienté provavelmente a melhor API a ser usada.
Adam Venezia
53

Isso funcionou para mim:

System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                        System.Security.Cryptography.X509Certificates.X509Chain chain,
                        System.Net.Security.SslPolicyErrors sslPolicyErrors)
    {
        return true; // **** Always accept
    };

Fragmento daqui: http://www.west-wind.com/weblog/posts/2011/Feb/11/HttpWebRequest-and-Ignoring-SSL-Certificate-Errors

blak3r
fonte
17
ServicePointManager.ServerCertificateValidationCallback + = (remetente, certificado, cadeia, sslPolicyErrors) => true;
David Rutgos
3
Sempre retornando verdadeiro é inseguro. Ele deve retornar condicionalmente true.
22415 Fred
Isso é por processo, portanto não é seguro.
Mikhail Orlov 24/05
25

Também há a solução de delegado curto:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 
Andrej Rommel
fonte
3
Sempre retornando trueé inseguro.
22415 Fred
7
Sim, sempre confiar em todos os certificados SSL é inseguro por definição. Evite fazê-lo, se possível.
Andrej Rommel
@AndrejRommel qual é o seu caminho recomendado?
Kiquenet
2
A maneira recomendada é criar um certificado SSL válido e utilizá-lo adequadamente se você tiver controle sobre o servidor. Acabamos criando um usando letsencrypt.org.
Andrej Rommel
5

Mencionou-se que antes do .NET 4.5 a propriedade na solicitação para acessar sua ServicePointManagernão estava disponível.

Aqui está o código do .NET 4.0 que fornecerá acesso a ServicePointcada solicitação. Ele não fornece acesso ao retorno de chamada por solicitação, mas deve permitir que você descubra mais detalhes sobre o problema. Basta acessar as propriedades scvPoint.Certificate(ou ClientCertificatese preferir).

WebRequest request = WebRequest.Create(uri);

// oddity: these two .Address values are not necessarily the same!
//  The service point appears to be related to the .Host, not the Uri itself.
//  So, check the .Host vlaues before fussing in the debugger.
//
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
if (null != svcPoint)
{
    if (!request.RequestUri.Host.Equals(svcPoint.Address.Host, StringComparison.OrdinalIgnoreCase))
    {
        Debug.WriteLine(".Address              == " + request.RequestUri.ToString());
        Debug.WriteLine(".ServicePoint.Address == " + svcPoint.Address.ToString());
    }
    Debug.WriteLine(".IssuerName           == " + svcPoint.Certificate.GetIssuerName());
}
Jesse Chisholm
fonte
Acordado! Mas então, este OP é sobre como ignoreeles, não confiam neles.
Jesse Chisholm
De qualquer forma, Usando ServicePointEu não pode sempre confiar em todos os certificados SSL, nem ignorar todos os certificados , porque não ServerCertificateValidationCallback delegar em ServicePoint
Kiquenet
4

Aliás, essa é a maneira menos detalhada de desativar toda a validação de certificado em um determinado aplicativo que eu conheço:

ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Christian Findlay
fonte
4

Em vez de adicionar um retorno de chamada ao ServicePointManager que substituirá a validação de certificado globalmente, você pode definir o retorno de chamada em uma instância local do HttpClient. Essa abordagem deve afetar apenas as chamadas feitas usando essa instância do HttpClient.

Aqui está um código de exemplo que mostra como ignorar erros de validação de certificado para servidores específicos pode ser implementado em um controlador de API da Web.

using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public class MyController : ApiController
{

    // use this HttpClient instance when making calls that need cert errors suppressed
    private static readonly HttpClient httpClient;

    static MyController()
    {
        // create a separate handler for use in this controller
        var handler = new HttpClientHandler();

        // add a custom certificate validation callback to the handler
        handler.ServerCertificateCustomValidationCallback = ((sender, cert, chain, errors) => ValidateCert(sender, cert, chain, errors));

        // create an HttpClient that will use the handler
        httpClient = new HttpClient(handler);
    }

    protected static ValidateCert(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
    {

        // set a list of servers for which cert validation errors will be ignored
        var overrideCerts = new string[]
        {
            "myproblemserver",
            "someotherserver",
            "localhost"
        };

        // if the server is in the override list, then ignore any validation errors
        var serverName = cert.Subject.ToLower();
        if (overrideCerts.Any(overrideName => serverName.Contains(overrideName))) return true;

        // otherwise use the standard validation results
        return errors == SslPolicyErrors.None;
    }

}
Sheldon
fonte
Isso não funcionará apenas para o .NET Core? (Ou sempre que ServerCertificateCustomValidationCallback foi adicionado ao HttpClientHandler)?
phillyslick
Esta solução deve funcionar no .Net Framework 4.5 e posterior, bem como no .Net Core (embora eu não o tenha testado no .Net Core).
Sheldon
@ Sheldon, esta foi uma jóia :) Usado em minhas APIs localhost entre um .net e um .net app principal com sucesso. Solução agradável sem aceitar tudo.
Large
3

Com base na resposta de Adam e no comentário de Rob, usei isso:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => certificate.Issuer == "CN=localhost";

que filtra um pouco a "ignorar". Outros emissores podem ser adicionados conforme necessário, é claro. Isso foi testado no .NET 2.0, pois precisamos oferecer suporte a algum código legado.

Andrej Grobler
fonte
@karank Desculpe pela resposta tardia - Ele pode ser adicionado em qualquer lugar antes da chamada real, por exemplo. antes de chamar request.GetResponse (). Observe que o emissor pode conter outra coisa no seu caso.
Andrej Grobler
3

CA5386: As ferramentas de análise de vulnerabilidades alertarão você sobre esses códigos.

Código correto:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
   return (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != SslPolicyErrors.RemoteCertificateNotAvailable;
};
dev_O
fonte
3

Para o núcleo .net

using (var handler = new HttpClientHandler())
{ 
    // allow the bad certificate
    handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => true;
    using (var httpClient = new HttpClient(handler))
    {
        await httpClient.PostAsync("the_url", null);
    }
}
Rich Hildebrand
fonte
2

Expressa explicitamente ...

ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(CertCheck);

private static bool CertCheck(object sender, X509Certificate cert,
X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
    return true;
}
dev_O
fonte
0

Adicionando às respostas da Sani e blak3r, adicionamos o seguinte ao código de inicialização do meu aplicativo, mas no VB:

'** Overriding the certificate validation check.
Net.ServicePointManager.ServerCertificateValidationCallback = Function(sender, certificate, chain, sslPolicyErrors) True

Parece fazer o truque.

MCattle
fonte
0

Dica: Você também pode usar esse método para rastrear certificados que expiram em breve. Isso pode economizar seu bacon se você descobrir um certificado que está prestes a expirar e pode consertá-lo a tempo. Bom também para empresas de terceiros - para nós, é a DHL / FedEx. A DHL acabou de expirar um certificado, o que está nos ferrando três dias antes do Dia de Ação de Graças. Felizmente, eu estou por perto para consertar ... desta vez!

    private static DateTime? _nextCertWarning;
    private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
    {
        if (error == SslPolicyErrors.None)
        {
            var cert2 = cert as X509Certificate2;
            if (cert2 != null)
            { 
                // If cert expires within 2 days send an alert every 2 hours
                if (cert2.NotAfter.AddDays(-2) < DateTime.Now)
                {
                    if (_nextCertWarning == null || _nextCertWarning < DateTime.Now)
                    {
                        _nextCertWarning = DateTime.Now.AddHours(2);

                        ProwlUtil.StepReached("CERT EXPIRING WITHIN 2 DAYS " + cert, cert.GetCertHashString());   // this is my own function
                    }
                }
            }

            return true;
        }
        else
        {
            switch (cert.GetCertHashString())
            {
                // Machine certs - SELF SIGNED
                case "066CF9CAD814DE2097D367F22D3A7E398B87C4D6":    

                    return true;

                default:
                    ProwlUtil.StepReached("UNTRUSTED CERT " + cert, cert.GetCertHashString());
                    return false;
            }
        }
    }
Simon_Weaver
fonte
Certifique-se de lidar com a situação em que seu mecanismo de alerta pode ter um certificado expirado - ou você terminará com um fluxo de empilhamento!
precisa saber é o seguinte
O que é ProwlUtil.StepReached?
Kiquenet
Desculpe, este é apenas o meu próprio método para ligar para a API Prowl, que pode enviar notificações para o meu telefone. No entanto, você deseja fazer logon, isso é bom. Eu gosto de ser incomodado no meu telefone por coisas assim!
22618 Simon_Weaver
0

Várias respostas acima funcionam. Eu queria uma abordagem que não precisasse continuar fazendo alterações no código e não o tornasse inseguro. Por isso, criei uma lista de permissões. A lista de permissões pode ser mantida em qualquer armazenamento de dados. Eu usei o arquivo de configuração, pois é uma lista muito pequena.

Meu código está abaixo.

ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) => {
    return error == System.Net.Security.SslPolicyErrors.None || certificateWhitelist.Contains(cert.GetCertHashString());
};
Osa E
fonte
0

Versão Unity C # desta solução:

void Awake()
{
    System.Net.ServicePointManager.ServerCertificateValidationCallback += ValidateCertification;
}

void OnDestroy()
{
    ServerCertificateValidationCallback = null;
}

public static bool ValidateCertification(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
    return true;
}
Santiago Game Lover
fonte