/// <summary> /// Establishes the SSL certificate we will use for communication with /// RPC clients and starts a seperate thread to listen for connections /// </summary> /// <param name="port">port to listen on</param> /// <param name="service">The KeePassRPCService the server should interact with.</param> public KeePassRPCServer(int port, KeePassRPCService service, KeePassRPCExt keePassRPCPlugin, bool useSSL) { _useSSL = useSSL; if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Starting KPRPCServer"); } Service = service; KeePassRPCPlugin = keePassRPCPlugin; if (_useSSL) { // if (true) if (Type.GetType("Mono.Runtime") == null) { _store = new System.Security.Cryptography.X509Certificates.X509Store(); _store.Open(OpenFlags.ReadWrite); // Find any certificates in this user's certificate store and re-use // them rather than suffer the overhead of creating an entirly new // certificate. Our certificates are considered "invalid" by the // store (probably becuase they are self-signed) X509Certificate2Collection matchingCertificates = _store.Certificates .Find(X509FindType.FindBySubjectDistinguishedName, "CN=KeePassRPC certificate for " + Environment.MachineName, false); //foreach (X509Certificate2 temp in matchingCertificates) // _store.Remove(temp); //matchingCertificates = _store.Certificates // .Find(X509FindType.FindBySubjectDistinguishedName, // "CN=KeePassRPC TLS aaa for " + Environment.MachineName, false); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Matching certificates from store: " + matchingCertificates.Count); } if (matchingCertificates.Count > 0) { _serverCertificate = matchingCertificates[0]; } else { //_serverCertificate = (X509Certificate2)X509Certificate2.CreateFromCertFile(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeePassRPC"), "cert.p12")); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Generating new certificate (MS)."); } // We can use the MakeCert feature from Mono to generate a new // certificate for use by this user on this machine. This means // that every KeePassRPC user will establish TLS connections // that are protected by a private key held on their own // system, rather than a key that is disclosed in this open // source code. NB: The local server is assumed to be secure! PKCS12 p12 = MakeCertKPRPC.Generate("KeePassRPC certificate for " + Environment.MachineName, "KeePassRPC Automated Self-Signed Key Generator", keePassRPCPlugin); byte[] cert = p12.GetBytes(); _serverCertificate = new X509Certificate2(cert, (string)null, X509KeyStorageFlags.PersistKeySet); _store.Add(_serverCertificate); } } else { /* * Problem 1: * For Linux/Mono, we cannot use the X509Store. It appears that only a .cer file is saved. That certificate does not include * the private key that we need for SSL. So we will need to save the key ourselves. * * Problem 2: * When using PKCS12 SaveToFile to save the key ourselves, it appears that it is not possible to save a private key that is not * password protected. * */ string certdir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeePassRPC"); string certfile = Path.Combine(certdir, "cert.p12"); _serverCertificate = null; // Check if cert directory exists, if not, we need to create it if (!System.IO.Directory.Exists(certdir)) { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Cert directory does not exist, creating " + certdir); } System.IO.Directory.CreateDirectory(certdir); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Cert directory created"); } } else { // Attempt to load cert try { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Looking for existing certificate (Mono)"); } _serverCertificate = new X509Certificate2(); _serverCertificate.Import(certfile, pkcs12_password, X509KeyStorageFlags.PersistKeySet); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Existing certificate loaded(Mono) : " + certfile); } } catch (Exception ex) { _serverCertificate = null; } } // If we didn't load a cert, create one and save it if (_serverCertificate == null) { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Generating new certificate (Mono)."); } PKCS12 p12 = MakeCertKPRPC.Generate("KeePassRPC certificate for " + Environment.MachineName, "KeePassRPC Automated Self-Signed Key Generator", pkcs12_password, keePassRPCPlugin); p12.SaveToFile(certfile); byte[] cert = p12.GetBytes(); _serverCertificate = new X509Certificate2(cert, pkcs12_password, X509KeyStorageFlags.PersistKeySet); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Generated new certificate (Mono) : " + certfile); } } } } if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Server certificate has private key? " + _serverCertificate.HasPrivateKey); } try { this._tcpListener = new TcpListener(IPAddress.Loopback, port); this._listenThread = new Thread(new ThreadStart(ListenForClients)); this._listenThread.Start(); this._isListening = true; // just in case the main thread checks // for successful startup before the thread has got going. } catch (Exception e) { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Failed to start TCP listener: " + e.ToString()); } } if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Started KPRPCServer"); } }
/// <summary> /// Establishes the SSL certificate we will use for communication with /// RPC clients and starts a seperate thread to listen for connections /// </summary> /// <param name="port">port to listen on</param> /// <param name="service">The KeePassRPCService the server should interact with.</param> public KeePassRPCServer(int port, KeePassRPCService service, KeePassRPCExt keePassRPCPlugin, bool useSSL) { _useSSL = useSSL; if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Starting KPRPCServer"); } Service = service; KeePassRPCPlugin = keePassRPCPlugin; if (_useSSL) { if (Type.GetType("Mono.Runtime") == null) { _store = new X509Store(); _store.Open(OpenFlags.ReadWrite); // Find any certificates in this user's certificate store and re-use // them rather than suffer the overhead of creating an entirly new // certificate. Our certificates are considered "invalid" by the // store (probably becuase they are self-signed) X509Certificate2Collection matchingCertificates = _store.Certificates .Find(X509FindType.FindBySubjectDistinguishedName, "CN=KeePassRPC certificate for " + Environment.MachineName, false); //foreach (X509Certificate2 temp in matchingCertificates) // _store.Remove(temp); //matchingCertificates = _store.Certificates // .Find(X509FindType.FindBySubjectDistinguishedName, // "CN=KeePassRPC TLS aaa for " + Environment.MachineName, false); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Matching certificates from store: " + matchingCertificates.Count); } if (matchingCertificates.Count > 0) { _serverCertificate = matchingCertificates[0]; } else { //_serverCertificate = (X509Certificate2)X509Certificate2.CreateFromCertFile(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeePassRPC"), "cert.p12")); if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Generating new certificate (MS)."); } // We can use the MakeCert feature from Mono to generate a new // certificate for use by this user on this machine. This means // that every KeePassRPC user will establish TLS connections // that are protected by a private key held on their own // system, rather than a key that is disclosed in this open // source code. NB: The local server is assumed to be secure! byte[] cert = MakeCertKPRPC.Generate("KeePassRPC certificate for " + Environment.MachineName, "KeePassRPC Automated Self-Signed Key Generator", keePassRPCPlugin); _serverCertificate = new X509Certificate2(cert, (string)null, X509KeyStorageFlags.PersistKeySet); _store.Add(_serverCertificate); } } else { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Looking for existing certificate (Mono)"); } _serverCertificate = (X509Certificate2)X509Certificate2.CreateFromCertFile(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeePassRPC"), "cert.p12")); if (_serverCertificate == null) { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Generating new certificate (Mono)."); } MakeCertKPRPC.Generate("KeePassRPC certificate for " + Environment.MachineName, "KeePassRPC Automated Self-Signed Key Generator", keePassRPCPlugin); _serverCertificate = (X509Certificate2)X509Certificate2.CreateFromCertFile(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeePassRPC"), "cert.p12")); } } } if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Server certificate has private key? " + _serverCertificate.HasPrivateKey); } try { this._tcpListener = new TcpListener(IPAddress.Loopback, port); this._listenThread = new Thread(new ThreadStart(ListenForClients)); this._listenThread.Start(); this._isListening = true; // just in case the main thread checks // for successful startup before the thread has got going. } catch (Exception e) { if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Failed to start TCP listener: " + e.ToString()); } } if (keePassRPCPlugin.logger != null) { keePassRPCPlugin.logger.WriteLine("Started KPRPCServer"); } }