Domanda

Sto cercando di connettersi a un'API che utilizza un certificato SSL self-signed. Sto facendo in modo da utilizzare di NET HttpWebRequest e HttpWebResponse oggetti. E mi sto un'eccezione che:

  

La connessione sottostante è stata chiusa:. Impossibile stabilire una relazione di fiducia per la SSL / TLS canale sicuro

capisco che cosa questo significa. E capisco perché .NET ritiene di dover mettere in guardia me e chiudere la connessione. Ma in questo caso, mi piacerebbe per la connessione solo per l'API in ogni caso, man-in-the-middle essere dannato.

Quindi, come posso fare per aggiungere un'eccezione per questo certificato auto-firmato? O è l'approccio di dire HttpWebRequest / Response di non convalidare il certificato a tutti? Come potrei farlo?

È stato utile?

Soluzione

@Domster: che funziona, ma si potrebbe desiderare di far rispettare un po 'di sicurezza controllando se l'hash certificato corrisponde ciò che vi aspettate. Quindi, una versione ampliata sembra un po 'come questo (sulla base di un codice dal vivo che stiamo utilizzando):

static readonly byte[] apiCertHash = { 0xZZ, 0xYY, ....};

/// <summary>
/// Somewhere in your application's startup/init sequence...
/// </summary>
void InitPhase()
{
    // Override automatic validation of SSL server certificates.
    ServicePointManager.ServerCertificateValidationCallback =
           ValidateServerCertficate;
}

/// <summary>
/// Validates the SSL server certificate.
/// </summary>
/// <param name="sender">An object that contains state information for this
/// validation.</param>
/// <param name="cert">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the
/// remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote
/// certificate.</param>
/// <returns>Returns a boolean value that determines whether the specified
/// certificate is accepted for authentication; true to accept or false to
/// reject.</returns>
private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Good certificate.
        return true;
    }

    log.DebugFormat("SSL certificate error: {0}", sslPolicyErrors);

    bool certMatch = false; // Assume failure
    byte[] certHash = cert.GetCertHash();
    if (certHash.Length == apiCertHash.Length)
    {
        certMatch = true; // Now assume success.
        for (int idx = 0; idx < certHash.Length; idx++)
        {
            if (certHash[idx] != apiCertHash[idx])
            {
                certMatch = false; // No match
                break;
            }
        }
    }

    // Return true => allow unauthenticated server,
    //        false => disallow unauthenticated server.
    return certMatch;
}

Altri suggerimenti

risulta, se si desidera solo per disabilitare la convalida del certificato del tutto, è possibile modificare il ServerCertificateValidationCallback sul ServicePointManager, in questo modo:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Questa convaliderà tutti i certificati non validi (tra cui, scaduto o quelli self-signed).

Si noti che in .NET 4.5 è possibile ignorare la convalida SSL per HttpWebRequest in sé (e non tramite delegato globale che colpisce tutte le richieste):

http://msdn.microsoft.com/ it-it / library / system.net.httpwebrequest.servercertificatevalidationcallback.aspx

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.ServerCertificateValidationCallback = delegate { return true; };

Aggiungi il sé firmato cert alle Autorità di certificazione principale computer affidabile locali

È possibile importare il CERT eseguendo MMC come amministratore.

Procedura: Visualizza certificati con lo snap-in MMC

Il campo di applicazione della richiamata convalida utilizzato in risposta di Domster può essere limitata ad una specifica richiesta utilizzando il parametro mittente sul delegato ServerCertificateValidationCallback. La seguente classe di portata semplice utilizza questa tecnica per collegare temporaneamente un callback di convalida che esegue solo per un determinato oggetto richiesta.

public class ServerCertificateValidationScope : IDisposable
{
    private readonly RemoteCertificateValidationCallback _callback;

    public ServerCertificateValidationScope(object request,
        RemoteCertificateValidationCallback callback)
    {
        var previous = ServicePointManager.ServerCertificateValidationCallback;
        _callback = (sender, certificate, chain, errors) =>
            {
                if (sender == request)
                {
                    return callback(sender, certificate, chain, errors);
                }
                if (previous != null)
                {
                    return previous(sender, certificate, chain, errors);
                }
                return errors == SslPolicyErrors.None;
            };
        ServicePointManager.ServerCertificateValidationCallback += _callback;
    }

    public void Dispose()
    {
        ServicePointManager.ServerCertificateValidationCallback -= _callback;
    }
}

La classe di cui sopra può essere utilizzato per ignorare tutti gli errori certificato per una richiesta specifica nel seguente modo:

var request = WebRequest.Create(uri);
using (new ServerCertificateValidationScope(request, delegate { return true; }))
{
    request.GetResponse();
}

Per aggiungere come possibile aiuto a qualcun altro ... Se si desidera richiedere all'utente di installare il CERT auto-firmato, è possibile utilizzare questo codice (modificato da sopra).

