我试图连接到使用自签名的SSL证书的API。我这样做使用.NET的HttpWebRequest和HttpWebResponse对象。而且我发现了一个异常:

  

在基础连接已关闭:无法建立用于SSL信任关系/ TLS安全通道

我明白这意味着什么。我理解的为什么的.NET觉得应该提醒我,关闭连接。但是,在这种情况下,我想只是连接到API无论如何,人在这方面的中间人攻击被定罪。

所以,我怎么去增加一个例外这个自签名的证书?还是要告诉的HttpWebRequest /响应完全不验证证书的办法?我该怎么做?

有帮助吗?

解决方案

@Domster:该作品,但你可能想如果证书哈希值匹配你所期望的检查,以强制执行位安全的。因此,一个扩展版本看起来有点像这个(基于我们使用一些现场代码):

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

其他提示

原来,如果你只是想完全禁用证书验证,您可以更改在ServicePointManager的ServerCertificateValidationCallback,像这样:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

这将验证的所有证书(包括无效,过期或自签署的)。

请注意,在.NET 4.5,你可以每HttpWebRequest的本身(而不是通过全球委托影响所有请求)覆盖SSL验证

http://msdn.microsoft.com/ EN-US /库/ system.net.httpwebrequest.servercertificatevalidationcallback.aspx

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

自签名的证书添加到本地计算机受信任的根证书颁发机构

可以通过运行MMC作为管理员导入证书。

如何:使用MMC管理单元 <查看证书/ p>

Domster的回答可以使用在发送器参数被限定于特定的请求的范围ServerCertificateValidationCallback代表。以下简单的范围的类使用这种技术来暂时线了一个校验回调只执行对于给定的请求对象。

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

上面的类可以被用来忽略特定请求的所有的证书错误如下:

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

要添加为可能的帮助别人......如果你想让它提示用户安装自签名的证书,您可以使用此代码(从上面修改)。

不需要管理员权限,安装到本地用户信赖配置文件:

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

这似乎也为我们的应用程序工作,如果用户按下不,通信将无法正常工作。

更新:2015年12月11日 - 更改StoreName.Root到StoreName.My - 我将安装到本地用户存储,而不是根源。在一些系统上根本就不会工作,即使你“以管理员身份运行”

只是建立在从 devstuff 的答案,包括主体和发行人...欢迎评论...

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

有一点要记住的是,具有ServicePointManager.ServerCertificateValidationCallback似乎并不意味着CRL检查和服务器名验证都没有做,它只是提供了覆盖其结果的手段。所以,你的服务可能还需要一段时间才能获得一个CRL,你只知道事后,它失败了一些检查。

我运行到同一个问题,因为OP其中web请求将抛出该异常精确。我拥有了一切设置正确我想,安装证书,我可以在计算机存储就好找到它并将其连接到Web请求,我已经禁用的请求上下文证书的验证。

原来,我是用我的用户帐户下运行,而该证书安装到机器商店。这引起了web请求抛出此异常。为了解决我不得不问题或者是作为运行管理员或证书安装到用户存储,并从那里读取。

这似乎是C#是能够找到,即使它不能与Web请求中使用的计算机存储的证书,一旦Web请求被发出,这导致OP的异常被抛出。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top