/// <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; }
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(); }