Non richiede diritti di amministratore, installa agli utenti locali di fiducia profili:

    private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            // Good certificate.
            return true;
        }

        Common.Helpers.Logger.Log.Error(string.Format("SSL certificate error: {0}", sslPolicyErrors));
        try
        {
            using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                store.Open(OpenFlags.ReadWrite);
                store.Add(new X509Certificate2(cert));
                store.Close();
            }
            return true;
        }
        catch (Exception ex)
        {
            Common.Helpers.Logger.Log.Error(string.Format("SSL certificate add Error: {0}", ex.Message));
        }

        return false;
    }

Questo sembra funzionare bene per la nostra applicazione, e se l'utente non preme, la comunicazione non funziona.

Aggiornamento: 2015/12/11 - Cambiato StoreName.Root a StoreName.My - My installerà nel negozio utenti locali, invece di Root. Root su alcuni sistemi non funziona, anche se si "Esegui come amministratore"

Proprio sulla base risposta da devstuff per includere commenti relativi all'oggetto e all'emittente ... benvenuti ...

public class SelfSignedCertificateValidator
{
    private class CertificateAttributes
    {
        public string Subject { get; private set; }
        public string Issuer { get; private set; }
        public string Thumbprint { get; private set; }

        public CertificateAttributes(string subject, string issuer, string thumbprint)
        {
            Subject = subject;
            Issuer = issuer;                
            Thumbprint = thumbprint.Trim(
                new char[] { '\u200e', '\u200f' } // strip any lrt and rlt markers from copy/paste
                ); 
        }

        public bool IsMatch(X509Certificate cert)
        {
            bool subjectMatches = Subject.Replace(" ", "").Equals(cert.Subject.Replace(" ", ""), StringComparison.InvariantCulture);
            bool issuerMatches = Issuer.Replace(" ", "").Equals(cert.Issuer.Replace(" ", ""), StringComparison.InvariantCulture);
            bool thumbprintMatches = Thumbprint == String.Join(" ", cert.GetCertHash().Select(h => h.ToString("x2")));
            return subjectMatches && issuerMatches && thumbprintMatches; 
        }
    }

    private readonly List<CertificateAttributes> __knownSelfSignedCertificates = new List<CertificateAttributes> {
        new CertificateAttributes(  // can paste values from "view cert" dialog
            "CN = subject.company.int", 
            "CN = issuer.company.int", 
            "f6 23 16 3d 5a d8 e5 1e 13 58 85 0a 34 9f d6 d3 c8 23 a8 f4") 
    };       

    private static bool __createdSingleton = false;

    public SelfSignedCertificateValidator()
    {
        lock (this)
        {
            if (__createdSingleton)
                throw new Exception("Only a single instance can be instanciated.");

            // Hook in validation of SSL server certificates.  
            ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate;

            __createdSingleton = true;
        }
    }

    /// <summary>
    /// Validates the SSL server certificate.
    /// </summary>
    /// <param name="sender">An object that contains state information for this
    /// validation.</param>
    /// <param name="cert">The certificate used to authenticate the remote party.</param>
    /// <param name="chain">The chain of certificate authorities associated with the
    /// remote certificate.</param>
    /// <param name="sslPolicyErrors">One or more errors associated with the remote
    /// certificate.</param>
    /// <returns>Returns a boolean value that determines whether the specified
    /// certificate is accepted for authentication; true to accept or false to
    /// reject.</returns>
    private bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;   // Good certificate.

        Dbg.WriteLine("SSL certificate error: {0}", sslPolicyErrors);
        return __knownSelfSignedCertificates.Any(c => c.IsMatch(cert));            
    }
}

Una cosa da tenere a mente è che avere il ServicePointManager.ServerCertificateValidationCallback non sembra significare che il controllo CRL e la validazione servername non sono fatto, fornisce solo un mezzo per ignorare il loro risultato. Quindi, il servizio potrebbe ancora prendere un po 'per ottenere un CRL, saprete solo dopo che non è riuscito alcuni controlli.

Sono stato in esecuzione nello stesso problema come l'OP in cui la richiesta web getterebbe tale eccezione esatto. Avevo impostato tutto correttamente ho pensato, il certificato è stato installato, ho potuto individuarlo nell'archivio del computer bene e allegarlo alla richiesta Web, e avevo disattivato la verifica dei certificati sul contesto richiesta.

Si è scoperto che stavo correndo sotto il mio account utente, e che il certificato è stato installato al negozio macchina. Ciò ha causato la richiesta web per lanciare questa eccezione. Per risolvere il problema ho dovuto essere sia in esecuzione come amministratore o installare il certificato all'archivio utente e leggerlo da lì.

Sembrerebbe che C # è in grado di trovare il certificato nell'archivio del computer, anche se non può essere utilizzato con una richiesta Web, e che questo si traduce in deroga del PO gettati una volta che la richiesta web è stato rilasciato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top