我有一个应用程序,其中我们的许多端点不支持 DNS 查找,所以对于那些
他们无法使用 URL 访问我们的服务器的端点。
我们的应用程序给出了他们需要访问的我们服务器的 IP 列表,这对 http 工作正常。
我正在尝试启用 https 来访问我们的服务器,并且我已经创建了一个带有一些的 SAN 证书
url 作为主题备用名称加上我们服务器的 IP 作为主题备用名称。
例如在我用来创建 CSR 的 openssl.cnf 中:
DNS.1 = test.example.com
DNS.2 = test2.example.com
IP.1 = xxx.xxx.xxx.xxx
IP.2 = yyy.yyy.yyy.yyy
请注意,xxx 和 yyy 实际上是证书中的真实 IP 地址。
我们的 Java Web 服务客户端可以使用带有 IP 地址的 https 访问我们的服务器,没有问题。
我们的 .NET Web 服务客户端不能。
我正在为客户端使用 .NET Framework 3.5。
我正在使用 Tomcat 8 作为服务器。
.NET 客户端可以使用像 www.test.example.com 这样的comman name 和备用
test2.example.com 之类的名称,但如果我尝试使用 IP 地址,它会失败。
我从 system.diagnostics 启用了 Trace 以查看 SSL 握手提示的内容以及
我在来自服务器的一些数据中看到 DNS.x 的主题备用名称正在
发送到客户端但 IP 没有,因此无法在证书中找到 IP 作为替代。
[主题]
CN=www.test.example.com, O=Epicor Software Corporation, L=Austin, S=Texas,
C=US
简单名称:www.test.example.com
DNS 名称:test.example.com
[发行人]
CN=DigiCert SHA2 安全服务器 CA,O=DigiCert Inc,C=US
简单名称:DigiCert SHA2 安全服务器 CA
DNS 名称:DigiCert SHA2 安全服务器 CA
[序列号]
0DB4E110FDCE072E4D98F756B3D66B3C
[之前没有]
2016 年 3 月 27 日下午 7:00:00
[不在]
2017 年 4 月 19 日上午 7:00:00
[指纹]
6FBC98CA67D77121BC934E0A1AC5AB552EAB88ED
[签名算法]
sha256RSA(1.2.840.113549.1.1.11)
[公钥]
算法:RSA
长度:2048
key block :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 2 5 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 加州0a 3c 8d f6 e0 31 14 3a a3 d8 7b 97 7b 3d 98 80 3a d8 f6 76 ca....
ProcessId=20004
日期时间=2017-02-27T21:22:06.0846039Z
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 信息:0:[22244] SecureChannel#66166301 - 远程证书被用户验证为无效。
ProcessId=20004
日期时间=2017-02-27T21:22:06.1146042Z
System.Net.Sockets 详细:0:[22244] Socket#15688314::Dispose()
ProcessId=20004
日期时间=2017-02-27T21:22:06.1146042Z
System.Net 错误:0:[22244] HttpWebRequest 中的异常#35320229::- 基础连接已关闭:无法为 SSL/TLS 安全通道建立信任关系。
最佳答案
在 Steffens 的帮助下,我找到了 Schannel 和 IP 地址字段以及许多其他研究,我找到了这个问题的答案。
.NET 不支持直接将 IP 地址作为主题备用名称,因为它依赖于 Schannel 来验证证书中的 IP 地址。
Schannel 仅在证书的“DNS 名称=”字段中查找 https 请求中给出的主机名 (IP)。因此,如果您可以让 CA 为您提供“DNS 名称 =”字段中列出的 IP 地址的证书,那么它可能会起作用。 但是,我的 CA 不允许将 IP 地址放在“DNS 名称=”字段中,并要求它们放在“IP 地址=”字段中,因为这是现在的行业标准。
所以这意味着当 Schannel 在证书中找不到 IP 时,它将收到证书不匹配错误。
然后它会在 ServicePointManager.ServerCertificateValidationCallback 属性中查找要调用的方法,以要求用户验证证书,默认情况下此属性设置为 None。 所以它直接拒绝连接。
此处以 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;
}
关于.net - .NET Web 服务客户端是否支持在备用主题名称中使用 IP 的 SSL 证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42517028/