.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安全通道的信任关系。

+1

Schannel中传统的和错误需要将包含在DNSNAME代替ip地址字段:(。该IP地址,以便通过包括IP地址,它与两种类型的尝试,使其既SChannel中和标准的工作符合休息 –

+0

所以如果我正确地理解你,你说我应该用DNS.3 = xxx.xxx.xxx.xxx和IP.1 = xxx.xxx.xxx.xx创建我的CSR。我会试试看。 – Richard

+0

是的,这就是你应该做的 –

与斯蒂芬斯帮助指向我的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; 
    }