SSL Certification works with localhost but not computer name or ip

16,012

Solution 1

The certificate warning that you are encountering is really a name mismatch error, which indicates that the common name (domain name) in the SSL certificate does not match the URL/address used to access the web site/server.

https://www.sslshopper.com/ssl-certificate-name-mismatch-error.html

In your usage scenario, you may want to transition away from localhost and ip addresses in favor of a simple domain model that leverages the computer name. (e.g. computerName.someDomain.com)

Then, you could obtain a wildcard certificate (e.g. *.someDomain.com) which could be used to authenticate inter-process communication.

https://www.sslshopper.com/best-ssl-wildcard-certificate.html

Solution 2

Your security guy is right. The way you are trying to make this happen wont work with SSL.

If you have a certificate, it is set to authenticate one CN. So, for an oversimplified example. Google has a certificate. It authenticates https://*.google.com. This means that any requests to google.com come up as having a valid certificate. And your browser is happy.

Now open a command prompt, ping google.com. Grab the ip address (in my case it came up as 216.58.210.14). Enter https://216.58.210.14. Your browser complains that the site is insecure etc. The reason being that the server may be the same one that served your earlier request, but the way you are getting to it is not valid according to the certificate,as the CN is not google.com, but an IP address.

So if you have a service which needs to connect to (for eg) 127.0.0.1, 10.92.1.4 AND myserver.com, you will need a cert which is valid for each case.

Solution 3

I am not sure what your "https://computerName:portNumber/applicationName/doSomething" means. So not sure if you are using the certificate right or whether the paths/connection that you are accessing or using in code are correct. https://technet.microsoft.com/en-us/library/dd891009.aspx

It can access any protocol not restricted to TCP. https://technet.microsoft.com/en-us/library/cc784450(v=ws.10).aspx

  • Wildcard SSL Certificates allow admins to secure an unlimited number of subdomains on the same domain with a single SSL Certificate. A WildCard SSL Certificate is issued to *.yourdomain.com giving cost savings benefits from having to purchase numerous SSL Certificates for each subdomain, and also only require the use of one IP address.
  • Typical SSL Certificates only secure one Fully Qualified Domain Name. Unified Communications certificates allow you to assign multiple host names—known as Subject Alternative Names or SANs—in one certificate. For total flexibility, a Multi-Domain SSL Certificate allows the use of the Subject Alternative Name (SAN) field so you can secure up to 100 different domain names, sub-domains, or public IP addresses using a single SSL Certificate and requiring just one IP address.

** The value must match exactly with the hostname or domain name you access. Otherwise you will still receive browser certificate errors after the certificate has been imported and trusted. Eg: If you were previously using http://101.10.10.1:9000/ to access the site and the common name of your new certificate is "mydomain", then the shortcut should be updated to http://mydomain:9000/ ; if you created a certificate for mydomain:9000. Second, Use the hostname or CNAME by which you will address the server. This is very important. If your web server’s real hostname is mysubdomain.mydomain.com but people will be using www.mydomain.com to address the box, then use the latter name to answer the “Common Name” (CNAME) question.

** Check if you are able to connect using the openssl s_time -connect remote.host:443 (use specific ssl commands to troubleshoot, I am citing openssl) or the openssl s_time -connect remote.host:443 -www /test.html -new ** If you don’t have an SSL-enabled web server available for your use, you can emulate one using the s_server option.

# on one host, set up the server (using default port 4433) openssl s_server -cert mycerti.pem -www # on second host (or even the same one), run s_time openssl s_time -connect mydomain:9001 -www / -new -ssl3

or

# on second host (or even the same one), run s_time openssl s_time -connect mydomain:9001 -www / -new -ssl3 ** Check if cert is in use? $ openssl s_client -connect [ip_or_dns_name]:[port] $ man s_client

Share:
16,012
Orkun Bekar
Author by

Orkun Bekar

Senior Software Developer

Updated on June 07, 2022

