コード例 #1
0
        int RunProcessPrintOutput(LogWindow log, string exe, string arguments)
        {
            log.WriteNormal(System.IO.Path.GetFileName(exe));
            log.WriteNormal(" ");
            log.WriteHighlight(arguments);
            log.WriteNormal("\n");

            ProcessStartInfo startInfo = new ProcessStartInfo(exe, arguments);

            startInfo.WorkingDirectory       = Environment.CurrentDirectory;
            startInfo.CreateNoWindow         = true;
            startInfo.UseShellExecute        = false;
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError  = true;

            var process = System.Diagnostics.Process.Start(startInfo);

            log.WriteTrace(process.StandardOutput.ReadToEnd());
            log.WriteTrace(process.StandardError.ReadToEnd());

            process.WaitForExit();

            log.WriteTrace(process.StandardError.ReadToEnd());
            log.WriteTrace(process.StandardOutput.ReadToEnd());

            log.WriteNormal("\n");

            return(process.ExitCode);
        }
コード例 #2
0
        private void RestoreFromBackup(object sender, EventArgs e)
        {
            var log = new LogWindow(MainWindow);

            var selectedItem = "/etc/openvpn/" + backupList.SelectedItem.ToString();

            using (var ssh = MainWindow.ConnectedDirector.ScpClient)
            {
                log.WriteNormal($"Connecting to director via SCP.. ");
                ssh.Connect();
                log.WriteSuccess($" .. connected!\n");

                // Get the existing certificate
                using (var stream = new MemoryStream())
                {
                    log.WriteNormal($"Downloading {selectedItem}\n");
                    ssh.Download(selectedItem, stream);
                    log.WriteSuccess($"Done - got {stream.Length} bytes\n\n");

                    stream.Position = 0;

                    using (StreamReader reader = new StreamReader(stream))
                    {
                        stream.Position = 0;
                        var certificate = reader.ReadToEnd();

                        RestoreCert(log, certificate);
                    }
                }
            }
        }
コード例 #3
0
        private void RestoreCert(LogWindow log, string certificate)
        {
            if (!certificate.Contains("BEGIN CERTIFICATE"))
            {
                log.WriteError("This file doesn't seem to be valid?");
                return;
            }

            using (var ssh = MainWindow.ConnectedDirector.ScpClient)
            {
                log.WriteNormal($"Connecting to director via SCP.. ");
                ssh.Connect();
                log.WriteSuccess($" .. connected!\n");

                // Get the existing certificate
                using (var stream = new MemoryStream())
                {
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        writer.Write(certificate);
                        writer.Flush();

                        stream.Position = 0;

                        log.WriteNormal($"Uploading /etc/openvpn/clientca-prod.pem\n");
                        ssh.Upload(stream, "/etc/openvpn/clientca-prod.pem");
                        log.WriteSuccess($"Done!\n");
                    }
                }
            }

            MainWindow.ConnectedDirector.Reboot(log);
            MainWindow.DirectorDisconnected();
        }
コード例 #4
0
        private void CopyFile(LogWindow log, string a, string b)
        {
            log.WriteNormal($"Copying ");
            log.WriteHighlight(a);
            log.WriteNormal($" to ");
            log.WriteHighlight(b);
            log.WriteNormal($"\n");

            System.IO.File.Copy(a, b, true);
        }
コード例 #5
0
        private void GenerateCertificates(object sender, EventArgs e)
        {
            var log = new LogWindow(MainWindow);

            log.WriteNormal("Generating Certificates\n");
            if (!GenerateCertificates(log))
            {
                return;
            }
            log.WriteSuccess("Certificate Generation Successful");
            log.WriteNormal("\n\n");
        }
コード例 #6
0
        private void JailbreakDirector(object sender, EventArgs e)
        {
            var log = new LogWindow(MainWindow);

            log.WriteNormal("Generating Certificates\n");
            if (!GenerateCertificates(log))
            {
                return;
            }
            log.WriteSuccess("Certificate Generation Successful");
            log.WriteNormal("\n\n");

            log.WriteNormal("Copying To Composer\n");
            if (!PatchComposer(log))
            {
                return;
            }
            log.WriteNormal("\n\n");

            log.WriteNormal("Copying To Director\n");
            if (!PatchDirector(log))
            {
                return;
            }
            log.WriteNormal("\n\n");

            log.WriteNormal("Restarting Director\n");
            if (!RestartDirector(log))
            {
                return;
            }
            log.WriteNormal("\n\n");

            log.WriteSuccess("100% All Done!");
        }
