Aplikasi konsol penyamaran kata sandi


201

Saya mencoba kode berikut ...

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

Tapi dengan cara ini fungsi backspace tidak berfungsi saat mengetik kata sandi. Ada saran?


11
Saya sarankan Anda tidak mengulangi apa pun kembali ke konsol karena itu akan memaparkan panjang kata sandi.
Ray Cheng

8
@ RayCheng - cukup adil, tetapi sangat sedikit antarmuka pengguna (selain pada beberapa sistem Unix) tidak ada gema sama sekali. Untuk pengalaman pengguna yang konsisten dengan aplikasi dan situs web lain, menunjukkan karakter * mungkin yang terbaik.
Stephen Holt

4
@StephenHolt Saya cukup yakin setiap input kata sandi berbasis terminal yang pernah saya temui memilih untuk tidak mengulangi apa pun ke terminal. Mengingat manfaat keamanan dan fakta bahwa ini adalah konvensi terkenal di dunia Unix, saya pribadi berpendapat bahwa tidak ada yang bergema adalah pilihan yang tepat, kecuali jika Anda yakin basis pengguna Anda mungkin tidak terbiasa dengan penggunaan terminal (dalam hal ini mungkin lebih baik menggunakan GUI saja).
Ajedi32

Jawaban:


225

Console.Write("\b \b");akan menghapus karakter tanda bintang dari layar, tetapi Anda tidak memiliki kode di dalam elseblok Anda yang menghapus karakter yang sebelumnya dimasukkan dari Andapass variabel string .

Berikut kode kerja yang relevan yang harus melakukan apa yang Anda butuhkan:

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 saya pikir \ b \ b akan membawa saya dua tempat kembali. Namun demikian ini tampaknya berfungsi dengan baik.
Mohammad Nadeem

8
@ Nadeem: Catat karakter spasi ( ' ') di antara karakter backspace ( '\b'). "\b \b"membawa Anda satu tempat kembali, lalu mencetak spasi (yang membawa Anda satu tempat ke depan) dan kemudian membawa Anda kembali, sehingga Anda berakhir di tempat '*'karakter yang dihapus itu.
dtb

14
@ Nadeem - Yang pertama \bmemindahkan kursor kembali satu posisi (sekarang di bawah karakter terakhir *. [space]Karakter "mencetak" tanda bintang, tetapi juga menggerakkan kursor satu karakter ke depan lagi, sehingga yang terakhir \bmemindahkan kursor kembali ke tempat yang terakhir *digunakan untuk be! (Fiuh - Harapan itu masuk akal!)
CraigTP

3
if (pass.Length > 0)seharusnya if (key.Key == ConsoleKey.Backspace && pass.Length > 0)kalau tidak Anda tidak akan mendapatkan karakter terakhir dari kata sandi ..
MemphiZ

9
Jika Anda tidak ingin pengguna dapat menulis karakter kontrol (seperti F5 atau Escape), Anda bisa menggantinya if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)dengan if (!char.IsControl(key.KeyChar)).
Safron

90

Untuk ini, Anda harus menggunakan 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;
}

Ini hanya akan membawa saya dua tempat kembali. Tapi yang saya butuhkan adalah ketika saya menekan Backspace karakter terakhir harus dihapus. Sama seperti fungsi asli dari backspace.
Mohammad Nadeem

1
Saya perlu bersarang if( pwd.Length > 0)di pernyataan orang lain untuk menghentikan orang menghapus pertanyaan :)
Dead.Rabit

1
Demikian pula dengan komentar Safron pada jawaban yang saat ini diterima, elseklausa terakhir akan mendapat manfaat dari tes if (!char.IsControl(i.KeyChar))(atau paling tidak if (i.KeyChar != '\u0000')).
Peter Taylor

1
Bagaimana saya bisa mengubah kata sandi itu menjadi string?
Joseph Kreifels II


47

Solusi lengkap, vanilla C # .net 3.5+

Potong tempel :)

    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
Selalu lebih sulit :) Tidak akan berfungsi di Mac / Linux karena baris baru tidak dikenali. Environment.NewLine memiliki string untuk baris baru. Jadi saya memodifikasi ini menjadi: while (! Environment.NewLine.Contains (chr = System.Console.ReadKey (true) .KeyChar))
Hugo Logmans

19

Mengambil jawaban teratas, serta saran dari komentarnya, dan memodifikasinya untuk menggunakan SecureString alih-alih String, uji semua kunci kontrol, dan jangan salah atau menulis "*" tambahan ke layar ketika panjang kata sandi 0, solusi saya adalah:

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

Milik saya mengabaikan karakter kontrol dan menangani pembungkus garis:

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

Bekerja dengan sempurna. Anda mungkin harus menambahkan kode untuk membuat DELETE karakter menghapus semua teks yang dimasukkan. Urutan kuncinya adalah CTRL + BACKSPACEdan kode char-nya 0x7f.
Alex Essilfie

