.NET Web Service Client Supports SSL Certificate with IP Address in Subject Alternative Name

I have an application in which many of our endpoints do not support DNS queries, so for those endpoints they cannot use the URL to get to our servers. Our application lists the IP addresses of our servers that they need to hit, and this works great for http. I am trying to enable the use of https for our servers, and I created a SAN certificate with some urls as alternative subject names plus the IP addresses of our servers as alternative subject names.
For example, in openssl.cnf, which I used to create the CSR, I have:
DNS.1 = test.example.com
DNS.2 = test2.example.com
IP.1 = xxx.xxx.xxx.xxx
IP.2 = yyy.yyy.yyy.yyy

Note that xxx and yyy are actually valid IP addresses in the certificate.

Our Java web service clients can hit our servers using https with an IP address without any problems.
Our .NET Web Service client cannot. I am using the .NET framework 3.5 for the client.
I am using Tomcat 8 for the server.

The .NET client can hit it using the comman name, such as www.test.example.com and alternative names, like test2.example.com, but if I try to use the IP address, this will not work.
I turned on Trace from system.diagnostics to find out what the SSL handshake is associated with, and I see in some data coming from the server that the alternative object names for DNS.x are sent to the client, but there are no IP addresses, so the IP address cannot be found as an alternative in the certificate.

[Theme]
CN = www.test.example.com, O = Epicor Software Corporation, L = Austin, S = Texas,
C = USA
Simple name: www.test.example.com
DNS Name: test.example.com

[Issuer]
CN = DigiCert SHA2 Secure Server CA, O = DigiCert Inc, C = US
Simple name: DigiCert SHA2 Secure Server CA
DNS Name: DigiCert SHA2 Secure Server CA

[Serial number]
0DB4E110FDCE072E4D98F756B3D66B3C

[Not until]
07/27/2012 6:00:00

[Not after]
4/19/2017 7:00:00 AM

[Imprint]
6FBC98CA67D77121BC934E0A1AC5AB552EAB88ED

[Signature Algorithm]
sha256RSA (1.2.840.113549.1.1.11)

[Public key]
Algorithm: RSA
Length: 2048
Key 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: 0: [22244] SecureChannel # 66166301 - The remote certificate has errors:
ProcessId = 20004
DateTime = 2017-02-27T21: 22: 06.1146042Z
System.Net: 0: [22244] SecureChannel # 66166301 - certificate name mismatch.
ProcessId = 20004
DateTime = 2017-02-27T21: 22: 06.1146042Z
System.Net: 0: [22244] SecureChannel # 66166301 - The remote certificate was invalidated by the user.
ProcessId = 20004
DateTime = 2017-02-27T21: 22: 06.1146042Z
System.Net.Sockets Detail: 0: [22244] Socket # 15688314 :: Dispose ()
ProcessId = 20004
DateTime = 2017-02-27T21: 22: 06.1146042Z
System.Net error: 0: [22244] Exception in HttpWebRequest # 35320229 :: - The main connection was closed: the trust relationship for the SSL / TLS secure channel could not be established.

0
source share
1 answer

Well, with the help of Steffens showing me the Schannel and IP Address fields, and many other studies, I found the answer to this question.

.NET does not support IP addresses as alternative object names directly because it relies on Schannel to verify the IP address in the certificate.

Schannel only looks for the host name (IP) specified in the https request among the "DNS Name =" fields of the certificate. Therefore, if you can get a CA to provide you with a certificate with the IP addresses specified in the "DNS Name =" fields, this will probably work. However, my CA would not allow IP addresses to be placed in the "DNS Name =" fields and required them to be in the "IP Address =" fields, as this is now the industry standard.

So this means that Schannel will receive a certificate mismatch error if it cannot find the IP address in the certificate.

It then looks like the ServicePointManager.ServerCertificateValidationCallback property to invoke a method to ask the user to verify the certificate, which defaults to None for this property. Therefore, it rejects the connection directly.

Using C # as an example, you can create your own callback method and assign it to the ServicePointManager.ServerCertificateValidationCallback property. The method accepts the sending object, which is the WebRequest object used in the request, X509Certificate, which is the certificate, the X509Chain value, and the SslPolicyErrors value, which, if the IP address is used in the URL, will be a SslPolicyErrors.RemoteCertificationNameMismatch error.

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; } 
+2
source

Source: https://habr.com/ru/post/1275722/


All Articles