密码屏蔽控制台应用程序

Moh*_*eem 186 c# passwords user-input console-application masking

我尝试了以下代码......

string pass = "";
Console.Write("Enter your password: ");
ConsoleKeyInfo key;

do
{
    key = Console.ReadKey(true);

    // Backspace Should Not Work
    if (key.Key != ConsoleKey.Backspace)
    {
        pass += key.KeyChar;
        Console.Write("*");
    }
    else
    {
        Console.Write("\b");
    }
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);

Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
Run Code Online (Sandbox Code Playgroud)

但是这样输入密码时退格功能不起作用.有什么建议吗?

Cra*_*gTP 208

Console.Write("\b \b");将从屏幕上删除星号字符,但您的else块中没有任何代码可以从pass字符串变量中删除先前输入的字符.

以下是应该满足您需求的相关工作代码:

string pass = "";
do
{
    ConsoleKeyInfo key = Console.ReadKey(true);
    // Backspace Should Not Work
    if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
    {
        pass += key.KeyChar;
        Console.Write("*");
    }
    else
    {
        if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
        {
            pass = pass.Substring(0, (pass.Length - 1));
            Console.Write("\b \b");
        }
        else if(key.Key == ConsoleKey.Enter)
        {
            break;
        }
    }
} while (true);
Run Code Online (Sandbox Code Playgroud)

  • @Nadeem - 第一个`\ b`将光标向后移动一个位置(现在在最后一个`*`char下面.` [space]`字符'打印在"星号"上,但也将光标向前移动一个字符,所以最后一个`\ b`将光标移回到最后一个`*`的位置!(Phew - 希望有意义!) (14认同)
  • @Nadeem:注意退格字符(''\ b'`)之间的空格字符(`''`).`"\ b\b"`将你带回一个地方,然后打印一个空格(向前带你一个地方),然后再带你回去,这样你最终会删除被删除的''*'字符. (8认同)
  • 如果你不希望用户能够编写控制字符(如F5或Escape),你可以用`if替换`if(key.Key!= ConsoleKey.Backspace && key.Key!= ConsoleKey.Enter)` (!char.IsControl(key.KeyChar))`. (8认同)
  • `if(pass.Length> 0)`应该是`if(key.Key == ConsoleKey.Backspace && pass.Length> 0)`否则你不会得到密码的最后一个字符.. (3认同)
  • 哦,我认为\ b \ b将带我回到第二位。但是,这似乎很有效。 (2认同)

Dam*_*ash 88

为此,您应该使用System.Security.SecureString

public SecureString GetPassword()
{
    var pwd = new SecureString();
    while (true)
    {
        ConsoleKeyInfo i = Console.ReadKey(true);
        if (i.Key == ConsoleKey.Enter)
        {
            break;
        }
        else if (i.Key == ConsoleKey.Backspace)
        {
            if (pwd.Length > 0)
            {
                pwd.RemoveAt(pwd.Length - 1);
                Console.Write("\b \b");
            }
        }
        else if (i.KeyChar != '\u0000' ) // KeyChar == '\u0000' if the key pressed does not correspond to a printable character, e.g. F1, Pause-Break, etc
        {
            pwd.AppendChar(i.KeyChar);
            Console.Write("*");
        }
    }
    return pwd;
}
Run Code Online (Sandbox Code Playgroud)

  • [DE0001:不应使用 SecureString](https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md) (4认同)
  • @Amessihel 虽然该文档为不使用凭据提供了一个很好的案例,但如果您无论如何都必须使用它们,那么 SecureString 总比没有好。 (2认同)

she*_*rmy 45

完整的解决方案,香草C#.net 3.5+

剪切和粘贴:)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace ConsoleReadPasswords
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.Write("Password:");

                string password = Orb.App.Console.ReadPassword();

                Console.WriteLine("Sorry - I just can't keep a secret!");
                Console.WriteLine("Your password was:\n<Password>{0}</Password>", password);

                Console.ReadLine();
            }
        }
    }

    namespace Orb.App
    {
        /// <summary>
        /// Adds some nice help to the console. Static extension methods don't exist (probably for a good reason) so the next best thing is congruent naming.
        /// </summary>
        static public class Console
        {
            /// <summary>
            /// Like System.Console.ReadLine(), only with a mask.
            /// </summary>
            /// <param name="mask">a <c>char</c> representing your choice of console mask</param>
            /// <returns>the string the user typed in </returns>
            public static string ReadPassword(char mask)
            {
                const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127;
                int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const

                var pass = new Stack<char>();
                char chr = (char)0;

                while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER)
                {
                    if (chr == BACKSP)
                    {
                        if (pass.Count > 0)
                        {
                            System.Console.Write("\b \b");
                            pass.Pop();
                        }
                    }
                    else if (chr == CTRLBACKSP)
                    {
                        while (pass.Count > 0)
                        {
                            System.Console.Write("\b \b");
                            pass.Pop();
                        }
                    }
                    else if (FILTERED.Count(x => chr == x) > 0) { }
                    else
                    {
                        pass.Push((char)chr);
                        System.Console.Write(mask);
                    }
                }

                System.Console.WriteLine();

                return new string(pass.Reverse().ToArray());
            }

            /// <summary>
            /// Like System.Console.ReadLine(), only with a mask.
            /// </summary>
            /// <returns>the string the user typed in </returns>
            public static string ReadPassword()
            {
                return Orb.App.Console.ReadPassword('*');
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 它总是更难:)在Mac/Linux上无法工作,因为无法识别换行符.Environment.NewLine具有换行符的字符串.所以我将其修改为:while(!Environment.NewLine.Contains(chr = System.Console.ReadKey(true).KeyChar)) (3认同)

MDM*_*313 16

获取最佳答案,以及来自其注释的建议,并将其修改为使用SecureString而不是String,测试所有控制键,而不是错误或在密码长度为0时向屏幕写入额外的"*",我的解决方案是:

public static SecureString getPasswordFromConsole(String displayMessage) {
    SecureString pass = new SecureString();
    Console.Write(displayMessage);
    ConsoleKeyInfo key;

    do {
        key = Console.ReadKey(true);

        // Backspace Should Not Work
        if (!char.IsControl(key.KeyChar)) {
            pass.AppendChar(key.KeyChar);
            Console.Write("*");
        } else {
            if (key.Key == ConsoleKey.Backspace && pass.Length > 0) {
                pass.RemoveAt(pass.Length - 1);
                Console.Write("\b \b");
            }
        }
    }
    // Stops Receving Keys Once Enter is Pressed
    while (key.Key != ConsoleKey.Enter);
    return pass;
}
Run Code Online (Sandbox Code Playgroud)


Ron*_*rby 12

我忽略控制字符并处理换行:

public static string ReadLineMasked(char mask = '*')
{
    var sb = new StringBuilder();
    ConsoleKeyInfo keyInfo;
    while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter)
    {
        if (!char.IsControl(keyInfo.KeyChar))
        {
            sb.Append(keyInfo.KeyChar);
            Console.Write(mask);
        }
        else if (keyInfo.Key == ConsoleKey.Backspace && sb.Length > 0)
        {
            sb.Remove(sb.Length - 1, 1);

            if (Console.CursorLeft == 0)
            {
                Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
                Console.Write(' ');
                Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
            }
            else Console.Write("\b \b");
        }
    }
    Console.WriteLine();
    return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*h S 9

这会用红色方框掩盖密码,然后在输入密码后恢复为原始颜色.

它不会阻止用户使用复制/粘贴来获取密码,但如果它更像是阻止某人盯着你看,那么这是一个很好的快速解决方案.

Console.Write("Password ");
ConsoleColor origBG = Console.BackgroundColor; // Store original values
ConsoleColor origFG = Console.ForegroundColor;

Console.BackgroundColor = ConsoleColor.Red; // Set the block colour (could be anything)
Console.ForegroundColor = ConsoleColor.Red;

string Password = Console.ReadLine(); // read the password

Console.BackgroundColor= origBG; // revert back to original
Console.ForegroundColor= origFG;
Run Code Online (Sandbox Code Playgroud)

  • 支持独特性和独创性 (5认同)
  • 您还可以执行“Console.CursorVisible = false”并将其设置回之前的值。这可以防止有人达到密码长度的峰值。 (2认同)

Raf*_*łys 8

读取控制台输入很难,您需要处理特殊键,如Ctrl,Alt,光标键和退格/删除.在某些键盘布局上,甚至需要使用瑞典语 Ctrl来输入直接存在于美国键盘上的键.我相信尝试使用"低级别"处理这个问题Console.ReadKey(true)非常困难,因此最简单和最强大的方法是在使用一点WINAPI输入密码时禁用"控制台输入回显".

下面的示例基于从std :: cin问题中读取密码的答案.

    private enum StdHandle
    {
        Input = -10,
        Output = -11,
        Error = -12,
    }

    private enum ConsoleMode
    {
        ENABLE_ECHO_INPUT = 4
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetStdHandle(StdHandle nStdHandle);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int lpMode);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetConsoleMode(IntPtr hConsoleHandle, int dwMode);

    public static string ReadPassword()
    {
        IntPtr stdInputHandle = GetStdHandle(StdHandle.Input);
        if (stdInputHandle == IntPtr.Zero)
        {
            throw new InvalidOperationException("No console input");
        }

        int previousConsoleMode;
        if (!GetConsoleMode(stdInputHandle , out previousConsoleMode))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not get console mode.");
        }

        // disable console input echo
        if (!SetConsoleMode(stdInputHandle , previousConsoleMode & ~(int)ConsoleMode.ENABLE_ECHO_INPUT))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not disable console input echo.");
        }

        // just read the password using standard Console.ReadLine()
        string password = Console.ReadLine();

        // reset console mode to previous
        if (!SetConsoleMode(stdInputHandle , previousConsoleMode))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not reset console mode.");
        }

        return password;
    }
