.NET Web服务客户端是否支持IP替代主题名称中的SSL证书
我有一个应用程序,其中许多端点不支持DNS查找,因此对于那些端点他们不能使用URL来访问我们的服务器。 我们的应用程序给出了我们的服务器的IP列表,他们需要击中,这对http很有效。 我试图启用使用https打我们的服务器,我已经创建了一个与一些 网址作为主题替代名称加上我们的服务器IP作为主题替代名称的SAN证书。
例如在openssl.cnf中我用来创建CSR我有:
DNS.1 = test.example.com
DNS.2 = test2.example.com
IP.1 = xxx.xxx。 xxx.xxx
IP.2 = yyy.yyy.yyy.yyy
.NET Web服务客户端是否支持IP替代主题名称中的SSL证书
注XXX和YYY实际上是在证书的真实IP地址。
我们的Java Web服务客户端可以使用https访问我们的服务器,IP地址没有问题。
我们的.NET Web服务客户端不能。
我为客户端使用.NET Framework 3.5。
我正在使用Tomcat 8作为服务器。
.NET客户端可以使用诸如www.test.example.com之类的comman名称和诸如test2.example.com之类的替代 名称命中它,但如果尝试使用IP地址,则它会失败。
我启用了来自system.diagnostics的跟踪,以查看SSL握手抱怨什么,并且我在来自服务器的某些数据中看到,DNS.x的使用者替代名称是 发送给客户端,但发送给客户端但是 IP不是,所以IP不能作为证书中的替代品。
[主题]
CN = www.test.example.com,O = Epicor软件公司,L =奥斯汀,S =德州,
C = US
简单名称:www.test.example.com
DNS名称:test.example.com
[发行]
CN = DigiCert SHA2安全服务器CA,O = DigiCert公司,C = US
简单名称:DigiCert SHA2安全服务器CA
DNS名称: DigiCert SHA2安全服务器CA
[序列号]
0DB4E110FDCE072E4D98F756B3D66B3C
[不前]
2016年3月27日下午7点00分00秒
[不经过]
2017年4月19日7 :00:上午12点
[指纹]
6FBC98CA67D77121BC934E0A1AC5AB552EAB88ED
[签名算法]
sha256RSA(1.2.840.113549.1.1。11)
[公钥]
算法:RSA
长度:2048
密钥blob:30 82 01 0A 02 82 01 01 00 CA 24 0B F0 F4 F6 58 1D 53 F6 5E 11 E6 7C 07 ae 81 4e bd b8 8d 6c ff 2c 7b c9 21 6f d4 99 86 9c 04 23 25 8b 34 31 dd 1c 85 1a 0c 86 34 a3 32 a1 17 12 3f c1 45 bf 38 3d 37 19 29 9c 44 e8 d0 b3 d6 92 9d 3d 9c ad 31 24 55 41 86 1a 2e ff 4c cb bf 32 0a 48 24 05 3f ca 0a 3c 8d f6 e0 31 14 3a a3 d8 7b 97 7b 3d 98 80 3a d8 f6 76 ca ....
ProcessId = 20004
DateTime = 2017-02-27T21:22:06.0846039Z
System.Net Inf ormation:0:[22244] SecureChannel#66166301 - 远程证书有误:
的ProcessID = 20004
日期时间= 2017-02-27T21:22:06.1146042Z
System.Net信息0:[22244] SecureChannel#66166301 - 证书名称不匹配。
的ProcessID = 20004
日期时间= 2017-02-27T21:22:06.1146042Z
System.Net信息0:[22244] SecureChannel#66166301 - 远程证书,验证由用户为无效。
的ProcessID = 20004
日期时间= 2017-02-27T21:22:06.1146042Z
的System.Net.Sockets详细:0:[22244]插孔#15688314 ::的Dispose()
的ProcessID = 20004
日期时间= 2017-02-27T21:22:06.1146042Z
System.Net错误:0:[22244] HttpWebRequest中的异常#35320229 :: - 底层连接已关闭:无法建立SSL/TLS安全通道的信任关系。
与斯蒂芬斯帮助指向我的Schannel和IP地址字段以及许多其他研究,我已经找到了这个问题的答案。
.NET不直接支持将IP地址作为主题备用名称,因为它依赖于Schannel对证书中的IP地址进行验证。
Schannel只在cert的“DNS Name =”字段中查找https请求中给出的主机名(IP)。因此,如果您可以让CA向您提供“DNS Name =”字段中列出的IP地址的证书,那么它可能会起作用。 但是,我的CA不允许将IP地址放置在“DNS Name =”字段中,并要求它们位于“IP Address =”字段中,因为这是现在的行业标准。
所以这意味着当Schannel无法在证书中找到IP时,它将获得证书不匹配错误。
然后,它查找ServicePointManager.ServerCertificateValidationCallback属性,调用的方法要求用户验证默认情况下此属性设置为None的cert。 因此它完全拒绝连接。
以c#为例,您可以创建自己的回调方法并将其分配给ServicePointManager.ServerCertificateValidationCallback属性。 该方法采用发件人对象,该对象是请求中使用的WebRequest对象,X509Certificate是证书,X509Chain和SslPolicyErrors值,在URL中使用IP时,该值将是错误SslPolicyErrors.RemoteCertificationNameMismatch。
private void init()
{
// Add a custom callback for server validation so that https to IP addresses will work.
// See comments in InternalCallback method for more details
ServicePointManager.ServerCertificateValidationCallback += this.InternalCallback;
}
private bool InternalCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// The security package on Windows OS known as Schannel provides the authentication between
// clients and servers.
// If you are using https with an IP to a server with a cert that has IP addresses in the Subject Alternative Names,
// Schannel fails to find those IPs among the "IP Address=" fields in the cert.
// Schannel only looks at the DNS Name= fields.
// consequently Schannel will pass SslPolicyErrors.RemoteCertificationNameMismatch error to the
// ServicePointManager.ServerCertificateValidationCallback method which will cause the SSL connectino
// to be rejected.
// By default the ServicePointManager.ServerCertificateValidationCallback = None which I guess means
// Schannel rejects the connection outright because of the mismatch error.
// You can create a callback method like this one and assign it to the ServerCertificateValidationCallback
// property so that you can do the validation yourself.
//
// The sender passed to this method is the WebRequest which contains the url used to hit the service.
// The certificate is also passed to this method and you can use ToString(true) to get a verbose listing
// of the cert that contains the Subject Alternative Names including the "IP Address=" fields.
// This method will get the hostname of of the URL and if it is an IP address it will check to make
// sure the cert contains the IP used and if so will return true (ignoring the sslPolicyErrors value.
// If the hostname is not an IP then it will check that the hostname is found in the cert and that
// the sslPolicyErrors contains the value of None before returning true.
// This way we still do full validation of a hostname url and ignore validation errors when using an IP.
Regex ipv4Validator = new Regex("((25[0-5]|(2[0-4]|1\\d|[1-9])?\\d)(\\.|$)){4}");
WebRequest request = (WebRequest)sender;
UriBuilder theuri = new UriBuilder(request.RequestUri);
String thecert = certificate.ToString(true);
bool containsIP = ipv4Validator.IsMatch(theuri.Host);
if (sslPolicyErrors == SslPolicyErrors.None)
logger.Info("Settings.InternalCallback() sslPolicyErrors = " + sslPolicyErrors.ToString());
else
{
logger.Info("Settings.InternalCallback() " + (containsIP ? "IP was used so ignoring " : "") + " sslPolicyErrors = " + sslPolicyErrors.ToString());
}
logger.Debug("Settings.InternalCallback() requesturi = " + request.RequestUri);
logger.Debug("Settings.InternalCallback() subject = " + certificate.Subject);
logger.Debug("Settings.InternalCallback() request host = " + theuri.Host);
logger.Debug("Settings.InternalCallback() certificate verbose = " + thecert);
// possible sslPolicyErrors are:
// None
// RemoteCertificateChainErrors
// RemoteCertificateNameMismatch
// RemoteCertificateNotAvailable
// If the request was sent to an IP then we will get a RemoteCertificateNameMismatch
// error so ignore it and just check that the IP is found in the cert.
if (containsIP)
return thecert.Contains(theuri.Host);
else
return thecert.Contains(theuri.Host) && sslPolicyErrors == SslPolicyErrors.None;
}
Schannel中传统的和错误需要将包含在DNSNAME代替ip地址字段:(。该IP地址,以便通过包括IP地址,它与两种类型的尝试,使其既SChannel中和标准的工作符合休息 –
所以如果我正确地理解你,你说我应该用DNS.3 = xxx.xxx.xxx.xxx和IP.1 = xxx.xxx.xxx.xx创建我的CSR。我会试试看。 – Richard
是的,这就是你应该做的 –