Contains the information required to create self signed certificates
Inheritance: IDisposable
Example #1
0
        /// <summary>
        /// Creates a self signed certificate which can be used for peer to peer authentication.
        /// Initial implementation used with permission from http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx
        /// </summary>
        /// <param name="certificateDetails">The certificate details to use.</param>
        /// <returns>PFX file data</returns>
        public static byte[] CreateSelfSignedCertificatePFX(CertificateDetails certificateDetails)
        {
            if (certificateDetails == null)
            {
                throw new ArgumentNullException("certificateDetails");
            }

            byte[] pfxData;

            SystemTime startSystemTime = ToSystemTime(certificateDetails.StartTime);
            SystemTime endSystemTime   = ToSystemTime(certificateDetails.EndTime);
            string     containerName   = Guid.NewGuid().ToString();

            GCHandle dataHandle       = new GCHandle();
            IntPtr   providerContext  = IntPtr.Zero;
            IntPtr   cryptKey         = IntPtr.Zero;
            IntPtr   certContext      = IntPtr.Zero;
            IntPtr   certStore        = IntPtr.Zero;
            IntPtr   storeCertContext = IntPtr.Zero;
            IntPtr   passwordPtr      = IntPtr.Zero;

            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                Check(NativeMethods.CryptAcquireContextW(
                          out providerContext,
                          containerName,
                          null,
                          1,   // PROV_RSA_FULL
                          8)); // CRYPT_NEWKEYSET

                Check(NativeMethods.CryptGenKey(
                          providerContext,
                          1,                                        // AT_KEYEXCHANGE
                          1 | (certificateDetails.KeyLength << 16), // CRYPT_EXPORTABLE
                          out cryptKey));

                IntPtr errorStringPtr;
                int    nameDataLength = 0;
                byte[] nameData;

                // errorStringPtr gets a pointer into the middle of the x500 string,
                // so x500 needs to be pinned until after we've copied the value
                // of errorStringPtr.
                dataHandle = GCHandle.Alloc(certificateDetails.X500, GCHandleType.Pinned);

                if (!NativeMethods.CertStrToNameW(
                        0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
                        dataHandle.AddrOfPinnedObject(),
                        3,          // CERT_X500_NAME_STR = 3
                        IntPtr.Zero,
                        null,
                        ref nameDataLength,
                        out errorStringPtr))
                {
                    string error = Marshal.PtrToStringUni(errorStringPtr);
                    throw new ArgumentException(error);
                }

                nameData = new byte[nameDataLength];

                if (!NativeMethods.CertStrToNameW(
                        0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
                        dataHandle.AddrOfPinnedObject(),
                        3,          // CERT_X500_NAME_STR = 3
                        IntPtr.Zero,
                        nameData,
                        ref nameDataLength,
                        out errorStringPtr))
                {
                    string error = Marshal.PtrToStringUni(errorStringPtr);
                    throw new ArgumentException(error);
                }

                dataHandle.Free();

                dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
                CryptoApiBlob nameBlob = new CryptoApiBlob(
                    nameData.Length,
                    dataHandle.AddrOfPinnedObject());

                CryptKeyProviderInformation kpi = new CryptKeyProviderInformation();
                kpi.ContainerName = containerName;
                kpi.ProviderType  = 1; // PROV_RSA_FULL
                kpi.KeySpec       = 1; // AT_KEYEXCHANGE

                certContext = NativeMethods.CertCreateSelfSignCertificate(
                    providerContext,
                    ref nameBlob,
                    0,
                    ref kpi,
                    IntPtr.Zero, // default = SHA1RSA
                    ref startSystemTime,
                    ref endSystemTime,
                    IntPtr.Zero);
                Check(certContext != IntPtr.Zero);
                dataHandle.Free();

                certStore = NativeMethods.CertOpenStore(
                    "Memory", // sz_CERT_STORE_PROV_MEMORY
                    0,
                    IntPtr.Zero,
                    0x2000, // CERT_STORE_CREATE_NEW_FLAG
                    IntPtr.Zero);
                Check(certStore != IntPtr.Zero);

                Check(NativeMethods.CertAddCertificateContextToStore(
                          certStore,
                          certContext,
                          1, // CERT_STORE_ADD_NEW
                          out storeCertContext));

                NativeMethods.CertSetCertificateContextProperty(
                    storeCertContext,
                    2, // CERT_KEY_PROV_INFO_PROP_ID
                    0,
                    ref kpi);

                if (certificateDetails.Password != null)
                {
                    passwordPtr = Marshal.SecureStringToCoTaskMemUnicode(certificateDetails.Password);
                }

                CryptoApiBlob pfxBlob = new CryptoApiBlob();
                Check(NativeMethods.PFXExportCertStoreEx(
                          certStore,
                          ref pfxBlob,
                          passwordPtr,
                          IntPtr.Zero,
                          7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY

                pfxData      = new byte[pfxBlob.DataLength];
                dataHandle   = GCHandle.Alloc(pfxData, GCHandleType.Pinned);
                pfxBlob.Data = dataHandle.AddrOfPinnedObject();
                Check(NativeMethods.PFXExportCertStoreEx(
                          certStore,
                          ref pfxBlob,
                          passwordPtr,
                          IntPtr.Zero,
                          7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
                dataHandle.Free();
            }
            finally
            {
                if (passwordPtr != IntPtr.Zero)
                {
                    Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr);
                }

                if (dataHandle.IsAllocated)
                {
                    dataHandle.Free();
                }

                if (certContext != IntPtr.Zero)
                {
                    NativeMethods.CertFreeCertificateContext(certContext);
                }

                if (storeCertContext != IntPtr.Zero)
                {
                    NativeMethods.CertFreeCertificateContext(storeCertContext);
                }

                if (certStore != IntPtr.Zero)
                {
                    NativeMethods.CertCloseStore(certStore, 0);
                }

                if (cryptKey != IntPtr.Zero)
                {
                    NativeMethods.CryptDestroyKey(cryptKey);
                }

                if (providerContext != IntPtr.Zero)
                {
                    NativeMethods.CryptReleaseContext(providerContext, 0);
                    NativeMethods.CryptAcquireContextW(
                        out providerContext,
                        containerName,
                        null,
                        1,     // PROV_RSA_FULL
                        0x10); // CRYPT_DELETEKEYSET
                }
            }

            return(pfxData);
        }
        /// <summary>
        /// Creates a self signed certificate which can be used for peer to peer authentication. 
        /// Initial implementation used with permission from http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx
        /// </summary>
        /// <param name="certificateDetails">The certificate details to use.</param>
        /// <returns>PFX file data</returns>
        public static byte[] CreateSelfSignedCertificatePFX(CertificateDetails certificateDetails)
        {
            if (certificateDetails == null) throw new ArgumentNullException("certificateDetails");

            byte[] pfxData;

            SystemTime startSystemTime = ToSystemTime(certificateDetails.StartTime);
            SystemTime endSystemTime = ToSystemTime(certificateDetails.EndTime);
            string containerName = Guid.NewGuid().ToString();

            GCHandle dataHandle = new GCHandle();
            IntPtr providerContext = IntPtr.Zero;
            IntPtr cryptKey = IntPtr.Zero;
            IntPtr certContext = IntPtr.Zero;
            IntPtr certStore = IntPtr.Zero;
            IntPtr storeCertContext = IntPtr.Zero;
            IntPtr passwordPtr = IntPtr.Zero;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                Check(NativeMethods.CryptAcquireContextW(
                    out providerContext,
                    containerName,
                    null,
                    1, // PROV_RSA_FULL
                    8)); // CRYPT_NEWKEYSET

                Check(NativeMethods.CryptGenKey(
                    providerContext,
                    1, // AT_KEYEXCHANGE
                    1 | (certificateDetails.KeyLength << 16), // CRYPT_EXPORTABLE
                    out cryptKey));

                IntPtr errorStringPtr;
                int nameDataLength = 0;
                byte[] nameData;

                // errorStringPtr gets a pointer into the middle of the x500 string,
                // so x500 needs to be pinned until after we've copied the value
                // of errorStringPtr.
                dataHandle = GCHandle.Alloc(certificateDetails.X500, GCHandleType.Pinned);

                if (!NativeMethods.CertStrToNameW(
                    0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
                    dataHandle.AddrOfPinnedObject(),
                    3, // CERT_X500_NAME_STR = 3
                    IntPtr.Zero,
                    null,
                    ref nameDataLength,
                    out errorStringPtr))
                {
                    string error = Marshal.PtrToStringUni(errorStringPtr);
                    throw new ArgumentException(error);
                }

                nameData = new byte[nameDataLength];

                if (!NativeMethods.CertStrToNameW(
                    0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
                    dataHandle.AddrOfPinnedObject(),
                    3, // CERT_X500_NAME_STR = 3
                    IntPtr.Zero,
                    nameData,
                    ref nameDataLength,
                    out errorStringPtr))
                {
                    string error = Marshal.PtrToStringUni(errorStringPtr);
                    throw new ArgumentException(error);
                }

                dataHandle.Free();

                dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
                CryptoApiBlob nameBlob = new CryptoApiBlob(
                    nameData.Length,
                    dataHandle.AddrOfPinnedObject());

                CryptKeyProviderInformation kpi = new CryptKeyProviderInformation();
                kpi.ContainerName = containerName;
                kpi.ProviderType = 1; // PROV_RSA_FULL
                kpi.KeySpec = 1; // AT_KEYEXCHANGE

                certContext = NativeMethods.CertCreateSelfSignCertificate(
                    providerContext,
                    ref nameBlob,
                    0,
                    ref kpi,
                    IntPtr.Zero, // default = SHA1RSA
                    ref startSystemTime,
                    ref endSystemTime,
                    IntPtr.Zero);
                Check(certContext != IntPtr.Zero);
                dataHandle.Free();

                certStore = NativeMethods.CertOpenStore(
                    "Memory", // sz_CERT_STORE_PROV_MEMORY
                    0,
                    IntPtr.Zero,
                    0x2000, // CERT_STORE_CREATE_NEW_FLAG
                    IntPtr.Zero);
                Check(certStore != IntPtr.Zero);

                Check(NativeMethods.CertAddCertificateContextToStore(
                    certStore,
                    certContext,
                    1, // CERT_STORE_ADD_NEW
                    out storeCertContext));

                NativeMethods.CertSetCertificateContextProperty(
                    storeCertContext,
                    2, // CERT_KEY_PROV_INFO_PROP_ID
                    0,
                    ref kpi);

                if (certificateDetails.Password != null)
                {
                    passwordPtr = Marshal.SecureStringToCoTaskMemUnicode(certificateDetails.Password);
                }

                CryptoApiBlob pfxBlob = new CryptoApiBlob();
                Check(NativeMethods.PFXExportCertStoreEx(
                    certStore,
                    ref pfxBlob,
                    passwordPtr,
                    IntPtr.Zero,
                    7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY

                pfxData = new byte[pfxBlob.DataLength];
                dataHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned);
                pfxBlob.Data = dataHandle.AddrOfPinnedObject();
                Check(NativeMethods.PFXExportCertStoreEx(
                    certStore,
                    ref pfxBlob,
                    passwordPtr,
                    IntPtr.Zero,
                    7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
                dataHandle.Free();
            }
            finally
            {
                if (passwordPtr != IntPtr.Zero)
                {
                    Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr);
                }

                if (dataHandle.IsAllocated)
                {
                    dataHandle.Free();
                }

                if (certContext != IntPtr.Zero)
                {
                    NativeMethods.CertFreeCertificateContext(certContext);
                }

                if (storeCertContext != IntPtr.Zero)
                {
                    NativeMethods.CertFreeCertificateContext(storeCertContext);
                }

                if (certStore != IntPtr.Zero)
                {
                    NativeMethods.CertCloseStore(certStore, 0);
                }

                if (cryptKey != IntPtr.Zero)
                {
                    NativeMethods.CryptDestroyKey(cryptKey);
                }

                if (providerContext != IntPtr.Zero)
                {
                    NativeMethods.CryptReleaseContext(providerContext, 0);
                    NativeMethods.CryptAcquireContextW(
                        out providerContext,
                        containerName,
                        null,
                        1, // PROV_RSA_FULL
                        0x10); // CRYPT_DELETEKEYSET
                }
            }

            return pfxData;
        }
Example #3
0
        public static void RunExample()
        {
            NetworkComms.ConnectionEstablishTimeoutMS = 600000;

            //Create a suitable certificate if it does not exist
            if (!File.Exists("testCertificate.pfx"))
            {
                CertificateDetails details = new CertificateDetails("CN=networkcomms.net", DateTime.Now, DateTime.Now.AddYears(1));
                SSLTools.CreateSelfSignedCertificatePFX(details, "testCertificate.pfx");
            }

            //Load the certificate
            X509Certificate cert = new X509Certificate2("testCertificate.pfx");

            IPAddress localIPAddress = IPAddress.Parse("::1");

            Console.WriteLine("Please select mode:");
            Console.WriteLine("1 - Server (Listens for connections)");
            Console.WriteLine("2 - Client (Creates connections to server)");

            //Read in user choice
            if (Console.ReadKey(true).Key == ConsoleKey.D1) serverMode = true;
            else serverMode = false;

            if (serverMode)
            {
                NetworkComms.AppendGlobalIncomingPacketHandler<byte[]>("Data", (header, connection, data) =>
                {
                    Console.WriteLine("Received data (" + data.Length + ") from " + connection.ToString());
                });

                //Establish handler
                NetworkComms.AppendGlobalConnectionEstablishHandler((connection) =>
                {
                    Console.WriteLine("Connection established - " + connection);
                });

                //Close handler
                NetworkComms.AppendGlobalConnectionCloseHandler((connection) =>
                {
                    Console.WriteLine("Connection closed - " + connection);
                });

                SSLOptions sslOptions = new SSLOptions(cert, true, true);
                TCPConnectionListener listener = new TCPConnectionListener(NetworkComms.DefaultSendReceiveOptions, 
                    ApplicationLayerProtocolStatus.Enabled, sslOptions);
                Connection.StartListening(listener, new IPEndPoint(localIPAddress, 10000), true);

                Console.WriteLine("\nListening for TCP (SSL) messages on:");
                foreach (IPEndPoint localEndPoint in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP)) 
                   Console.WriteLine("{0}:{1}", localEndPoint.Address, localEndPoint.Port);

                Console.WriteLine("\nPress any key to quit.");
                ConsoleKeyInfo key = Console.ReadKey(true);
            }
            else
            {
                ConnectionInfo serverInfo = new ConnectionInfo(new IPEndPoint(localIPAddress, 10000));

                SSLOptions sslOptions = new SSLOptions("networkcomms.net", true);
                //SSLOptions sslOptions = new SSLOptions(cert, true);

                TCPConnection conn = TCPConnection.GetConnection(serverInfo, NetworkComms.DefaultSendReceiveOptions, sslOptions);
                conn.SendObject("Data", sendArray);
                Console.WriteLine("Sent data to server.");

                Console.WriteLine("\nClient complete. Press any key to quit.");
                Console.ReadKey(true);
            }

            NetworkComms.Shutdown();
        }
        /// <summary>
        /// Creates a self signed certificate which can be used for peer to peer authentication and 
        /// saves it to disk using provided certificateFileName. Initial implementation used with permission from http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx
        /// </summary>
        /// <param name="certificateDetails">The certificate details to use.</param>
        /// <param name="certificateFileName">The certificate file name, e.g. certFile.PFX</param>
        public static void CreateSelfSignedCertificatePFX(CertificateDetails certificateDetails, string certificateFileName)
        {
            if (certificateDetails == null) throw new ArgumentNullException("certificateDetails");
            if (certificateFileName == null) throw new ArgumentNullException("certificateFileName");

            if (Path.GetExtension(certificateFileName).ToLower() != ".pfx")
                throw new ArgumentException("Provided certificateFileName must end in extension PFX.");

            //Get the certificate
            byte[] pfxData = CreateSelfSignedCertificatePFX(certificateDetails);

            //Save the certificate
            using (BinaryWriter binWriter = new BinaryWriter(File.Open(certificateFileName, FileMode.Create)))
            {
                binWriter.Write(pfxData);
            }
        }
        /// <summary>
        /// Run example
        /// </summary>
        public static void RunExample()
        {
            Console.WriteLine("Secure Socket Layer (SSL) Example ...\n");

            //Ensure we have a certificate to use
            //Maximum security is achieved by using a trusted certificate
            //To keep things simple here we just create a self signed certificate for testing
            string certName = "testCertificate.pfx";
            if (!File.Exists(certName))
            {
                Console.WriteLine("Creating self-signed test certificate - " + certName);
                CertificateDetails details = new CertificateDetails("CN=networkcomms.net", DateTime.Now, DateTime.Now.AddYears(1));

                //We could increase/decrease the default key length if we want
                details.KeyLength = 1024;

                //Save the certificate to disk
                SSLTools.CreateSelfSignedCertificatePFX(details, certName);
                certificate = new X509Certificate2(certName);
                Console.WriteLine("\t... certificate successfully created.");
            }
            else
            {
                //Load an existing certificate
                Console.WriteLine("Loading existing certificate - " + certName);
                certificate = new X509Certificate2(certName);
                Console.WriteLine("\t... certificate successfully loaded.");
            }

            //Add a global incoming packet handler for packets of type "Message"
            //This handler will convert the incoming raw bytes into a string (this is what 
            //the <string> bit means) and then write that string to the local console window.
            NetworkComms.AppendGlobalIncomingPacketHandler<string>("Message", (packetHeader, connection, incomingString) => 
            { 
                Console.WriteLine("\n  ... Incoming message from " + connection.ToString() + " saying '" + incomingString + "'."); 
            });

            //Create suitable SSLOptions to use with TCP
            SelectSSLOptions();

            //Get a list of all local endPoints using the default port
            List<IPEndPoint> desiredlocalEndPoints = (from current in HostInfo.IP.FilteredLocalAddresses()
                                                    select new IPEndPoint(current, 0)).ToList();

            //Create a list of matching TCP listeners where we provide the listenerSSLOptions
            List<ConnectionListenerBase> listeners = (from current in desiredlocalEndPoints
                                                      select (ConnectionListenerBase)(new TCPConnectionListener(NetworkComms.DefaultSendReceiveOptions, 
                                                           ApplicationLayerProtocolStatus.Enabled, 
                                                           listenerSSLOptions))).ToList();
            
            //Start listening for incoming TCP connections
            Connection.StartListening(listeners, desiredlocalEndPoints, true);

            //Print out the listening addresses and ports
            Console.WriteLine("\nListening for incoming TCP (SSL) connections on:");
            foreach (IPEndPoint localEndPoint in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP))
                Console.WriteLine("{0}:{1}", localEndPoint.Address, localEndPoint.Port);

            //We loop here to allow any number of test messages to be sent and received
            while (true)
            {
                //Request a message to send somewhere
                Console.WriteLine("\nPlease enter your message and press enter (Type 'exit' to quit):");
                string stringToSend = Console.ReadLine();

                //If the user has typed exit then we leave our loop and end the example
                if (stringToSend == "exit") break;
                else
                {
                    //Once we have a message we need to know where to send it
                    //We have created a small wrapper class to help keep things clean here
                    ConnectionInfo targetServerConnectionInfo = ExampleHelper.GetServerDetails();

                    try
                    {
                        //Get a connection to the target server using the connection SSL options we configured earlier
                        //If there is a problem with the SSL handshake this will throw a CommsSetupShutdownException
                        TCPConnection connection = TCPConnection.GetConnection(targetServerConnectionInfo,
                            sendingSendReceiveOptions,
                            connectionSSLOptions);

                        //Send our message of the encrypted connection
                        connection.SendObject("Message", stringToSend);
                    }
                    catch (CommsException)
                    {
                        //We catch all exceptions by using CommsException
                        Console.WriteLine("\nERROR - Connection to "+targetServerConnectionInfo+" was unsuccessful."+
                            " Server is either not listening or the entered SSL configurations"+
                            "are not compatible. Please check settings and try again.");
                    }
                }
            }

            //If we have used comms features we must gracefully shutdown
            NetworkComms.Shutdown();
        }