Run Code Online (Sandbox Code Playgroud)


Ste*_*son 6

我在shermy的vanilla C#3.5 .NET解决方案中发现了一个错误,否则它会起到一定作用.我还在这里收录了DamianLeszczyński - Vash的SecureString想法,但如果你愿意,你可以使用普通的字符串.

BUG:如果在密码提示期间按退格键并且当前密码长度为0,则密码掩码中会错误地插入星号.要修复此错误,请修改以下方法.

    public static string ReadPassword(char mask)
    {
        const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127;
        int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const


        SecureString securePass = new SecureString();

        char chr = (char)0;

        while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER)
        {
            if (((chr == BACKSP) || (chr == CTRLBACKSP)) 
                && (securePass.Length > 0))
            {
                System.Console.Write("\b \b");
                securePass.RemoveAt(securePass.Length - 1);

            }
            // Don't append * when length is 0 and backspace is selected
            else if (((chr == BACKSP) || (chr == CTRLBACKSP)) && (securePass.Length == 0))
            {
            }

            // Don't append when a filtered char is detected
            else if (FILTERED.Count(x => chr == x) > 0)
            {
            }

            // Append and write * mask
            else
            {
                securePass.AppendChar(chr);
                System.Console.Write(mask);
            }
        }

        System.Console.WriteLine();
        IntPtr ptr = new IntPtr();
        ptr = Marshal.SecureStringToBSTR(securePass);
        string plainPass = Marshal.PtrToStringBSTR(ptr);
        Marshal.ZeroFreeBSTR(ptr);
        return plainPass;
    }
