Parola maskeleme konsolu uygulaması


203

Aşağıdaki kodu denedim ...

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);

Ancak bu şekilde parola yazılırken backspace işlevi çalışmaz. Herhangi bir öneri?


11
Parola uzunluğunu açığa çıkaracağından konsola hiçbir şey geri göndermemenizi öneririm.
Ray Cheng

8
@RayCheng - yeterince adil, ancak çok az kullanıcı arabirimi (bazı Unix sistemlerinden başka) hiçbir şey yankılanmıyor. Diğer uygulamalar ve web siteleriyle tutarlı kullanıcı deneyimi için, * karakterlerini göstermek muhtemelen en iyisidir.
Stephen Holt

4
@StephenHolt Şimdiye kadar karşılaştığım her terminal tabanlı şifre girişinin terminale hiçbir şey yapmamayı seçtiğinden oldukça eminim. Güvenlik yararları ve bunun Unix dünyasında iyi bilinen bir kongre olduğu gerçeği göz önüne alındığında, kullanıcı tabanınızın terminallerin kullanımına aşina olmayacağına inanmıyorsanız, kişisel olarak hiçbir şeyin yankılanmasının doğru bir seçim olduğunu düşünmüyorum (bu durumda muhtemelen bunun yerine bir GUI kullanmak en iyisidir).
Ajedi32

Yanıtlar:


227

Console.Write("\b \b");yıldız karakterini ekrandan siler, ancak elsebloğunuzda önceden girilen karakteri passdize değişkeninizden kaldıran herhangi bir kodunuz yoktur .

İhtiyacınız olan şeyi yapmanız gereken ilgili çalışma kodu:

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);

2
Oh \ b \ b beni iki yere geri götüreceğini düşündüm. Bununla birlikte, bu mükemmel bir şekilde çalışıyor gibi görünüyor.
Mohammad Nadeem

8
@Nadeem: Geri ' 'silme karakterleri ( '\b') arasındaki boşluk karakterini ( ) not edin. "\b \b"sizi bir yere geri götürür, sonra bir boşluk basar (bu da sizi bir adım ileri götürür) ve sonra sizi tekrar geri götürür, böylece silinen '*'karakterin olduğu yere gelirsiniz .
dtb

14
@Nadeem - İlk önce \bimleci bir konum geri taşır (şimdi son *karakterin altındadır .) [space]Yıldız işaretini "yazdırır", ancak imleci tekrar bir karakter ileri \btaşır , böylece son imleci son *kullanılana geri taşır be! (Vay be - Mantıklı geliyor!)
CraigTP

3
if (pass.Length > 0)olmalıdır if (key.Key == ConsoleKey.Backspace && pass.Length > 0), aksi takdirde şifre son karakterini almazsınız ..
MemphiZ

9
Kullanıcının kontrol karakterleri yazmasını istemiyorsanız (F5 veya Escape gibi) if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)ile değiştirebilirsiniz if (!char.IsControl(key.KeyChar)).
Safron

90

Bunun için System.Security.SecureString öğesini kullanmalısınız

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;
}

Bu beni sadece iki yere geri götürecek. Ama ihtiyacım olan şey Backspace tuşuna bastığımda son karakterin silinmesi. Tıpkı geri almanın orijinal işlevi gibi.
Mohammad Nadeem

1
if( pwd.Length > 0)İnsanların soruyu silmelerini durdurmak için başka bir ilk ifadeye yerleştirmem gerekiyor :)
Dead.Rabit

1
Safron'un şu anda kabul edilen cevap hakkındaki yorumuna benzer şekilde, son elsefıkra bir testten if (!char.IsControl(i.KeyChar))(veya en azından if (i.KeyChar != '\u0000')) yararlanabilir.
Peter Taylor

1
Bu şifreyi bir dizeye nasıl dönüştürebilirim?
Joseph Kreifels II


47

Komple çözüm, vanilya C # .net 3.5+

Kes yapıştır :)

    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('*');
            }
        }
    }

3
Her zaman daha zor olabilir :) Mac / Linux üzerinde çalışmaz, çünkü yeni satır tanınmaz. Environment.NewLine, yeni satır için bir dizeye sahiptir. Bu yüzden bunu şu şekilde değiştirdim: while (! Environment.NewLine.Contains (chr = System.Console.ReadKey (true) .KeyChar))
Hugo Logmans

19

En iyi yanıtı ve yorumlarından gelen önerileri almak ve String yerine SecureString kullanmak için değiştirmek, tüm kontrol tuşlarını test etmek ve şifre uzunluğu 0 olduğunda ekrana fazladan bir "*" yazmamak, benim çözümüm:

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;
}

15

Mine kontrol karakterlerini yok sayar ve satır kaydırmayı işler:

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();
}

Mükemmel çalışıyor. DELETE Karakterin girilen tüm metni silmesi için kod eklemeniz gerekebilir . Anahtar dizisi CTRL + BACKSPACEve karakter kodu 0x7f.
Alex Essilfie