コード例 #7
0
        internal async void Reboot(LogWindow log)
        {
            log.WriteNormal($"\nRebooting Director\n\n");

            using (var ssh = SshClient)
            {
                log.WriteTrace($"\nConnecting via SSH.. ");

                ssh.Connect();

                log.WriteTrace($"\n .. connected!");

                log.WriteSuccess($"\n\nYour director is now rebooting. This can take a few minutes and it's a nervous wait - I know.\n");
                log.WriteSuccess($"But nothing we've done here will stop your director from booting up, so don't worry.\n");

                var cmd = ssh.CreateCommand("sysman reboot");

                var task = cmd.BeginExecute();

                while (!task.IsCompleted)
                {
                    await Task.Delay(10);
                }
            }
        }
コード例 #8
0
        bool RestartDirector(LogWindow log)
        {
            log.WriteNormal($"Rebooting..\n");

            MainWindow.ConnectedDirector.Reboot(log);
            MainWindow.DirectorDisconnected();

            return(true);
        }
コード例 #9
0
        private void PatchDirectorCertificates(object sender, EventArgs e)
        {
            var log = new LogWindow(MainWindow);

            try
            {
                log.WriteNormal("Copying To Director\n");
                if (!PatchDirector(log))
                {
                    return;
                }
                log.WriteNormal("\n\n");
            }
            catch (System.Exception ex)
            {
                log.WriteError(ex);
            }
        }
コード例 #10
0
        private void UpdateCertificates(object sender, EventArgs e)
        {
            var log = new LogWindow(MainWindow);

            try
            {
                log.WriteNormal("Copying To Composer\n");
                UpdateComposerCertificate(log);
            }
            catch (System.Exception ex)
            {
                log.WriteError(ex);
            }
        }
コード例 #11
0
ファイル: Composer.cs プロジェクト: rg911/Control4.Jailbreak
        private void PatchComposer(object sender, EventArgs eventargs)
        {
            var oldLine = "<setting name=\"ComposerPro_LicensingService_Licensing\" serializeAs=\"String\">";
            var newLine = "<setting name=\"ComposerPro_LicensingService_Licensing\" serializeAs=\"0\">";

            var log = new LogWindow(MainWindow);

            log.WriteTrace("Asking for ComposerPro.exe.config location\n");

            OpenFileDialog open = new OpenFileDialog();

            open.Filter           = "Config Files|*.config";
            open.Title            = "Find Original ComposerPro.exe.config";
            open.InitialDirectory = "C:\\Program Files (x86)\\Control4\\Composer\\Pro";
            open.FileName         = "ComposerPro.exe.config";

            if (open.ShowDialog() != DialogResult.OK)
            {
                log.WriteError("Cancelled\n");
                return;
            }

            if (string.IsNullOrEmpty(open.FileName))
            {
                log.WriteError("Filename was invalid\n");
                return;
            }

            log.WriteNormal("Opening ");
            log.WriteHighlight($"{open.FileName}\n");

            var contents = System.IO.File.ReadAllText(open.FileName);

            if (!contents.Contains(oldLine))
            {
                log.WriteHighlight("Couldn't find the line - probably already patched??");
                return;
            }

            log.WriteHighlight($"Writing Backup..\n");
            System.IO.File.WriteAllText(open.FileName + $".backup-{DateTime.Now.ToString( "yyyy-dd-M--HH-mm-ss" )}", contents);

            log.WriteHighlight($"Writing New File..\n");
            contents = contents.Replace(oldLine, newLine);

            System.IO.File.WriteAllText(open.FileName, contents);
            log.WriteHighlight($"Done!\n");
        }
