Q: I need to read passwords from the command line. Is there a way to easily read characters from the command line securely in Java? Specifically, how do you echo * to the command line?
To read items in from the command line, we use the input stream
System.in. If we dig into the code a bit, we see that the default
System.in input stream is really just an
InputStream. Digging into my implementation of Java (IBM 1.1.7), the true implementation type of that
System.in is a
FileInputStream. That input stream is set to read in from the source file descriptor
FileDescriptor.in is initialized by natively defined code. It is left to the system to point the
FileDescriptor.in at the standard input handle.
If we look at the API definition for
FileInputStream, we see that the read method is natively defined. Typed data is not taken from the command line and made available from the standard in file descriptor until the return button is pressed. Since we cannot grab a character as it is typed, there is no way to intercept the character and echo a
* to the command line. Instead, we can only grab complete lines.
That leaves us with few alternatives. Unfortunately, most of the solutions require us to dive into native code or employ a GUI frontend. Briefly, here are a few solutions:
- Forgo a command-line-based username/password interface. Instead, use a graphical frontend. Swing provides for protected text fields. Of course, that approach is useless if you plan to deploy on a terminal or another nongraphical environment.
- Forget writing that piece of your application in Java. Instead, write a process based on C or C++ that reads from the command line. Your Java application can talk to that process over the wire through sockets or some other protocol such as CORBA. There are some disadvantages to that approach: first, you must recompile the code for each deployment environment. Second, you'll need to encrypt the password since it may pass over the network.
- Write your own JNI command line reader and call it from your code, thus bypassing the input stream. Again, you'll need to recompile your library for each platform.
- If you feel really adventurous, you can always extend
FileInputStreamand provide your own