9

Konsol girişini okumak zordur, Ctrl, Alt, ayrıca imleç tuşları ve Geri / Sil gibi özel tuşları kullanmanız gerekir. Doğrudan ABD klavyesinde bulunan tuşları girmek için İsveç Ctrl gibi bazı klavye düzenlerinde bile gereklidir. Ben "düşük seviye" kullanarak bu işlemek için Console.ReadKey(true)çok zor olduğuna inanıyorum , bu yüzden en kolay ve en sağlam yolu biraz WINAPI kullanarak şifre girerken "konsol giriş yankı" devre dışı bırakmaktır.

Aşağıdaki örnek std :: cin sorusundan bir parola okunmasına verilen cevaba dayanmaktadır .

    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;
    }

9

Bu, şifreyi kırmızı bir kare ile maskeler, ardından şifre girildikten sonra orijinal renklerine geri döner.

Kullanıcının şifreyi almak için kopyala / yapıştır özelliğini kullanmasını engellemez, ancak daha çok birinin omzunun üstünden bakmasını durdurmakla ilgili, iyi bir hızlı çözümdür.

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;

Tek sorun, karakterlerin bulunduğu arka planı geri alırsanız kırmızı kalır. Linux tarzı şifre girişi için ForegroundColor origBG olarak ayarlayarak gitmek istiyorum.
mababin

1
Ayrıca Console.CursorVisible=false, daha sonra tekrar yapabilir ve önceki değerine geri ayarlayabilirsiniz. Bu, birisinin şifre uzunluğunda zirve yapmasını engelleyecektir.
mababin

6

Ben shermy vanilya C # 3.5. NET çözümü aksi bir cazibe çalışır bir hata bulundu. Vash'in SecureString fikrini Damian Leszczyński'yi de ekledim, ancak isterseniz sıradan bir dize kullanabilirsiniz.

Hata: Parola istemi sırasında geri tuşuna basarsanız ve parolanın geçerli uzunluğu 0 ise, parola maskesine bir yıldız işareti yanlış eklenir. Bu hatayı gidermek için aşağıdaki yöntemi değiştirin.

    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;
    }

4

EscapeAnahtar için destek ekleyen bir sürüm ( nulldize döndüren )

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;
        }
    }
}

3

(Benim) nuget paketi, üst cevaba dayanarak bunu yapmak için:

install-package PanoramicData.ConsoleExtensions

Kullanımı:

using PanoramicData.ConsoleExtensions;

...

Console.Write("Password: ");
var password = ConsolePlus.ReadPassword();
Console.WriteLine();

Proje URL'si: https://github.com/panoramicdata/PanoramicData.ConsoleExtensions

Çekme istekleri kabul edilir.


Bunu küçük bir proje için kullandım. Beklendiği gibi çalıştı. Thank you
gmalenko

1

Anahtarlarınızı biriken bağlantılı listeye ekleyebilirsiniz.

Geri tuşu alındığında, son anahtarı listeden kaldırın.

Enter tuşunu aldığınızda, listenizi bir dizeye daraltın ve kalan işinizi yapın.


Ulaşılabilir gibi geliyor ancak son karakteri ekrandan nasıl kaldıracağım.
Mohammad Nadeem

1

Backspace için bazı değişiklikler yaptım

        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);

1

Ronnie'nin versiyonunu, sadece CAPS LOCK'umu açtığımı öğrenmek için çok fazla zaman harcadıktan sonra güncelledim !

Bu sürümde mesajın ne olduğu _CapsLockMessageyazım alanının sonunda "yüzer" ve kırmızı renkte görüntülenir.

Bu sürüm biraz daha kod alır ve bir yoklama döngüsü gerektirir. Bilgisayarımda CPU kullanımı yaklaşık% 3 ila% 4, ancak gerekirse CPU kullanımını azaltmak için her zaman küçük bir Uyku () değeri eklenebilir.

    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();
    }

1

İşte benim basit versiyonum. Bir tuşa her bastığınızda, konsoldan hepsini silin ve şifre dizesinin uzunluğu kadar '*' çizin.

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

0

Bunu doğru anlarsam, geri silme ekranındaki görünür * karakteri ve geçiş değişkeninizdeki önbelleğe alınmış karakteri silmeye çalışıyorsunuz?

Öyleyse, başka bloğunuzu bu şekilde değiştirin:

            else
            {
                Console.Write("\b");
                pass = pass.Remove(pass.Length -1);
            }

1
Bu, karakterin geri silme ile silinmesinin görüntülenmemesi dışında iyi çalışır.
Mohammad Nadeem

-2
 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);

2
Bu cevap mevcut cevapların ötesinde bir şey eklemez. Buna ek olarak, iyi yanıtlar yalnızca kodu yanıt kutusuna yapıştırmak yerine kodu açıklamalıdır. Lütfen Cevap Nasıl
Okuyun
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.