0

I'm working on a C# WinForms app to automate a manual TLS/SSL certificate request executed in the Windows cmd window. The CA provides a desktop client which communicates with the CA's servers which issue domain ownership "Challenges" and perform ownership verification.

Basically it is a three step process:

  1. CA issues challenges
  2. user creates DNS TXT records containing the challenges on the relevant domain
  3. CA checks for the records; if found a new SSL cert is issued

During Challenge issuance processing pauses for each domain listed in the request and waits for the user to press ENTER. After the final challenge is issued the next time ENTER is pressed it kicks off verification. Manually running the process in the CMD window results in

J:\Verify_DNS>le64 --key account.key --email "az@example.com" 
                    --csr example.csr --csr-key example.key 
                    --crt example.crt --generate-missing 
                    --domains "example.com,*.example.com"  
                    --handle-as dns
[ Crypt::LE64 client v0.38 started. ]
Challenge for example.com requires the following DNS record to be created:
Host: _acme-challenge.example.com, type: TXT, value:  
      cb4XNLh_ZnkwkuD7EiwlV9wk7qsP8QHLHUlQ2OO5DX8
Check for DNS propagation using: 
      nslookup -q=TXT _acme-challenge.example.com
When you see a text record returned, press <Enter>

Challenge for *.example.com requires the following DNS record to be created:
Host: _acme-challenge.example.com, type: TXT, value: 
      nUhw1Xy9H219nfiEx0vZuGDVbpe5KXuUenFoOlc3-4Q
Check for DNS propagation using:
      nslookup -q=TXT _acme-challenge.example.com
When you see a text record returned, press <Enter>

Processing the 'dns' verification
Verification result for 'example.com': success.
Verification result for '*.example.com': success.
Saving the full cert chain to example.crt.
The job is done.
J:\Verify_DNS>

Since DNS propagation times are unpredictable the process can be split into two steps by appending a "delay" flag to the argument. When split Step One of the process halts after issuing the challenges and returns control to the user.

Code running Step One and nslookup successfully

ProcessStartInfo si = new ProcessStartInfo();
si.FileName = @"C:\Windows\System32\cmd.exe";
si.RedirectStandardOutput = true;
si.RedirectStandardError = true;
si.RedirectStandardInput = true;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.WorkingDirectory = Terms.dir_DNS;
si.Verb = "runas";

using (Process proc = new Process())
{
    proc.StartInfo = si;
    proc.ErrorDataReceived += cmd_DataReceived_DNS;
    proc.OutputDataReceived += cmd_DataReceived_DNS;
    proc.EnableRaisingEvents = true;
    proc.Start();
    proc.PriorityClass = ProcessPriorityClass.RealTime;
    proc.BeginOutputReadLine();
    proc.BeginErrorReadLine();
    if (nslookup) proc.StandardInput.WriteLine(@"ipconfig /flushdns");
    proc.StandardInput.WriteLine(arg); 
    proc.StandardInput.WriteLine("exit");
    proc.WaitForExit();
}

In Step Two the process runs in its entirety from the top with the attendant pauses waiting for the user to press ENTER. To automate the process I need to "simulate/emulate" pressing ENTER in the cmd window.

This problem appears to be a show stopper; any ideas will be much appreciated.

[EDIT] removed confusing failed attempts inappropriately trying to input { ENTER } via SendKeys

Art Hansen
  • 57
  • 8
  • Is there not a NuGet package that can do this for you without using command line utilities? – ProgrammingLlama Oct 29 '22 at 10:12
  • Does writing `"\r\n"` work? – ProgrammingLlama Oct 29 '22 at 10:14
  • a quick test shows StandardOutput as multiple cmd prompts with no commands and no errors. just like you'd get pressing enter in the actual cmd window. I'll perform a full test but this looks very promising. – Art Hansen Oct 29 '22 at 11:14
  • `proc.StandardInput.WriteLine()` without arguments should do it. – Sardelka Oct 29 '22 at 12:42
  • See the notes here: [How do I get output from a command to appear in a control on a Form in real-time?](https://stackoverflow.com/a/51682585/7444103) and the code in the `KeyPress` event -- Better avoid `SendKeys.Send()` – Jimi Oct 29 '22 at 12:48
  • I think OP means he wants to simulate pressing enter in the CMD process. Not sure why `SendKeys` is used here (sending key to the form instead of the process), the question isn't very clear. – Sardelka Oct 29 '22 at 12:55
  • @Sardelka is correct, I missed that SendKeys is only for forms when reading the MSDN material on CMD and SendKey both of which use the { ENTER } syntax so I thought they were connected; apologies if that caused confusion. \n , \r and no arg at all result in communication with the CA completing as needed and avoids the app locking up. Note tho that the closing "exit" is not processed. Regarding NuGet only one SSL offering seemed relevant and it has zero documentation. If a comment is written up as an answer I can give the "solved" check mark. – Art Hansen Oct 29 '22 at 16:19

1 Answers1

1

Simply call proc.StandardInput.WriteLine() without any arguments, since pressing enter in a console window is just writing a line terminator to stdin, usually \r\n(Windows) or \n(*nix), aka CR/LF.

Since DNS propagation times are unpredictable the process can be split into two steps by appending a "delay" flag to the argument.

An ideal approach would be waiting the desired output and continue the execution. In this case, you may want to read the output synchronously instead using BeginOutputReadLine():

string output = null;

// Replace "completed_message" with the termination message you want, maybe "The job is done" here.
while(!(output = proc.StandardOutput.ReadLine()).Contains("completed_message"))
{
   ProcessOutput(output);
}

// continue execution or exit the process here
Sardelka
  • 354
  • 3
  • 9
  • I'm checking as solved since it answers my specific question and gets me one step closer to a full solution. With this a Cert can be issued. The caveat is that processing does not complete and the backgroundworker running the process therefore also will not progress to a completed state. Not sure what you mean with "waiting the desired output" - I have inserted Thread.Sleeps but they don't have any impact. Once the arg engages the CA server there's no opportunity to interrupt the process. – Art Hansen Oct 30 '22 at 15:18