1

How can I use a StreamSocket in a Windows 8 Metro app to connect to a Python+OpenSSL-based server using a self-signed certificate in the server ? I've tried adding the server's public key to the various trusted stores on the Windows Desktop to no avail. Everything I've tried yields the same results: an exception with the same message as in this post.

I'm developing with Visual Studio 2013 on Windows 8.1, connecting to a Python 3.4 server running OpenSSL 1.0.1h

Alex Marshall
  • 10,162
  • 15
  • 72
  • 117

1 Answers1

1

I was able to find the answer. Microsoft provides a sample here. Once you download the sample, check out scenario 5. Based on the code in that scenario, here's a Windows 8 App Store unit test class that provides an example of how to use the code:

namespace Services.Implementation.Tests
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Text;
    using Windows.Networking;
    using Windows.Networking.Sockets;
    using Windows.Storage.Streams;
    using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
    using Windows.Security.Cryptography.Certificates;
    using System.Collections.Generic;

    [TestClass]
    public class NetworkConnectionIPv6Tests
    {
        #region Test Lifecycle Members

        /// <summary>
        /// Gets or sets the test context.
        /// </summary>
        /// <value>
        /// The test context.
        /// </value>
        public TestContext TestContext
        {
            get; set;
        }

        #endregion // Test Lifecycle Members

        #region Test Methods

        [TestMethod, TestCategory("Integration")]
        public void TestConnection()
        {
            const int ServerPort = 63253;
            const string ServerIpAddress = "fe80::7ed1:c3ff:fed9:6fc7";
            HostName hostName = new HostName(ServerIpAddress);

            byte[] receiveBuffer = new byte[4096];

            using (StreamSocket streamSocket = new StreamSocket())
            {
                bool retry = true;

                do
                {
                    try
                    {
                        streamSocket.ConnectAsync(hostName, ServerPort.ToString(), SocketProtectionLevel.Tls12).GetAwaiter().GetResult();

                        string certInformation = GetCertificateInformation(
                            streamSocket.Information.ServerCertificate,
                            streamSocket.Information.ServerIntermediateCertificates);

                        Debug.WriteLine("Certificate information: {0}", certInformation);

                        retry = false;
                    }
                    catch (Exception exception)
                    {
                        // If this is an unknown status it means that the error is fatal and retry will likely fail.
                        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
                        {
                            throw;
                        }

                        // If the exception was caused by an SSL error that is ignorable we are going to prompt the user
                        // with an enumeration of the errors and ask for permission to ignore.
                        if (streamSocket.Information.ServerCertificateErrorSeverity != SocketSslErrorSeverity.Ignorable)
                        {
                            Debug.WriteLine("Connect failed with error: " + exception.Message);
                            Assert.Fail("Failed to avoid unignorable errors");
                        }

                        // ---------------------------------------------------------------------------
                        // WARNING: Only test applications may ignore SSL errors.
                        // In real applications, ignoring server certificate errors can lead to MITM
                        // attacks (while the connection is secure, the server is not authenticated).
                        // ---------------------------------------------------------------------------
                        streamSocket.Control.IgnorableServerCertificateErrors.Clear();
                        foreach (var ignorableError in streamSocket.Information.ServerCertificateErrors)
                        {
                            streamSocket.Control.IgnorableServerCertificateErrors.Add(ignorableError);
                        }
                    }

                } while (retry);

                byte[] messageBytes = Encoding.UTF8.GetBytes("Test");

                Stopwatch stopwatch = Stopwatch.StartNew();

                uint bytesSent = streamSocket.OutputStream.WriteAsync(messageBytes.AsBuffer()).GetAwaiter().GetResult();

                Assert.AreEqual(messageBytes.Length, (int) bytesSent, "Failed to sent the correct amount of bytes");

                IBuffer bytesReceived = streamSocket.InputStream.ReadAsync(receiveBuffer.AsBuffer(), (uint) receiveBuffer.Length, InputStreamOptions.None).GetAwaiter().GetResult();

                stopwatch.Stop();

                Debug.WriteLine("Remote call turnaround in {0} seconds", stopwatch.Elapsed.TotalSeconds);

                Assert.IsTrue(bytesReceived.Length > 0, "There were no bytes received from the server");

                string responseString = new string(Encoding.UTF8.GetChars(receiveBuffer, 0, (int) bytesReceived.Length));

                Assert.AreEqual("Test right back", responseString, "Failed to receive the expected message from the server");
            }
        }

        #endregion // Test Methods

        #region Helper Methods

        /// <summary>
        /// Gets detailed certificate information
        /// </summary>
        /// <param name="serverCert">The server certificate</param>
        /// <param name="intermediateCertificates">The server certificate chain</param>
        /// <returns>A string containing certificate details</returns>
        private string GetCertificateInformation(
            Certificate serverCert,
            IReadOnlyList<Certificate> intermediateCertificates)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("\tFriendly Name: " + serverCert.FriendlyName);
            sb.AppendLine("\tSubject: " + serverCert.Subject);
            sb.AppendLine("\tIssuer: " + serverCert.Issuer);
            sb.AppendLine("\tValidity: " + serverCert.ValidFrom + " - " + serverCert.ValidTo);

            // Enumerate the entire certificate chain.
            if (intermediateCertificates.Count > 0)
            {
                sb.AppendLine("\tCertificate chain: ");
                foreach (var cert in intermediateCertificates)
                {
                    sb.AppendLine("\t\tIntermediate Certificate Subject: " + cert.Subject);
                }
            }
            else
            {
                sb.AppendLine("\tNo certificates within the intermediate chain.");
            }

            return sb.ToString();
        }

        #endregion // Helper Methods
    }
}

The key to using this example is that you have to be using Visual Studio 2013 targeting for Windows 8.1.

Alex Marshall
  • 10,162
  • 15
  • 72
  • 117