Wednesday, September 8, 2021

How to read a secret from command line

Summary: A sample of C# code illustrating how to read a masked secret value entered via a console.
The following function can be used to read a secret value, such as a password from a command line:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/// <summary>
/// Reads a secret from command line masking the characters entered by the user.
/// </summary>
/// <param name="prompt">
/// Prompt to be displayed for the user (e.g. "Enter password: "). If not specified, the prompt will not be displayed.
///
/// <param name="mask">
/// The masking character.
///
/// <returns>
/// The string entered by the user.
/// </returns>
private static string GetSecret
(
    string prompt = null,
    char mask = '*'
)
{
    // Input codes requiring special handling.
    const int ENTER         = 13;
    const int BACKSPACE     = 8;
    const int CTRLBACKSPACE = 127;
    const int ESC           = 27;
 
    // Character codes that must be ignored.
    int[] FILTERED = { 0, 9, 10 /*, 32 space, if you care */ };
 
    var secret = new System.Collections.Generic.Stack<char>();
    char chr = (char)0;
 
    // If the prompt was specified, show it to the user.
    if (!String.IsNullOrEmpty(prompt))
      Console.Write(prompt);
     
    // Continue reading entered keys until user presses ENTER or ESC.
    while (((chr = System.Console.ReadKey(true).KeyChar) != ENTER) && (chr != ESC))
    {
        if (chr == BACKSPACE)
        {
            if (secret.Count > 0)
            {
                System.Console.Write("\b \b");
                secret.Pop();
            }
        }
        else if (chr == CTRLBACKSPACE)
        {
            while (secret.Count > 0)
            {
                System.Console.Write("\b \b");
                secret.Pop();
            }
        }
        else if (chr == ESC)
        {
            while (secret.Count > 0)
            {
                System.Console.Write("\b \b");
                secret.Pop();
            }
        }
        else if (FILTERED.Count(x => chr == x) > 0)
        {
        }
        else
        {
            secret.Push((char)chr);
            System.Console.Write(mask);
        }
    }
 
    System.Console.WriteLine();
 
    return new string(secret.Reverse().ToArray());
}
 
</char>