Password masking console application
Solution 1
Console.Write("\b \b");
will delete the asterisk character from the screen, but you do not have any code within your else
block that removes the previously entered character from your pass
string variable.
Here's the relevant working code that should do what you require:
var pass = string.Empty;
ConsoleKey key;
do
{
var keyInfo = Console.ReadKey(intercept: true);
key = keyInfo.Key;
if (key == ConsoleKey.Backspace && pass.Length > 0)
{
Console.Write("\b \b");
pass = pass[0..^1];
}
else if (!char.IsControl(keyInfo.KeyChar))
{
Console.Write("*");
pass += keyInfo.KeyChar;
}
} while (key != ConsoleKey.Enter);
Solution 2
For this you should use the 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;
}
Solution 3
Complete solution, vanilla C# .net 3.5+
Cut & Paste :)
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('*');
}
}
}
Solution 4
Taking the top answer, as well as the suggestions from its comments, and modifying it to use SecureString instead of String, test for all control keys, and not error or write an extra "*" to the screen when the password length is 0, my solution is:
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;
}
Solution 5
Mine ignores control characters and handles line wrapping:
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();
}
Related videos on Youtube
Mohammad Nadeem
Updated on August 04, 2021Comments
-
Mohammad Nadeem over 2 years
I tried the following code...
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);
But this way the backspace functionality doesn't work while typing the password. Any suggestion?
-
Ray Cheng over 11 yearsI suggest you do not echo anything back to the console because that'll expose the length of the password.
-
Stephen Holt about 10 years@RayCheng - fair enough, but very few user interfaces (other than on some Unix systems) echo nothing at all. For consistent user experience with other apps and websites, showing the * characters is probably best.
-
Ajedi32 over 7 years@StephenHolt I'm fairly certain every terminal-based password input I've ever encountered chose to echo nothing to the terminal. Given the security benefits and the fact that this is a well-known convention in the Unix world, I personally think echoing nothing is the right choice, unless you believe your user base is likely to be unfamiliar with the use of terminals (in which case it's probably best to use a GUI instead anyway).
-
-
Mohammad Nadeem over 13 yearsThis will only take me two places back. But what I need is that when I press Backspace the last character should be deleted. Just like the original functinality of backspace.
-
Mohammad Nadeem over 13 yearsSounds achievable but how will I remove the last character from the display.
-
Mohammad Nadeem over 13 yearsThis will work fine except that the deletion of the character by backspace will not be displayed.
-
Mohammad Nadeem over 13 yearsOh I thought \b \b will take me two places back. Nevertheless this seems to be working prfecttly.
-
Feidex over 13 years@Nadeem: Note the space character (
' '
) between the backspace characters ('\b'
)."\b \b"
takes you one place back, then prints a space (which takes you one place forward) and then takes you back again, so you end up where the deleted'*'
character was. -
DMpal Jain over 13 years@Nadeem - The first
\b
moves the cursor back one position (now underneath the last*
char. The[space]
character "prints over" the asterisk, but also moves the cursor one character forward again, so the last\b
moves the cursor back to where the last*
used to be! (Phew - Hope that makes sense!) -
Dead.Rabit almost 12 yearsI needed to nest
if( pwd.Length > 0)
into the first else statement to stop people deleting the question :) -
MemphiZ over 11 years
if (pass.Length > 0)
should beif (key.Key == ConsoleKey.Backspace && pass.Length > 0)
otherwise you will not get the last character of the password.. -
DMpal Jain over 11 years@MemphiZ - Good catch! Edited to reflect the fix.
-
Safron almost 9 yearsIf you don't want the user to be able to write control characters (like F5 or Escape), you could replace
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
withif (!char.IsControl(key.KeyChar))
. -
durron597 over 8 yearsThis answer does not add anything beyond what existing answers to. Additionally, good answers should typically explain the code, rather than just pasting code in to the answer box. Please read How to Answer
-
Peter Taylor over 8 yearsSimilarly to Safron's comment on the currently accepted answer, the final
else
clause would benefit from a testif (!char.IsControl(i.KeyChar))
(or at the very leastif (i.KeyChar != '\u0000')
). -
Alex Essilfie almost 7 yearsWorks perfectly. You may have to add code to make the
DELETE
character erase all entered text though. Its key sequence isCTRL + BACKSPACE
and its char code is0x7f
. -
Selman Genç over 6 yearsThis didn't work on me. It always left an asterisk at the end. I needed to add one space before the backspace to make it work:
Console.Write(" \b \b");
-
Hugo Logmans almost 6 yearsIt can always be more difficult :) Won't work on Mac/Linux because the newline is not recognised. Environment.NewLine has the string for a newline. So I modified this into: while (!Environment.NewLine.Contains(chr = System.Console.ReadKey(true).KeyChar))
-
mababin over 5 yearsThe only problem is that if you do backspace the background where you had characters stays red. I would rather go with setting ForegroundColor as origBG to have a Linux style password input.
-
mababin over 5 yearsYou could also do
Console.CursorVisible=false
and set it back to previous value after. This would prevent someone to peak at password length. -
Joseph Kreifels II over 5 yearsHow can I turn that password into a string?
-
plr108 almost 5 years
pass.Substring(0, (pass.Length - 1))
could be simplified topass.Substring(0, pass.Length - 1)
-
Amessihel over 4 years
-
jpaugh about 4 years@Amessihel While that document makes a good case for not using credentials, if you have to use them anyway, SecureString is better than nothing.
-
gmalenko almost 4 yearsI used this for a small project. Worked as expected. Thank you
-
SBF over 3 yearsHmm, what about backspace etc...?
-
Nabakamal Das almost 2 yearsWorks as promised.