问题描述
好的,这是我需要做的:
Okay, here is what I need to do:
我的应用程序用C#(.NET Framework 4.5)编写,需要通过HTTPS与我们的服务器通信.我们的服务器使用由我们自己的Root-CA颁发的TLS/SSL证书.我的应用程序完全信任该Root-CA,但未未安装在系统的受信任的根"证书存储中.因此,由于没有经过进一步的工作,C#拒绝与服务器联系,因为服务器的证书无法得到验证-符合预期.注意:我们无法使用系统中已安装的Root-CA.
My application, written in C# (.NET Framework 4.5), needs to communicate with our server via HTTPS. Our server uses a TLS/SSL certificate issued by our own Root-CA. That Root-CA, while perfectly trusted by my application, is not installed in the system's "trusted root" certificate store. So, without further work, C# refuses to contact the server, because the server's certificate cannot be validated - as expected. Note: We cannot use a Root-CA already installed in the system.
我该怎么做才能使我的应用程序(安全地)与我们的服务器联系?我知道C#提供了一些类来将我们的Root-CA证书作为受信任的根"安装到系统的证书存储中.那不是我们想要做的不是!这是因为(a)向用户显示了一个令人震惊的警告(以及过于技术性的警告),并且因为(b)也会对其他应用程序产生影响-我们不会这样做不需要或不需要.
What can I do to allow my application to (securely) contact our server? I know that C# provides classes to install our Root-CA certificate into the system's certificate store as a "trusted root". That's not what we want to do! That's because (a) it shows an alarming (and way too technical) warning to the user, and because (b) it would effect other applications too - which we don't want or need.
所以我需要的是告诉C#/.NET使用一组自定义"(即 application-specific )证书(而不是系统范围的证书存储)来验证服务器证书链.整个证书链仍然需要正确验证(包括吊销列表!).对于我的应用程序,只有我们的Root-CA需要被接受为受信任的"根目录.
So what I need is something that tells C#/.NET to use a "custom" (i.e. application-specific) set of certificates - instead of the system-wide certificate store - to validate the chain of the server certificate. The whole certificate chain still needs to be validated properly (including revocation lists!). Only our Root-CA needs to be accepted as a "trusted" root for my application.
做到这一点的最佳方法是什么?
What would be the best way to do this?
任何帮助将不胜感激.预先感谢!
Any help would be much appreciated. Thanks in advance!
BTW:我发现我可以使用 ServicePointManager.ServerCertificateValidationCallback
安装自己的证书验证功能.这确实起作用.但是这种方法不好,因为现在我需要在自己的代码中手动整个证书验证.但是,我不不想重新实现整个证书验证过程(例如,下载和检查CRL等),该过程已在.NET Framework中实现(并经过测试).就像重新发明轮子一样,永远无法像已经存在的.NET实现那样进行彻底的测试.
BTW: I found out that I can use ServicePointManager.ServerCertificateValidationCallback
to install my own certificate validation function. This does work. But that method isn't good, because now I need to do the whole certificate validation manually in my own code. However, I do not want to re-implement the whole certificate verification process (e.g. downloading and checking CRL's etc), which is already implemented (and tested) in .NET Framework. It's like re-inventing the wheel and can never be tested as thoroughly as the .NET implementation that already exists.
推荐答案
RemoteCertificateValidationCallback
委托是解决方案的正确方法.但是,我将在委托中使用与Olivier建议的行为不同的行为.这就是为什么:执行了太多无关的检查,而没有相关的检查.
The RemoteCertificateValidationCallback
delegate is the right way to your solution. However, I would use a different behavior in the delegate, than suggested by Olivier. That's why: too many irrelevant checks are performed and relevant are not.
因此,请详细研究该问题:
So, look at the issue in details:
首先,我们将考虑您的服务使用从商业CA购买的合法证书的情况(目前可能不是这种情况,但将来可能会这样).这意味着,如果 sslPolicyErrors
参数显示了 None
标志,则立即返回 True
,该证书有效,没有明显的理由拒绝该证书.仅当您的声明不严格时,才需要执行此步骤:
At first, we shall consider the scenario when your service uses legitimate certificate purchased from commercial CA (this may not be the case right now, but may be in some future). This means that if sslPolicyErrors
parameter has None
flag presented, immediately return True
, the certificate is valid and there are no obvious reasons to reject it. This step is necessary only if the following your statement is NOT strict:
否则,请忽略第一步.
假设,该服务仍使用来自私有和不受信任的CA的证书.在这种情况下,我们必须处理与证书链无关的错误,这些错误仅特定于SSL会话.因此,在调用 RemoteCertificateValidationCallback
委托时,我们应确保 sslPolicyErrors
RemoteCertificateNameMismatch 和 RemoteCertificateNotAvailable
标志.>参数.如果其中任何一个出现,我们将拒绝连接而不会进行其他检查.
Let's assume, the service still uses certificate from private and untrusted CA. In this case we have to handle errors which are not related to certificate chain and are specific only to SSL session. Thus, when the RemoteCertificateValidationCallback
delegate is called, we shall ensure that RemoteCertificateNameMismatch
and RemoteCertificateNotAvailable
flags are not presented in the sslPolicyErrors
parameter. If any of them presented, we shall reject connection without additional checks.
让我们假设这些标志均未显示.至此,我们已经正确处理了SSL特定的错误,只有证书链可能有问题.
Let's assume that none of these flags presented. At this point we correctly handled SSL-specific errors and only certificate chain may have issues.
如果到此为止,我们可以声明 sslPolicyErrors
参数包含 RemoteCertificateChainErrors
标志.这可能意味着一切,我们必须进行其他检查.您的根CA证书是一个常数.这意味着我们可以检查 chain
参数中的根证书,并将其与常量(例如,根CA证书的指纹)进行比较.如果比较失败,我们将立即拒绝该证书,因为它不是您的证书,并且没有明显的理由信任未知CA颁发的证书,并且可能还有其他连锁问题.
If we reach this far, we can claim that sslPolicyErrors
parameter contains RemoteCertificateChainErrors
flag. This can mean everything and we have to make additional checks. Your root CA certificate is a constant. This means that we can examine root certificate in the chain
parameter and compare it with our constant (Root CA certificate's thumbprint, for example). If comparison fails, we immediately reject the certificate, because it is not your's and there are no obvious reasons to trust certificate issued by an unknown CA and which may have other chain issues.
如果比较成功,那么我们就必须认真,正确地处理案件.我们必须执行另一个证书链接引擎实例,并指示它收集所有链问题,只有 UntrustedRoot
错误除外.这意味着,如果SSL证书还有其他问题(例如,RevocationOffline,有效性,策略错误),我们将对此有所了解,并将拒绝该证书.
If comparison succeeds, then we reached the case we have to handle carefully and properly. We have to execute another instance of certificate chaining engine and instruct it to collect any chain issues, except UntrustedRoot
error only. This means that if SSL certificate has other issues (RevocationOffline, validity, policy errors for example) we will know about that and will reject this certificate.
下面的代码是上面许多单词的编程实现:
The code below is a programmatical implementation of many words above:
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace MyNamespace {
class MyClass {
Boolean ServerCertificateValidationCallback(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
String rootCAThumbprint = ""; // write your code to get your CA's thumbprint
// remove this line if commercial CAs are not allowed to issue certificate for your service.
if ((sslPolicyErrors & (SslPolicyErrors.None)) > 0) { return true; }
if (
(sslPolicyErrors & (SslPolicyErrors.RemoteCertificateNameMismatch)) > 0 ||
(sslPolicyErrors & (SslPolicyErrors.RemoteCertificateNotAvailable)) > 0
) { return false; }
// get last chain element that should contain root CA certificate
// but this may not be the case in partial chains
X509Certificate2 projectedRootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
if (projectedRootCert.Thumbprint != rootCAThumbprint) {
return false;
}
// execute certificate chaining engine and ignore only "UntrustedRoot" error
X509Chain customChain = new X509Chain {
ChainPolicy = {
VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority
}
};
Boolean retValue = customChain.Build(chain.ChainElements[0].Certificate);
// RELEASE unmanaged resources behind X509Chain class.
customChain.Reset();
return retValue;
}
}
}
此方法(名为委托)可以附加到 ServicePointManager.ServerCertificateValidationCallback
.该代码可能被压缩(例如,在一个IF语句中组合多个IF),我使用了详细版本来反映文本逻辑.
This method (named delegate) can be attached to ServicePointManager.ServerCertificateValidationCallback
. The code might be compacted (combine multiple IF's in one IF statement, for example), I used verbose version to reflect textual logic.
这篇关于C#/.NET-如何允许“自定义"我的应用程序中的HTTPS的Root-CA(仅)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!