コード例 #12
0
        bool PatchDirector(LogWindow log)
        {
            using (var ssh = MainWindow.ConnectedDirector.ScpClient)
            {
                log.WriteNormal($"Connecting to director via SCP.. ");
                ssh.Connect();
                log.WriteSuccess($" .. connected!\n");

                // Get the existing certificate
                using (var stream = new MemoryStream())
                {
                    log.WriteNormal($"Downloading /etc/openvpn/clientca-prod.pem\n");
                    ssh.Download("/etc/openvpn/clientca-prod.pem", stream);
                    log.WriteSuccess($"Done - got {stream.Length} bytes\n\n");

                    stream.Position = 0;

                    var backupName = $"/etc/openvpn/clientca-prod.{DateTime.Now.ToString( "yyyy-dd-M--HH-mm-ss" )}.backup";
                    log.WriteNormal($"Uploading {backupName}\n");
                    ssh.Upload(stream, backupName);
                    log.WriteSuccess($"Done!\n\n");

                    log.WriteNormal($"Constructing new clientca-prod.pem\n");
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        stream.Position = 0;

                        var certificate = reader.ReadToEnd();

                        certificate += "\n";

                        log.WriteNormal($"  Reading Certs/public.pem\n");
                        var localCert = System.IO.File.ReadAllText("Certs/public.pem");

                        if (certificate.Contains(localCert))
                        {
                            log.WriteError($"The certificate on the director already contains our public key!\n");
                        }
                        else
                        {
                            //
                            // We just add our public key to the end
                            //
                            certificate += localCert;
                        }

                        //
                        // This serves no purpose but it doesn't hurt to have it hanging around
                        //
                        log.WriteNormal($"  Writing to Certs/clientca-prod.pem\n");
                        System.IO.File.WriteAllText("Certs/clientca-prod.pem", certificate);


                        //
                        // Upload the modded certificates to the director
                        //
                        log.WriteNormal($"Uploading New Certificate..\n");
                        using (var wstream = new MemoryStream())
                        {
                            using (StreamWriter writer = new StreamWriter(wstream))
                            {
                                writer.Write(certificate);
                                writer.Flush();

                                wstream.Position = 0;
                                ssh.Upload(wstream, "/etc/openvpn/clientca-prod.pem");
                            }
                        }

                        log.WriteSuccess($"Done!\n");
                    }
                }
            }

            return(true);
        }
コード例 #13
0
        bool PatchDirector(LogWindow log)
        {
            var SshConnectionInfo = new ConnectionInfo(Address.Text.ToString(), Username.Text, new PasswordAuthenticationMethod(Username.Text, Password.Text));

            SshConnectionInfo.RetryAttempts = 1;
            SshConnectionInfo.Timeout       = TimeSpan.FromSeconds(2);

            using (var ssh = new ScpClient(SshConnectionInfo))
            {
                log.WriteNormal($"Connecting to director via SCP.. ");

                try
                {
                    ssh.Connect();
                }
                catch (System.Exception e)
                {
                    log.WriteError(e);
                    return(false);
                }

                log.WriteSuccess($" .. connected!\n");

                // Get the existing certificate
                using (var stream = new MemoryStream())
                {
                    log.WriteNormal($"Downloading /etc/openvpn/clientca-prod.pem\n");
                    ssh.Download("/etc/openvpn/clientca-prod.pem", stream);
                    log.WriteSuccess($"Done - got {stream.Length} bytes\n\n");

                    stream.Position = 0;

                    var backupName = $"/etc/openvpn/clientca-prod.{DateTime.Now.ToString( "yyyy-dd-M--HH-mm-ss" )}.backup";
                    log.WriteNormal($"Uploading {backupName}\n");
                    ssh.Upload(stream, backupName);
                    log.WriteSuccess($"Done!\n\n");

                    log.WriteNormal($"Constructing new clientca-prod.pem\n");
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        stream.Position = 0;

                        var certificate = reader.ReadToEnd();

                        certificate += "\n";

                        log.WriteNormal($"  Reading Certs/public.pem\n");
                        var localCert = System.IO.File.ReadAllText("Certs/public.pem");

                        var localBackupName = $"Certs/clientca-prod.{DateTime.Now.ToString( "yyyy-dd-M--HH-mm-ss" )}.backup";
                        log.WriteNormal($"  Downloading to {localBackupName}\n");
                        System.IO.File.WriteAllText(localBackupName, certificate);

                        if (certificate.Contains(localCert))
                        {
                            log.WriteError($"The certificate on the director already contains our public key!\n");
                            return(false);
                        }
                        else
                        {
                            //
                            // We just add our public key to the end
                            //
                            certificate += localCert;
                        }

                        //
                        // This serves no purpose but it doesn't hurt to have it hanging around
                        //
                        localBackupName += ".new";
                        log.WriteNormal($"  Downloading to {localBackupName}\n");
                        System.IO.File.WriteAllText(localBackupName, certificate);


                        //
                        // Upload the modded certificates to the director
                        //
                        log.WriteNormal($"Uploading New Certificate..\n");
                        using (var wstream = new MemoryStream())
                        {
                            using (StreamWriter writer = new StreamWriter(wstream))
                            {
                                writer.Write(certificate);
                                writer.Flush();

                                wstream.Position = 0;
                                ssh.Upload(wstream, "/etc/openvpn/clientca-prod.pem");
                            }
                        }

                        log.WriteSuccess($"Done!\n");
                    }
                }
            }

            return(true);
        }
