67

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();
}
kentcdodds
  • 27,113
  • 32
  • 108
  • 187

6 Answers6

97

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.

adarshr
  • 61,315
  • 23
  • 138
  • 167
  • 32
    +1 for letting me know it doesn't work in Eclipse console (though I use Netbeans, apparently it doesn't work in either). – kentcdodds May 30 '12 at 15:58
  • 4
    Doesn't work in most IDEs as they use javaw. See http://stackoverflow.com/a/26473083/3696510 – muttonUp May 03 '16 at 12:33
  • 1
    It doesn't work even if I create jar and run it in console, pure command prompt. I get Null pointer. – Paresh Oct 13 '16 at 04:47
  • 1
    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 ) );` – krevelen Nov 10 '16 at 13:31
  • Might be useful to add how to retrieve a `Console` instance: ``System.console()` – Willi Mentzel Jan 08 '21 at 11:11
15

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.

Community
  • 1
  • 1
Vijay Shanker Dubey
  • 4,308
  • 6
  • 32
  • 49
  • 1
    http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers – T.J. Crowder May 30 '12 at 15:36
  • 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 May 30 '12 at 15:37
  • 1
    The link in the answer seems to not work anymore. Here is a live link. http://www.cse.chalmers.se/edu/course/TDA602/Eraserlab/pwdmasking.html – xubia Nov 12 '14 at 19:48
  • 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 Jun 07 '17 at 17:51
  • 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? – Urhixidur Jun 07 '17 at 21:11
9

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('*'));
Gareth Davis
  • 27,701
  • 12
  • 73
  • 106
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
3

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

Dhruv Gairola
  • 9,102
  • 5
  • 39
  • 43
1

Here is an example using console.readPassword(...); in an IDE. I use Netbeans. Note: In your IDE, Scanner will be used and it will show the password!. In the console, console.readPassword(..) will be used and it will not show the password!.

 public static void main(String[] args) {
     //The jar needs to be run from the terminal for console to work.
     Scanner input = new Scanner(System.in);

     Console console = System.console();
     String username = "";
     String password = "";
     if (console == null) 
     {
         System.out.print("Enter username: ");
         username = input.nextLine();
         System.out.print("Enter password: ");
         password = input.nextLine();
     }
     else 
     {
         username = console.readLine("Enter username: ");
         password = new String(console.readPassword("Enter password: "));
     }
     
     //I use the scanner for all other input in the code!
     //I do not know if there are any pitfalls associated with using the Scanner and console in this manner!
  }      

Note: I do not know if there are any pitfalls associated with using the Scanner and console in this manner!

SedJ601
  • 12,173
  • 3
  • 41
  • 59
0

The class Console has a method readPassword() that might solve your problem.

rlinden
  • 2,053
  • 1
  • 12
  • 13