Comments

  • Orkun Bekar
    Orkun Bekar about 2 years

    We have a web application running on server and it posts http requests via XDomainRequest (because of IE9).

    There are lots of client computers which have a console application listening on a port via socket listener. Clients open web application with their IE9 browsers and when they click on a link, the web page sends requests like that:

    "https://localhost:portNumber/applicationName/doSomething" "https://computerName:portNumber/applicationName/doSomething" "https://ipAddress:portNumber/applicationName/doSomething"

    The second and third requests are made to console applications of other users' computers.

    The problem is that if requests come with localhost, console application does not have a problem about reading incoming data and sending response back. But if the request comes with computer name or ip address then browser shows certification warning and wants user to click on "Continue to this web site (not recommended)" link.

    We thought creating three different certificates via code. But even using sslstream with three of them is possible we cannot decide to select true certification because we make authenticatiton first and then receive data. So when we catch incoming request the authentication must already be done.

    Another way is forcing socket listener or sslstream to behave all these three requests as if they are localhost. So for each one authentication will be made as localhost. But I could not find an actual way for that.

    Here is the code. I give the code because maybe there is some wrong usage of SslStream.

    using System;
    using System.Net.Sockets;
    using System.Net;
    using System.Configuration;
    using System.Security.Cryptography.X509Certificates;
    using System.Windows.Forms;
    using System.IO;
    using System.Net.Security;
    using System.Security.Authentication;
    using System.Threading;
    using System.Text;
    
    namespace StackOverFlowProject
    {
        class StackOverFlowSample
        {
            private static ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
            private static X509Certificate _cert = null;
    
            static void Main(string[] args)
            {
                StackOverFlowSample stackOverFlowSample = new StackOverFlowSample();
                stackOverFlowSample.StartListening();
            }
    
            private void StartListening()
            {
                GetCertificate();
    
                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 1234);
    
                if (localEndPoint != null)
                {
                    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                    if (listener != null)
                    {
                        listener.Bind(localEndPoint);
                        listener.Listen(10);
    
                        Console.WriteLine("Socket listener is running. Waiting for requests...");
    
                        listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                    }
                }
            }
    
            private static void GetCertificate()
            {
                byte[] pfxData = File.ReadAllBytes(Application.StartupPath + @"\" + "localhost.pfx");
    
                _cert = new X509Certificate2(pfxData, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
            }
    
    
            private void AcceptCallback(IAsyncResult result)
            {
                Socket listener = null;
                Socket handler = null;
                StateObject state = null;
                SslStream sslStream = null;
    
                _manualResetEvent.Set();
    
                listener = (Socket)result.AsyncState;
    
                handler = listener.EndAccept(result);
    
                state = new StateObject();
    
                if (handler.RemoteEndPoint != null)
                {
                    state.clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString();
                }
    
                sslStream = new SslStream(new NetworkStream(handler, true));
                sslStream.AuthenticateAsServer(_cert, false, SslProtocols.Tls, true);
    
                sslStream.ReadTimeout = 100000;
                sslStream.WriteTimeout = 100000;
    
                state.workStream = sslStream;
    
                if (state.workStream.IsAuthenticated)
                {
                    state.workStream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state);
                }
    
                listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
            }
    
            private void ReceiveCallback(IAsyncResult result)
            {
                StateObject stateObject = null;
                SslStream sslStreamReader = null;
    
                byte[] byteData = null;
    
                stateObject = (StateObject)result.AsyncState;
                sslStreamReader = stateObject.workStream;
    
                int byteCount = sslStreamReader.EndRead(result);
    
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(stateObject.buffer, 0, byteCount)];
                decoder.GetChars(stateObject.buffer, 0, byteCount, chars, 0);
                stateObject.sb.Append(chars);
    
                if (byteCount > 0)
                {
                    stateObject.totalReceivedBytes += byteCount;
    
                    string[] lines = stateObject.sb.ToString().Split('\n');
    
                    if (lines[lines.Length - 1] != "<EOF>")
                    {
                        // We didn't receive all data. Continue reading...
                        sslStreamReader.BeginRead(stateObject.buffer, 0, stateObject.buffer.Length, new AsyncCallback(ReceiveCallback), stateObject);
                    }
                    else
                    {
                        Console.WriteLine("We received all data. Sending response...");
    
                        byteData = Encoding.UTF8.GetBytes("Hello! I received your request!");
    
                        string httpHeaders = "HTTP/1.1" + "\r\n"
                                        + "Cache-Control: no-cache" + "\r\n"
                                        + "Access-Control-Allow-Origin: *" + "\r\n"
                                        + "\r\n";
    
                        byte[] byteHttpHeaders = Encoding.UTF8.GetBytes(httpHeaders);
    
                        byte[] concat = new byte[byteHttpHeaders.Length + byteData.Length];
                        Buffer.BlockCopy(byteHttpHeaders, 0, concat, 0, byteHttpHeaders.Length);
                        Buffer.BlockCopy(byteData, 0, concat, byteHttpHeaders.Length, byteData.Length);
    
                        stateObject.sslStreamReader = sslStreamReader;
    
                        sslStreamReader.BeginWrite(concat, 0, concat.Length, new AsyncCallback(SendCallback), stateObject);
                    }
                }
            }
    
            private void SendCallback(IAsyncResult ar)
            {
                SslStream sslStreamSender = null;
    
                StateObject stateObject = (StateObject)ar.AsyncState;
    
                sslStreamSender = stateObject.sslStreamReader;
                sslStreamSender.EndWrite(ar);
    
                Console.WriteLine(stateObject.totalReceivedBytes.ToString() + " bytes sent to " + stateObject.clientIP + " address");
    
                sslStreamSender.Close();
                sslStreamSender.Dispose();
            }
    
        }
    
        public class StateObject
        {
            public SslStream workStream = null;
    
            public SslStream sslStreamReader = null;
    
            public const int BufferSize = 1024;
            public byte[] buffer = new byte[BufferSize];
            public StringBuilder sb = new StringBuilder();
    
            public string clientIP = "";
    
            public int totalReceivedBytes = 0;
        }
    }
    
  • Orkun Bekar
    Orkun Bekar over 8 years
    Do you mean we need three different certification file or we can create a certification file which has these three options inside?
  • Orkun Bekar
    Orkun Bekar over 8 years
    Your last sentence confused me. The rest is OK.
  • chris.ellis
    chris.ellis over 8 years
    A certificate can, by definition, only work for one domain. So you would need one for each. You might be able to work around this by adding entries to your hosts file which maps an IP to a particular domain, but you would have to do that on every users machine.
  • Joel C
    Joel C over 8 years
    This isn't technically correct. You can have a certificate that is valid for multiple domain names, generally by using the SAN (subject alternate name) field in the cert. This is often used by shared hosting environments, but generally costs more.
  • Orkun Bekar
    Orkun Bekar over 8 years
    @JoelC can we make cert file to be valid for both localhost and local ip address? But we won't write ip address by hand. The cert file will be distributed to a lot of machines and it will recognize each computers local ip and it will authenticate for localipaddress. Is that possible?
  • blowdart
    blowdart over 8 years
    No commercial CA will issue a certificate for an IP address or for localhost.