コード例 #14
0
        bool GenerateCertificates(LogWindow log)
        {
            //
            // Don't regenerate the certificates. They might be copying the folder
            // over to another computer or some shit.
            //
            if (System.IO.File.Exists($"Certs/{Constants.ComposerCertName}") &&
                System.IO.File.Exists($"Certs/composer.p12") &&
                System.IO.File.Exists($"Certs/private.key") &&
                System.IO.File.Exists($"Certs/public.pem"))
            {
                log.WriteSuccess($"\nThe certificates already exist - so we're going to use them.\n");
                System.Threading.Thread.Sleep(1000);
                log.WriteSuccess($"If you want to generate new certificates delete the Certs folder.\n\n");
                System.Threading.Thread.Sleep(1000);
                return(true);
            }

            if (!System.IO.File.Exists(Constants.OpenSslExe))
            {
                log.WriteError($"Couldn't find {Constants.OpenSslExe} - do you have composer installed?");
                return(false);
            }

            if (!System.IO.File.Exists(Constants.OpenSslConfig))
            {
                log.WriteError($"Couldn't find {Constants.OpenSslConfig} - do you have composer installed?");
                return(false);
            }

            if (!System.IO.Directory.Exists("Certs"))
            {
                log.WriteTrace("Creating Certs Folder\n");
                System.IO.Directory.CreateDirectory("Certs");
            }

            //
            // generate a self signed private and public key
            //
            log.WriteNormal("\nGenerating private + public keys\n");
            var exitCode = RunProcessPrintOutput(log, Constants.OpenSslExe, $"req -new -x509 -sha256 -nodes -days {Constants.CertificateExpireDays} -newkey rsa:1024 -keyout \"Certs/private.key\" -subj \"/C=US/ST=Utah/L=Draper/O=Control4/OU=Controller Certificates/CN={Constants.CertificateCN}/\" -out \"Certs/public.pem\" -config \"{Constants.OpenSslConfig}\"");

            if (exitCode != 0)
            {
                log.WriteError($"Failed.");
                return(false);
            }

            //
            // Create the composer.p12 (public key) which sits in your composer config folder
            //
            log.WriteNormal("Creating composer.p12\n");
            exitCode = RunProcessPrintOutput(log, Constants.OpenSslExe, $"pkcs12 -export -out \"Certs/composer.p12\" -inkey \"Certs/private.key\" -in \"Certs/public.pem\" -passout pass:{Constants.CertPassword}");

            if (exitCode != 0)
            {
                log.WriteError($"Failed.");
                return(false);
            }

            //
            // Get the text for the composer cacert-*.pem
            //
            log.WriteNormal($"Creating {Constants.ComposerCertName}\n");
            var output = RunProcessGetOutput(Constants.OpenSslExe, $"x509 -in \"Certs/public.pem\" -text");

            System.IO.File.WriteAllText($"Certs/{Constants.ComposerCertName}", output);

            return(true);
        }
