Hide input on command line

92,407

Solution 1

Try java.io.Console.readPassword. You'll have to be running at least Java 6 though.

   /**
    * Reads a password or passphrase from the console with echoing disabled
    *
    * @throws IOError
    *         If an I/O error occurs.
    *
    * @return  A character array containing the password or passphrase read
    *          from the console, not including any line-termination characters,
    *          or <tt>null</tt> if an end of stream has been reached.
    */
    public char[] readPassword() {
        return readPassword("");
    }

Beware though, this doesn't work with the Eclipse console. You'll have to run the program from a true console/shell/terminal/prompt to be able to test it.

Solution 2

Yes can be done. This is called Command-Line Input Masking. You can implement this easily.

You can uses a separate thread to erase the echoed characters as they are being entered, and replaces them with asterisks. This is done using the EraserThread class shown below

import java.io.*;

class EraserThread implements Runnable {
   private boolean stop;

   /**
    *@param The prompt displayed to the user
    */
   public EraserThread(String prompt) {
       System.out.print(prompt);
   }

   /**
    * Begin masking...display asterisks (*)
    */
   public void run () {
      stop = true;
      while (stop) {
         System.out.print("\010*");
     try {
        Thread.currentThread().sleep(1);
         } catch(InterruptedException ie) {
            ie.printStackTrace();
         }
      }
   }

   /**
    * Instruct the thread to stop masking
    */
   public void stopMasking() {
      this.stop = false;
   }
}

With using this thread

public class PasswordField {

   /**
    *@param prompt The prompt to display to the user
    *@return The password as entered by the user
    */
   public static String readPassword (String prompt) {
      EraserThread et = new EraserThread(prompt);
      Thread mask = new Thread(et);
      mask.start();

      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      String password = "";

      try {
         password = in.readLine();
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
      // stop masking
      et.stopMasking();
      // return the password entered by the user
      return password;
   }
}

This Link discuss in details.

Solution 3

JLine 2 may be of interest. As well as character masking, it'll provide command-line completion, editing and history facilities. Consequently it's very useful for a CLI-based Java tool.

To mask your input:

String password = new jline.ConsoleReader().readLine(new Character('*'));

Solution 4

There is :

Console cons;
char[] passwd;
if ((cons = System.console()) != null &&
    (passwd = cons.readPassword("[%s]", "Password:")) != null) {
    ...
    java.util.Arrays.fill(passwd, ' ');
}

source

but I don't think this works with an IDE like Eclipse because the program is run as a background process rather than a top level process with a console window.

Another approach is to use the JPasswordField in swing with the accompanying actionPerformed method:

public void actionPerformed(ActionEvent e) {
    ...
    char [] p = pwdField.getPassword();
}

source

Share:
92,407
kentcdodds
Author by

kentcdodds

I am Kent C. Dodds. I work at PayPal as a full stack JavaScript engineer. I host JavaScript Air, the live video broadcast podcast about JavaScript and the web platform. I spend a bit of time on GitHub and Twitter. I'm an Egghead.io instructor. I'm happily married and the father of three kids. I like code. I care about craft, design, and architecture. I like to talk about it. Come chat with me :-)

Updated on July 08, 2022

Comments

  • kentcdodds
    kentcdodds almost 2 years

    I know that command line interfaces like Git and others are able to hide input from a user (useful for passwords). Is there a way to programmtically do this in Java? I'm taking password input from a user and I would like their input to be "hidden" on that particular line (but not on all of them). Here's my code for it (though I doubt it would be helpful...)

    try (Scanner input = new Scanner(System.in)) {
      //I'm guessing it'd probably be some property you set on the scanner or System.in right here...
      System.out.print("Please input the password for " + name + ": ");
      password = input.nextLine();
    }
    
  • T.J. Crowder
    T.J. Crowder almost 12 years
  • kentcdodds
    kentcdodds almost 12 years
    I'm looking into this answer and I like the password masking. But please include the content of the article you feel useful for this to be a good answer.
  • kentcdodds
    kentcdodds almost 12 years
    +1 for letting me know it doesn't work in Eclipse console (though I use Netbeans, apparently it doesn't work in either).
  • Aura
    Aura over 9 years
    The link in the answer seems to not work anymore. Here is a live link. cse.chalmers.se/edu/course/TDA602/Eraserlab/pwdmasking.html
  • muttonUp
    muttonUp almost 8 years
    Doesn't work in most IDEs as they use javaw. See stackoverflow.com/a/26473083/3696510
  • Paresh
    Paresh over 7 years
    It doesn't work even if I create jar and run it in console, pure command prompt. I get Null pointer.
  • krevelen
    krevelen over 7 years
    Here's an example: final String passwd; final String message = "Enter password"; if( System.console() == null ) { final JPasswordField pf = new JPasswordField(); passwd = JOptionPane.showConfirmDialog( null, pf, message, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) == JOptionPane.OK_OPTION ? new String( pf.getPassword() ) : ""; } else passwd = new String( System.console().readPassword( "%s> ", message ) );
  • Urhixidur
    Urhixidur almost 7 years
    Oddly, in a JetBrains IntelliJ environment the code fails because the EraserThread's BS-* print behaves as a println. Weird. But if I run it directly from a command line, it works (there's a minor adjustment that needs to be done to the EraserThread System.out.print(prompt))
  • Urhixidur
    Urhixidur almost 7 years
    Behaviour of the PasswordApp on the www.cse.chalmers.se page is not quite what one expects. As soon as the thread gets going, the last character of the prompt gets overwritten with an asterisk. As other characters are typed in, further asterisks are added so that there are always one more asterisk than the user typed characters. This may be a cosmetic problem, but it is annoying. Also, the thread is erasing and writing the last asterisk every millisecond: isn't there a way to make it wait until a character gets typed?
  • Willi Mentzel
    Willi Mentzel over 3 years
    Might be useful to add how to retrieve a Console instance: ``System.console()`