HttpListener bug or developer issue?

I’m working on the following HttpListener (preview-4) application and no matter what combination of keys and certificates are used i get the exact same message:

CLR_E_INVALID_OPERATION (4)
GHIElectronics.TinyCLR.Devices.Network.Provider.NetworkControllerApiWrapper::AuthenticateAsServ

I have also created a very simple TcpClient that I can use to check/ignore “SslPolicyErrors” and unfortunately its been no help. https://github.com/microcompiler/socket-server/tree/master/samples/dotnet-client

Any advice would be greatly appreciated. I’ve burned more hours on this then I want to admit.

static void Main()
{
    Networking.SetupEthernet();

    // Using the following commands to create a certificate file
    // named "certificate.cer" (Certificate) and certificate.pkv (PrivateKey):
    // makecert.exe -r -pe -n "CN=tinyclr" -sky exchange -sv certificate.pkv certificate.cer
    // pvk2pfx -pvk certificate.pkv -spc certificate.cer -pfx certificate.pfx -pi password
    
    var certificate = Resources.GetBytes(Resources.BinaryResources.Certificate);
    var privateKey = Resources.GetBytes(Resources.BinaryResources.PrivateKey);

    var X509cert = new X509Certificate(certificate)
    {
        Password = "password",
        PrivateKey = privateKey
    };

    HttpsListener("https", X509cert);
}
public static void HttpsListener(string prefix, X509Certificate certificate)
{
    HttpListener listener = new HttpListener(prefix)
    {
        HttpsCert = certificate
    };

    listener.Start();
    Debug.WriteLine("Listening..");

    HttpListenerContext context = listener.GetContext();
    HttpListenerResponse response = context.Response;

    string responseString = "<HTML><BODY> Test </BODY></HTML>";
    byte[] buffer = Encoding.UTF8.GetBytes(responseString);

    response.ContentLength64 = buffer.Length;
    Stream output = response.OutputStream;
    output.Write(buffer, 0, buffer.Length);

    output.Close();
    listener.Stop();
}

we have a small test as server, not sure how it same as yours.

static void TestSocketSSLServer()
        {
            Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            var certificate = Ethernet_App.Properties.Resources.GetBytes(Properties.Resources.BinaryResources.MyServerOnly);
            var pkey = Ethernet_App.Properties.Resources.GetBytes(Properties.Resources.BinaryResources.MyPrivateKeyOnly);


            var cert = new X509Certificate(certificate);

            cert.PrivateKey = pkey;

            var serverCertificate = new X509Certificate[] { cert };

            listenSocket.Bind(new IPEndPoint(IPAddress.Any, 2000));

            listenSocket.Listen(1);

            while (true)
            {
                var clientSocket = listenSocket.Accept();

                System.Net.Security.SslStream sslStream = new System.Net.Security.SslStream(clientSocket);

                sslStream.AuthenticateAsServer(serverCertificate[0], System.Security.Authentication.SslProtocols.Tls12);

                System.Diagnostics.Debug.WriteLine("Verified OK...");

                while (sslStream.DataAvailable == false) ;

                var dataRead = new byte[1];
                string strRead = "";

                int bytes = -1;

                while (bytes != 0)
                {

                    bytes = sslStream.Read(dataRead, 0, dataRead.Length);
                    strRead += new string(UTF8Encoding.UTF8.GetChars(dataRead));
                    if (strRead.IndexOf("<EOF>") >= 0)
                    {
                        break;
                    }

                }


                System.Diagnostics.Debug.WriteLine(" Read: " + strRead);

                System.Diagnostics.Debug.WriteLine("write read...");

                string strSend = "Hello I am sitcore<EOF>";
                var dataSend = UTF8Encoding.UTF8.GetBytes(strSend);

                sslStream.Write(dataSend, 0, dataSend.Length);

                sslStream.Flush();

            }
        }

try to remove password for test???

Thank you! This is very helpful. Removing the password unfortunately did not help. Using your exact code any my certs also had the same error.

This make me think its how I’m building the certs that is the problem. Can you confirm for me that you are using certificate “.cer” files for the server cert and not personal information exchange “.pfx” files.

Here are my general thoughts on the process for creating the necessary files:

  • ServerCert.cer and CARoot.pkv certificate/private key is embedded in TinyCLR resource.
  • CARoot.cer certificate is installed installed in Local Machine Trusted Root Certification Authorities store on windows client machine.
  • ClientCert.cer certificate install in Current User store on on windows client machine.