コード例 #15
0
        private void PatchComposer(object sender, EventArgs eventargs)
        {
            var log = new LogWindow(MainWindow);

            log.WriteTrace("Asking for ComposerPro.exe location\n");

            OpenFileDialog open = new OpenFileDialog();

            open.Filter           = "Executable Files|*.exe";
            open.Title            = "Find Original ComposerPro.exe";
            open.InitialDirectory = "C:\\Program Files (x86)\\Control4\\Composer\\Pro";
            open.FileName         = "ComposerPro.exe";

            if (open.ShowDialog() != DialogResult.OK)
            {
                log.WriteError("Cancelled\n");
                return;
            }

            if (string.IsNullOrEmpty(open.FileName))
            {
                log.WriteError("Filename was invalid\n");
                return;
            }

            log.WriteNormal("Opening ");
            log.WriteHighlight($"{open.FileName}\n");

            using (var val = AssemblyDefinition.ReadAssembly(open.FileName))
            {
                log.WriteTrace($"Finding \"Control4.ComposerPro.MainForm\"\n");

                var mainForm = val.MainModule.Types.SingleOrDefault(x => x.FullName == "Control4.ComposerPro.MainForm");
                if (mainForm == null)
                {
                    log.WriteError("Oops - couldn't find the class Control4.ComposerPro.MainForm\n");
                    return;
                }

                log.WriteNormal($"Found ");
                log.WriteHighlight($"{mainForm}\n");

                log.WriteTrace($"Finding \"ShowStartupOnFirstRun\"\n");

                var showStartupOnFirstRun = mainForm.Methods.SingleOrDefault(x => x.Name == "ShowStartupOnFirstRun");
                if (showStartupOnFirstRun == null)
                {
                    log.WriteError("Oops - couldn't find the method ShowStartupOnFirstRun (Maybe already patched?)\n");
                    return;
                }

                log.WriteNormal($"Found ");
                log.WriteHighlight($"{showStartupOnFirstRun}\n");

                log.WriteNormal($"Removing..");
                mainForm.Methods.Remove(showStartupOnFirstRun);
                log.WriteSuccess($" ..Done!\n");

                // TODO - Skip update check
                // TODO - Add info in window title
                // TODO - Fix dealeraccount.xml not found exception

                try
                {
                    var outFile = System.IO.Path.ChangeExtension(open.FileName, NewExtension);

                    //
                    // We might be re-patching for some reason, so delete the old one
                    //
                    if (System.IO.File.Exists(outFile))
                    {
                        System.IO.File.Delete(outFile);
                    }

                    log.WriteNormal($"Saving to ");
                    log.WriteHighlight($"{outFile}\n\n");

                    // Save the file
                    val.Write(outFile);

                    // We need to copy the config and manifest too, or we'll be missing a bunch of settings
                    log.WriteNormal($"Copying ");
                    log.WriteHighlight($"ComposerPro.exe.manifest");
                    log.WriteNormal($" to ");
                    log.WriteHighlight($"ComposerPro{NewExtension}.manifest\n");

                    System.IO.File.Copy($"{open.FileName}.manifest", $"{outFile}.manifest", true);

                    log.WriteNormal($"Copying ");
                    log.WriteHighlight($"ComposerPro.exe.config");
                    log.WriteNormal($" to ");
                    log.WriteHighlight($"ComposerPro{NewExtension}.config\n");

                    System.IO.File.Copy($"{open.FileName}.config", $"{outFile}.config", true);

                    // Create desktop shortcut
                    {
                        log.WriteNormal($"\nCreating Desktop Shortcut.. ");

                        var shortcutPath = $"{Environment.GetFolderPath( Environment.SpecialFolder.DesktopDirectory )}\\Composer Pro (Patched).url";

                        if (System.IO.File.Exists(shortcutPath))
                        {
                            System.IO.File.Delete(shortcutPath);
                        }

                        using (StreamWriter writer = new StreamWriter(shortcutPath))
                        {
                            var app = outFile.Replace('\\', '/');

                            writer.WriteLine("[InternetShortcut]");
                            writer.WriteLine($"URL=file:///{app.Replace( " ", "%20" )}");
                            writer.WriteLine("IconIndex=0");
                            writer.WriteLine("IconFile=" + app);
                            writer.Flush();
                        }

                        log.WriteSuccess($"done!\n");
                    }


                    log.WriteSuccess($"\nAll done - you can close this window!\n");
                }
                catch (UnauthorizedAccessException e)
                {
                    log.WriteError($"Exception - {e.Message}\n");
                }
            }
        }
