c# - net - Aplicación de consola de enmascaramiento de contraseña
generar codigo alfanumerico aleatorio vb net (14)
¡He actualizado la versión de Ronnie después de pasar demasiado tiempo intentando ingresar una contraseña solo para descubrir que tenía mi BLOQUEO DE CAPS!
Con esta versión, cualquiera que sea el mensaje en _CapsLockMessage
"flotará" al final del área de escritura y se mostrará en rojo.
Esta versión requiere un poco más de código y requiere un ciclo de votación. En el uso de CPU de mi computadora, aproximadamente del 3% al 4%, pero siempre se puede agregar un pequeño valor de Suspensión () para disminuir el uso de la CPU si es necesario.
private const string _CapsLockMessage = " CAPS LOCK";
/// <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 ReadLineMasked(char mask = ''*'')
{
// Taken from http://stackoverflow.com/a/19770778/486660
var consoleLine = new StringBuilder();
ConsoleKeyInfo keyInfo;
bool isDone;
bool isAlreadyLocked;
bool isCapsLockOn;
int cursorLeft;
int cursorTop;
ConsoleColor originalForegroundColor;
isDone = false;
isAlreadyLocked = Console.CapsLock;
while (isDone == false)
{
isCapsLockOn = Console.CapsLock;
if (isCapsLockOn != isAlreadyLocked)
{
if (isCapsLockOn)
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
originalForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0}", _CapsLockMessage);
Console.SetCursorPosition(cursorLeft, cursorTop);
Console.ForegroundColor = originalForegroundColor;
}
else
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
Console.Write("{0}", string.Empty.PadRight(_CapsLockMessage.Length));
Console.SetCursorPosition(cursorLeft, cursorTop);
}
isAlreadyLocked = isCapsLockOn;
}
if (Console.KeyAvailable)
{
keyInfo = Console.ReadKey(intercept: true);
if (keyInfo.Key == ConsoleKey.Enter)
{
isDone = true;
continue;
}
if (!char.IsControl(keyInfo.KeyChar))
{
consoleLine.Append(keyInfo.KeyChar);
Console.Write(mask);
}
else if (keyInfo.Key == ConsoleKey.Backspace && consoleLine.Length > 0)
{
consoleLine.Remove(consoleLine.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");
}
}
if (isCapsLockOn)
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
originalForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0}", _CapsLockMessage);
Console.CursorLeft = cursorLeft;
Console.CursorTop = cursorTop;
Console.ForegroundColor = originalForegroundColor;
}
}
}
Console.WriteLine();
return consoleLine.ToString();
}
Probé el siguiente código ...
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);
Pero de esta manera la funcionalidad de retroceso no funciona al escribir la contraseña. ¿Cualquier sugerencia?
Aquí está mi versión simple. Cada vez que presione una tecla, elimine todo de la consola y dibuje tantos ''*'' como la longitud de la cadena de contraseña.
int chr = 0;
string pass = "";
const int ENTER = 13;
const int BS = 8;
do
{
chr = Console.ReadKey().KeyChar;
Console.Clear(); //imediately clear the char you printed
//if the char is not ''return'' or ''backspace'' add it to pass string
if (chr != ENTER && chr != BS) pass += (char)chr;
//if you hit backspace remove last char from pass string
if (chr == BS) pass = pass.Remove(pass.Length-1, 1);
for (int i = 0; i < pass.Length; i++)
{
Console.Write(''*'');
}
}
while (chr != ENTER);
Console.Write("/n");
Console.Write(pass);
Console.Read(); //just to see the pass
El mío ignora los caracteres de control y maneja el ajuste de línea:
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();
}
Encontré un error en la solución vainilla C # 3.5 .NET de shermy que de lo contrario funciona con encanto. También incorporé Damian Leszczyński, la idea de Vash''s SecureString aquí, pero puedes usar una cadena común si lo prefieres.
EL ERROR: si presiona la tecla de retroceso durante la solicitud de contraseña y la longitud actual de la contraseña es 0, se insertará incorrectamente un asterisco en la máscara de contraseña. Para solucionar este error, modifique el siguiente método.
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;
}
Esto enmascara la contraseña con un cuadrado rojo, luego vuelve a los colores originales una vez que se ha ingresado la contraseña.
No impide que el usuario use copiar / pegar para obtener la contraseña, pero si se trata más bien de detener a alguien que mira por encima del hombro, esta es una buena solución rápida.
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;
Hice algunos cambios para el retroceso
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
{
pass = pass.Remove(pass.Length - 1);
Console.Write("/b /b");
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
La entrada de la consola de lectura es difícil, necesita manejar teclas especiales como Ctrl, Alt, también teclas de cursor y Retroceso / Eliminar. En algunos diseños de teclado, incluso se necesita Ctrl Swedish para ingresar claves que existen directamente en el teclado de EE. UU. Creo que tratar de manejar esto usando Console.ReadKey(true)
"bajo nivel" es muy difícil, así que la forma más fácil y más sólida es simplemente deshabilitar "echo de entrada de consola" al ingresar la contraseña usando un poco de WINAPI .
El ejemplo a continuación se basa en la respuesta a Leer una contraseña de la pregunta de 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;
}
Para esto debes usar 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
{
pwd.AppendChar(i.KeyChar);
Console.Write("*");
}
}
return pwd;
}
Podría agregar sus claves a una lista enlazada acumulada.
Cuando se recibe una tecla de retroceso, elimine la última clave de la lista.
Cuando reciba la tecla Intro, contraiga su lista en una cadena y haga el resto de su trabajo.
Si entiendo esto correctamente, estás tratando de hacer un retroceso, elimina el carácter visible * en la pantalla y el carácter en caché en tu variable de pase.
Si es así, simplemente cambie su bloque else a esto:
else
{
Console.Write("/b");
pass = pass.Remove(pass.Length -1);
}
Solución completa, vainilla C # .net 3.5+
Cortar pegar :)
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(''*'');
}
}
}
Tomando la respuesta principal, así como las sugerencias de sus comentarios, y modificándola para usar SecureString en lugar de String, pruebe todas las teclas de control, y no error, o escriba un "*" extra en la pantalla cuando la longitud de la contraseña sea 0, mi solución es:
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;
}
Console.Write("/b /b");
eliminará el carácter de asterisco de la pantalla, pero no tiene ningún código dentro de su bloque else
que elimine el carácter ingresado previamente de su variable de cadena de pass
.
Aquí está la porción del código relevante (la sección if..else
) que debería hacer lo que necesita:
// 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");
}
}
string pass = "";
Console.WriteLine("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace)
{
pass += key.KeyChar;
Console.Write("*");
}
else
{
Console.Write("/b /b");
char[] pas = pass.ToCharArray();
string temp = "";
for (int i = 0; i < pass.Length - 1; i++)
{
temp += pas[i];
}
pass = temp;
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);