Run Code Online (Sandbox Code Playgroud)


Dav*_*ond 6

(我的)nuget 包可以根据最佳答案执行此操作:

install-package PanoramicData.ConsoleExtensions

用法:

using PanoramicData.ConsoleExtensions;

...

Console.Write("Password: ");
var password = ConsolePlus.ReadPassword();
Console.WriteLine();
Run Code Online (Sandbox Code Playgroud)

项目网址: https: //github.com/panoramicdata/PanoramicData.ConsoleExtensions

欢迎拉请求。

  • 我用这个来做一个小项目。按预期工作。谢谢 (2认同)
  • 按承诺工作。 (2认同)

Sve*_*ckx 5

这是一个增加了对Escape键(返回一个null字符串)的支持的版本

public static string ReadPassword()
{
    string password = "";
    while (true)
    {
        ConsoleKeyInfo key = Console.ReadKey(true);
        switch (key.Key)
        {
            case ConsoleKey.Escape:
                return null;
            case ConsoleKey.Enter:
                return password;
            case ConsoleKey.Backspace:
                if (password.Length > 0) 
                {
                    password = password.Substring(0, (password.Length - 1));
                    Console.Write("\b \b");
                }
                break;
            default:
                password += key.KeyChar;
                Console.Write("*");
                break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)