コード例 #16
0
        private void PatchComposer(object sender, EventArgs eventargs)
        {
            var oldLine = @"  <system.net>
    <connectionManagement>
      <add address=""*"" maxconnection=""20"" />
    </connectionManagement>
  </system.net>";
            var newLine = @"   <system.net>
    <connectionManagement>
      <add address=""*"" maxconnection=""20"" />
    </connectionManagement>
    <defaultProxy>
      <proxy usesystemdefault=""false"" proxyaddress=""http://127.0.0.1:31337/"" bypassonlocal=""True""/>
    </defaultProxy>
  </system.net>

";

            var log = new LogWindow(MainWindow);

            log.WriteTrace("Asking for ComposerPro.exe.config location\n");

            OpenFileDialog open = new OpenFileDialog();

            open.Filter           = "Config Files|*.config";
            open.Title            = "Find Original ComposerPro.exe.config";
            open.InitialDirectory = "C:\\Program Files (x86)\\Control4\\Composer\\Pro";
            open.FileName         = "ComposerPro.exe.config";

            if (open.ShowDialog() != DialogResult.OK)
            {
                log.WriteError("Cancelled\n");
                return;
            }

            if (string.IsNullOrEmpty(open.FileName))
            {
                log.WriteError("Filename was invalid\n");
                return;
            }

            log.WriteNormal("Opening ");
            log.WriteHighlight($"{open.FileName}\n");

            var contents = System.IO.File.ReadAllText(open.FileName);

            if (!contents.Contains(oldLine))
            {
                log.WriteHighlight("Couldn't find the line - probably already patched??");
                return;
            }

            log.WriteHighlight($"Writing Backup..\n");
            System.IO.File.WriteAllText(open.FileName + $".backup-{DateTime.Now.ToString( "yyyy-dd-M--HH-mm-ss" )}", contents);

            log.WriteHighlight($"Writing New File..\n");
            contents = contents.Replace(oldLine, newLine);

            System.IO.File.WriteAllText(open.FileName, contents);
            log.WriteHighlight($"Done!\n");
        }
コード例 #17
0
        bool UpdateComposerCertificate(LogWindow log)
        {
            var configFolder = $"{Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData )}\\Control4\\Composer";

            log.WriteNormal("\nCreating new Composer Key\n");
            var exitCode = RunProcessPrintOutput(log, Constants.OpenSslExe, $"genrsa -out Certs/composer.key 1024 -config \"{Constants.OpenSslConfig}\"");

            if (exitCode != 0)
            {
                log.WriteError($"Failed.");
                return(false);
            }

            log.WriteNormal("\nCreating Signing Request\n");
            exitCode = RunProcessPrintOutput(log, Constants.OpenSslExe, $"req -new -nodes -key Certs/composer.key -subj /C=US/ST=Utah/L=Draper/CN={Constants.CertificateCN}/ -out Certs/composer.csr -config \"{Constants.OpenSslConfig}\"");

            if (exitCode != 0)
            {
                log.WriteError($"Failed.");
                return(false);
            }

            System.IO.Directory.CreateDirectory("ca");
            System.IO.Directory.CreateDirectory("ca/newcerts");
            System.IO.File.WriteAllText("ca/index.txt", "");

            log.WriteNormal("\nSigning Request\n");
            exitCode = RunProcessPrintOutput(log, Constants.OpenSslExe, $"ca -subj /C=US/ST=Utah/L=Draper/CN={Constants.CertificateCN}/ -preserveDN -days 365 -batch -create_serial -cert Certs/public.pem -keyfile Certs/private.key -out Certs/composer.pem -in Certs/composer.csr -config \"{Constants.OpenSslConfig}\"");

            if (exitCode != 0)
            {
                log.WriteError($"Failed.");
                return(false);
            }

            //
            // Create the composer.p12 (public key) which sits in your composer config folder
            //
            log.WriteNormal("Creating composer.p12\n");
            exitCode = RunProcessPrintOutput(log, Constants.OpenSslExe, $"pkcs12 -export -out \"Certs/composer.p12\" -inkey \"Certs/composer.key\" -in \"Certs/composer.pem\" -passout pass:{Constants.CertPassword}");

            if (exitCode != 0)
            {
                log.WriteError($"Failed.");
                return(false);
            }

            //
            // Get the text for the composer cacert-*.pem
            //
            log.WriteNormal($"Creating {Constants.ComposerCertName}\n");
            var output = RunProcessGetOutput(Constants.OpenSslExe, $"x509 -in \"Certs/public.pem\" -text");

            System.IO.File.WriteAllText($"Certs/{Constants.ComposerCertName}", output);

            CopyFile(log, $"Certs/{Constants.ComposerCertName}", $"{configFolder}\\{Constants.ComposerCertName}");
            CopyFile(log, $"Certs/composer.p12", $"{configFolder}\\composer.p12");

            log.WriteNormal("\n\n");
            log.WriteSuccess("Success - composer should be good for 30 days\n\n");
            log.WriteSuccess("Once it starts complaining that you had x days left to renew, just run this step again\n\n");
            log.WriteSuccess("You shouldn't need to patch your Director again unless you update to a new version or delete the Certs folder next to this exe.\n\n");

            return(true);
        }