对.NET的HttpWebRequest / Response使用自签名证书
我试图连接到使用自签名SSL证书的API。 我这样做使用.NET的HttpWebRequest和HttpWebResponse对象。 我得到一个例外:
底层连接已closures:无法build立SSL / TLS安全通道的信任关系。
我明白这是什么意思。 我明白为什么 .NET感觉它应该警告我并closures连接。 但在这种情况下,我想只是连接到API,中间人攻击就该被诅咒。
那么,我如何去为这个自签名证书添加一个exception呢? 或者是告诉HttpWebRequest / Response不要validation证书的方法? 我该怎么做?
@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; }
事实certificate,如果您只想完全禁用证书validation,则可以更改ServicePointManager上的ServerCertificateValidationCallback,如下所示:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
这将validation所有证书(包括无效的,过期的或自签名的)。
将自签名证书添加到本地计算机受信任的根证书颁发机构
您可以通过以pipe理员身份运行MMC来导入证书。
如何:使用MMCpipe理单元查看证书
请注意,在.NET 4.5中,您可以覆盖每个HttpWebRequest本身的SSLvalidation(而不是通过影响所有请求的全局委托):
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); request.ServerCertificateValidationCallback = delegate { return true; };
在Domster的答案中使用的validationcallback的范围可以限制为使用ServerCertificateValidationCallback
委托上的sender参数的特定请求。 下面这个简单的作用域类使用这种技术来临时连接一个只对给定的请求对象执行的validationcallback。
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(); }
作为一个可能的帮助添加到别人…如果你想要它提示用户安装自签名证书,你可以使用这个代码(从上面修改)。
不需要pipe理员权限,安装到本地用户信任的configuration文件:
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 – 我将安装到本地用户存储中,而不是Root。 根在某些系统上将不起作用,即使您“以pipe理员身份运行”
只是build立在回答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检查和servernamevalidation没有完成,它只提供了一种方法来覆盖它们的结果。 所以你的服务可能还需要一段时间才能获得一个CRL,你只会事后才知道它没有通过一些检查。
我遇到了和OP一样的问题,在那里Web请求会抛出确切的exception。 我想所有东西都安装正确,我想,证书已安装,我可以在机器存储中find它,并将其附加到Web请求,并已禁用请求上下文中的证书的validation。
事实certificate,我在我的用户帐户下运行,并且证书已经安装到机器商店。 这导致Web请求引发此exception。 要解决这个问题,我不得不以pipe理员身份运行,或者将证书安装到用户存储中,然后从那里读取。
看起来C#能够在机器存储中find证书,即使它不能与Web请求一起使用,并且一旦Web请求被发出,就会导致OP的exception被抛出。