9

Input konsol baca sulit, Anda perlu menangani tombol khusus seperti Ctrl, Alt, juga tombol kursor dan Backspace / Hapus. Pada beberapa tata letak keyboard, seperti Swedish Ctrl bahkan diperlukan untuk memasukkan tombol yang ada langsung di keyboard AS. Saya percaya bahwa mencoba menangani ini menggunakan "level rendah"Console.ReadKey(true) hanya sangat sulit, jadi cara termudah dan paling kuat adalah dengan menonaktifkan "konsol masukan gema" selama memasukkan kata sandi menggunakan sedikit WINAPI.

Sampel di bawah ini didasarkan pada jawaban untuk Baca kata sandi dari pertanyaan 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;
    }

9

Ini menutupi kata sandi dengan kotak merah, lalu kembali ke warna semula setelah kata sandi dimasukkan.

Ini tidak menghentikan pengguna untuk menggunakan salin / tempel untuk mendapatkan kata sandi, tetapi jika ini lebih tentang menghentikan seseorang untuk melihat dari balik bahu Anda, ini adalah solusi cepat yang bagus.

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;

Satu-satunya masalah adalah jika Anda melakukan backspace latar belakang di mana karakter Anda tetap berwarna merah. Saya lebih suka pergi dengan menetapkan ForegroundColor sebagai origBG untuk memiliki input kata sandi gaya Linux.
mababin

1
Anda juga dapat melakukan Console.CursorVisible=falsedan mengaturnya kembali ke nilai sebelumnya. Ini akan mencegah seseorang untuk memuncak pada panjang kata sandi.
mababin

6

Saya menemukan bug di shermy vanilla C # 3.5. Solusi NET yang berfungsi pesona. Saya juga telah memasukkan Damian Leszczyński - ide SecureString Vash di sini tetapi Anda dapat menggunakan string biasa jika Anda mau.

BUG: Jika Anda menekan spasi mundur saat prompt kata sandi dan panjang kata sandi saat ini adalah 0 maka tanda bintang tidak dimasukkan dengan benar di dalam topeng kata sandi. Untuk memperbaiki bug ini, modifikasi metode berikut.

    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

Berikut adalah versi yang menambahkan dukungan untuk Escapekunci (yang mengembalikan nullstring)

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

Paket nuget (saya) untuk melakukan ini, berdasarkan jawaban teratas:

install-package PanoramicData.ConsoleExtensions

Pemakaian:

using PanoramicData.ConsoleExtensions;

...

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

URL Proyek: https://github.com/panoramicdata/PanoramicData.ConsoleExtensions

Permintaan tarik diterima.


Saya menggunakan ini untuk proyek kecil. Bekerja seperti yang diharapkan. Terima kasih
gmalenko

1

Anda dapat menambahkan kunci Anda ke daftar yang tertaut.

Ketika kunci backspace diterima, hapus kunci terakhir dari daftar.

Ketika Anda menerima kunci enter, runtuh daftar Anda menjadi string dan lakukan sisa pekerjaan Anda.


Kedengarannya dapat dicapai tetapi bagaimana cara saya menghapus karakter terakhir dari layar.
Mohammad Nadeem

1

Saya membuat beberapa perubahan untuk backspace

        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

Ini adalah versi sederhana saya. Setiap kali Anda menekan tombol, hapus semua dari konsol dan gambar '*' sebanyak panjang string kata sandi.

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

Saya telah memperbarui versi Ronnie setelah menghabiskan terlalu banyak waktu mencoba memasukkan kata sandi hanya untuk mengetahui bahwa saya telah mengaktifkan CAPS LOCK!

Dengan versi ini, pesan apa pun yang ada _CapsLockMessageakan "mengambang" di akhir area pengetikan dan akan ditampilkan dengan warna merah.

Versi ini membutuhkan sedikit lebih banyak kode dan membutuhkan loop polling. Di komputer saya penggunaan CPU sekitar 3% hingga 4%, tetapi orang selalu bisa menambahkan nilai Sleep () kecil untuk mengurangi penggunaan CPU jika diperlukan.

    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

Jika saya memahami ini dengan benar, Anda mencoba membuat backspace menghapus karakter * yang terlihat di layar dan karakter yang di-cache dalam variabel pass Anda?

Jika demikian, maka ubah saja blok Anda yang lain untuk ini:

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

1
Ini akan berfungsi dengan baik kecuali bahwa penghapusan karakter oleh backspace tidak akan ditampilkan.
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);

1
Jawaban ini tidak menambahkan apa pun di luar jawaban yang ada. Selain itu, jawaban yang baik biasanya menjelaskan kode, bukan hanya menempelkan kode ke kotak jawaban. Silakan baca Cara Menjawab
durron597
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.