By with contributions from Alan Sommerer, July 2004 | | | One of the ongoing criticisms of the Java command line text-based input/output APIs is the lack of support for command line input password masking. With AWT/Swing, this is not a problem as methods are provided to mask passwords easily. An earlier version of this article was published here in September 2002, and since then numerous letters of thanks, constructive comments, and permissions to use the source code in applications have been received. This article: - Presents a brief overview of password masking
- Describes the AWT/Swing utilities for password masking
- Provides a platform-independent solution to the command-line input password masking problem
- Provides an improved (reliable and secure) solution to password masking
Login screens and login dialog boxes use password masking — a technique to either hide the password as it is being typed or displaying a character (such as an asterisk '*') instead of the characters that users type. For example, when you login on a Windows machine, you are presented with a login dialog box in which the password field uses an asterisk as the masking or echo character. ![Figure 1: UNIX login screen](http://java.sun.com/developer/technicalArticles/Security/pwordmask/fig1.gif) | Figure 1: UNIX login screen | | In the case of UNIX, the password field in the login screen doesn't display echo characters. It simply doesn't display anything at all as shown in Figure 1. Password Masking in AWT/Swing If you wish to provide a graphical login dialog box for your application, you can use the AWT's TextField class, which is a text component that allows the editing of a single line of text. To mask the password field, use the setEchoChar method. For example, to set the echo char to an asterisk, you would do: TextField password = new TextField(8);password.setEchoChar('*'); | The number 8 specifies the width of the text field based on the average character width for the font in use. You can set the echo character to any character you like. Note that if you set it to zero, 0 , it means that the input will be echoed and not masked. ![Figure 2: JPasswordField with setEchoChar('#')](http://java.sun.com/developer/technicalArticles/Security/pwordmask/fig2.gif) | Figure 2: JPasswordField with setEchoChar('#') | | In Swing, you can use the JPasswordField , which allows the editing of a single line of text where the view indicates something was typed, but does not show the original characters. The JPasswordField class is source-compatible with the AWT's TextField used with setEchoChar . If you use the JPasswordField , the default echo character is an asterisk '*', but you can change it to any character of your choice. Again, if you set the echo character to zero, 0 , it means that characters will be displayed as they are typed and no masking will be performed. Figure 2 shows a Swing login dialog box where the echo character is being set to a hash sign, # using the following snippet of code: JPasswordField password = new JPasswordField(8);password.setEchoChar('#'); | Command-Line Input Masking Unlike AWT/Swing, there are no special APIs for masking command-line input in Java. This is a feature that has been asked for by many developers. It is useful if you wish to provide a login screen for command-line text-based Java applications as well as server-side Java applications. One way to provide for such a feature is to use Java Native Interface (JNI). This might be difficult for some Java developers who do not know C/C++, or wish to keep to 100% pure Java code. Here I provide a solution to this problem. In the earlier version of this article, a UNIX-like approach to login screens, where the password is not echoed on the screen at all, was used. This is done by having a separate thread that attempts to erase characters echoed to the console by re-writing and printing the password prompt. The code featured in that article can still be downloaded from along with code for improvements. One of the most asked-for features, however, was replacing the echoed characters with asterisks "*". Therefore, this article starts by providing a simple solution for password masking, followed by an improved, more reliable, and secure code. This solution 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, which is shown in Code Sample 1. Code Sample 1: EraserThread.java 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; }} | Note: The solution makes extensive use of threads, however, if the machine is under heavy load, there is no guarantee that the MaskingThread will run often enough. Please see the rest of the article for an improved version of the code. The EraserThread class is used by the PasswordField class, which is shown in Code Sample 2. This class prompts the user for a password and an instance of EraserThread attempts to mask the input with "*". Note that initially a asterisk (*) will be displayed. Code Sample 2: PasswordField.java 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; }} | As an example of how to use the PasswordField class, consider the application, TestApp , shown in Sample Code 3. This application displays a prompt and waits for the user to enter a password. The entry is of course masked by asterisks (*). Code Sample 3: TestApp.java class TestApp { public static void main(String argv[]) { String password = PasswordField.readPassword("Enter password: "); System.out.println("The password entered is: "+password); }} | ![Figure 3: TestApp Sample Output](http://java.sun.com/developer/technicalArticles/Security/pwordmask/fig3.gif) | Figure 3: TestApp Sample Output | | If you run TesApp on Windows, MacOS, or UNIX, you will see something similar to Figure 3. Again, note that when you run the application, an initial asterisk is displayed. Making the Code Secure and Reliable The above simple solution suffers from one main drawback: strings should not be used for storing sensitive information such as passwords!. In the rest of the article, an improved solution is shown. First, however, the MaskingThread class could benefit from a couple of improvements: - In order to ensure visibility across threads, especially on multi-CPU machines, the
stop field should be marked volatile . The volatile keywords specifies that the field is used by synchronized threads so that the compiler should not perform any optimizations with it; in other words, the variable's value should be read from memory and no copy should be saved on the stack. - To ensure that masking can occur when the system is under heavy use, the calling thread priority is set to the max for the duration of the call. The original priority is restored upon return.
Code Sample 4 shows the revised MaskingThread class with the changes highlighted in bold text. Code Sample 4: MaskingThread.java import java.io.*;/** * This class attempts to erase characters echoed to the console. */class MaskingThread extends Thread { private volatile boolean stop; private char echochar = '*'; /** *@param prompt The prompt displayed to the user */ public MaskingThread(String prompt) { System.out.print(prompt); } /** * Begin masking until asked to stop. */ public void run() { int priority = Thread.currentThread().getPriority(); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); try { stop = true; while(stop) { System.out.print("/010" + echochar); try { // attempt masking at this rate Thread.currentThread().sleep(1); }catch (InterruptedException iex) { Thread.currentThread().interrupt(); return; } } } finally { // restore the original priority Thread.currentThread().setPriority(priority); } } /** * Instruct the thread to stop masking. */ public void stopMasking() { this.stop = false; }} | While it may seem logical to collect and store the password using String s, they are not suitable for storing sensitive information such as passwords. This is because objects of type String are immutable -- the contents of the string cannot be changed or overwritten after use. An array of char s should be used instead. The revised PasswordField shown in Code Sample 5 has been adapted from . Code Sample 5: PasswordField.java import java.io.*;import java.util.*;/** * This class prompts the user for a password and attempts to mask input with "*" */public class PasswordField { /** *@param input stream to be used (e.g. System.in) *@param prompt The prompt to display to the user. *@return The password as entered by the user. */ public static final char[] getPassword(InputStream in, String prompt) throws IOException { MaskingThread maskingthread = new MaskingThread(prompt); Thread thread = new Thread(maskingthread); thread.start(); char[] lineBuffer; char[] buf; int i; buf = lineBuffer = new char[128]; int room = buf.length; int offset = 0; int c; loop: while (true) { switch (c = in.read()) { case -1: case '/n': break loop; case '/r': int c2 = in.read(); if ((c2 != '/n') && (c2 != -1)) { if (!(in instanceof PushbackInputStream)) { in = new PushbackInputStream(in); } ((PushbackInputStream)in).unread(c2); } else { break loop; } default: if (--room < 0) { buf = new char[offset + 128]; room = buf.length - offset - 1; System.arraycopy(lineBuffer, 0, buf, 0, offset); Arrays.fill(lineBuffer, ' '); lineBuffer = buf; } buf[offset++] = (char) c; break; } } maskingthread.stopMasking(); if (offset == 0) { return null; } char[] ret = new char[offset]; System.arraycopy(buf, 0, ret, 0, offset); Arrays.fill(buf, ' '); return ret; }} | Finally, the PasswordApp class shown in Code Sample 6 is just a test application to test the revised code. Code Sample 6: PasswordApp.java import java.io.*;public class PasswordApp { public static void main(String argv[]) { char password[] = null; try { password = PasswordField.getPassword(System.in, "Enter your password: "); } catch(IOException ioe) { ioe.printStackTrace(); } if(password == null ) { System.out.println("No password entered"); } else { System.out.println("The password entered is: "+String.valueOf(password)); } }} | ![Figure 4: PasswordApp Sample Output](http://java.sun.com/developer/technicalArticles/Security/pwordmask/fig4.gif) | Figure 4: PasswordApp Sample Output | | If you run PasswordApp on Windows, MacOS, or UNIX, you will see something similar to Figure 4. This article presented an overview of password masking in Java. It demonstrated how easy it is to mask passwords in AWT/Swing applications, and provided a reusable pure Java solution for command-line text-based password masking. Feel free to reuse, improve, and adapt the code in this article for your applications. You can enhance it by adding methods for password constraints. As an exercise, you may wish to enhance the code presented so that a password can be of a certain length, and so that some characters, such as a space for example, may not be allowed in a password. - Password Fields:
- Java Look and Feel Design Guidelines (online book):
|