Anything way off with this logic?

Hi, here is the script we used to generate / install certificates needed.

When we have time, we will review this mode again. But for now, when read the script, that may give you some idea.

@echo off
REM Certmgr.exe
REM makecert -r -pe -n "CN=My Root CA" -ss MyRootCA -sr CurrentUser -a sha256 -sky signature -cy authority -sv f:\MyRootCA.pvk f:\MyRootCA.cer
REM pvk2pfx -pvk f:\MyRootCA.pvk -spc f:\MyRootCA.cer -pfx f:\MyRootCA.pfx
REM certmgr.exe -add -all -c "f:\MyRootCA.pfx" -s -r CurrentUser Root
REM makecert -pe -n "CN=mytestserver" -a sha256 -sky Exchange -eku 1.3.6.1.5.5.7.3.1 -ic f:\MyRootCA.cer -iv f:\MyRootCA.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 f:\MyServer.cer -sv f:\MyServer.pvk
REM pvk2pfx -pvk f:\MyServer.pvk -spc f:\MyServer.cer -pfx f:\MyServer.pfx
REM openssl pkcs12 -in f:\MyServer.pfx -out f:\MyServerNew.pem -nodes

REM makecert -pe -n "CN=mytestclient" -ss my -sr CurrentUser -a sha256 -sp "Microsoft RSA SChannel Cryptographic Provider" -sky Exchange -eku 1.3.6.1.5.5.7.3.2 -in "MyRootCA" -ir CurrentUser -sy 12 f:\MyClient.cer
del -y f:\Out\*.*
SET MAKECER_DIR="C:\Program Files (x86)\Windows Kits\10\bin\x64"
SET OPENSSL_DIR="F:\OpenSSL-Win64\bin"
call %MAKECER_DIR%\makecert -r -pe -n "CN=My Root CA" -ss MyRootCA -sr CurrentUser -a sha1 -sky signature -cy authority -sv f:\Out\MyRootCA.pvk f:\Out\MyRootCA.cer
call %MAKECER_DIR%\pvk2pfx -pvk f:\Out\MyRootCA.pvk -spc f:\Out\MyRootCA.cer -pfx f:\Out\MyRootCA.pfx

call %MAKECER_DIR%\makecert -pe -n "CN=mytestserver" -a sha1 -sky Exchange -eku 1.3.6.1.5.5.7.3.1 -ic f:\Out\MyRootCA.cer -iv f:\Out\MyRootCA.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 f:\Out\MyServer.cer -sv f:\Out\MyServer.pvk
call %MAKECER_DIR%\pvk2pfx -pvk f:\Out\MyServer.pvk -spc f:\Out\MyServer.cer -pfx f:\Out\MyServer.pfx
call %OPENSSL_DIR%\openssl pkcs12 -in f:\Out\MyServer.pfx -nocerts -out f:\Out\MyServer_key.pem -nodes
call %OPENSSL_DIR%\openssl pkcs12 -in f:\Out\MyServer.pfx -nokeys -out f:\Out\MyServer.pem -nodes

REM call %MAKECER_DIR%\pvk2pfx -pvk f:\Out\MyServer.pvk -spc f:\Out\MyServer.cer -pfx f:\Out\MyServer.pfx

REM certmgr.exe -add -all -c "f:\Out\MyRootCA.cer" -s -r CurrentUser Root
1 Like

why do not use openssl instead to generate certs…

i forgot REM ano now i seen later

SET OPENSSL_DIR="F:\OpenSSL-Win64\bin"

:face_with_hand_over_mouth: :zipper_mouth_face:

Thanks! Your script was very helpful getting things working.

Annotation 2020-04-14 231146

A few things to note:

  1. I had to modify the the certs to include the Subject Alternative Name flag to use them with chrome and edge otherwise AuthenticateAsServer throws an exception. I found a nice powershell script on github that was simple to use and fixed the browser security errors.
  2. Don’t forget to update your local host file if you are not using DNS.
  3. SslStream works from both web browsers and desktop console application.
  4. The error handling could use some improvement as error messages are not very helpful.
4 Likes

Sweet! Now this time is your turn to teach us :)).

Could you please, somehow, step by step so we can document it?

1 Like

Here is a http/https socket project that includes some documentation on the cert setup and creation.

3 Likes

thanks a lot.