001/* 002 * Copyright (c) 2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030/** Textual password query class. Open source code taken from 031 * http://java.sun.com/developer/technicalArticles/Security/pwordmask/ 032 * 033 * '$RCSfile$' 034 * 035 * '$Author: welker $' 036 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 037 * '$Revision: 24234 $' 038 * 039 * For Details: http://www.kepler-project.org 040 * 041 */ 042 043package org.kepler.ssh; 044 045import java.io.IOException; 046import java.io.InputStream; 047import java.io.PushbackInputStream; 048import java.util.Arrays; 049 050/** 051 * This class prompts the user for a password and attempts to mask input with 052 * "*" 053 */ 054 055public class MaskedTextPasswordField { 056 057 /** 058 *@param input 059 * stream to be used (e.g. System.in) 060 *@param prompt 061 * The prompt to display to the user. 062 *@return The password as entered by the user. 063 */ 064 065 public static final char[] getPassword(InputStream in, String prompt) 066 throws IOException { 067 MaskingThread maskingthread = new MaskingThread(prompt); 068 Thread thread = new Thread(maskingthread); 069 thread.start(); 070 071 char[] lineBuffer; 072 char[] buf; 073 int i; 074 075 buf = lineBuffer = new char[128]; 076 077 int room = buf.length; 078 int offset = 0; 079 int c; 080 081 loop: while (true) { 082 switch (c = in.read()) { 083 case -1: 084 case '\n': 085 break loop; 086 087 case '\r': 088 int c2 = in.read(); 089 if ((c2 != '\n') && (c2 != -1)) { 090 if (!(in instanceof PushbackInputStream)) { 091 in = new PushbackInputStream(in); 092 } 093 ((PushbackInputStream) in).unread(c2); 094 } else { 095 break loop; 096 } 097 098 default: 099 if (--room < 0) { 100 buf = new char[offset + 128]; 101 room = buf.length - offset - 1; 102 System.arraycopy(lineBuffer, 0, buf, 0, offset); 103 Arrays.fill(lineBuffer, ' '); 104 lineBuffer = buf; 105 } 106 buf[offset++] = (char) c; 107 break; 108 } 109 } 110 maskingthread.stopMasking(); 111 if (offset == 0) { 112 return null; 113 } 114 char[] ret = new char[offset]; 115 System.arraycopy(buf, 0, ret, 0, offset); 116 Arrays.fill(buf, ' '); 117 return ret; 118 } 119 120 /** 121 * This class attempts to erase characters echoed to the console. Taken from 122 * http://java.sun.com/developer/technicalArticles/Security/pwordmask/ 123 * Source is open. 124 */ 125 private static class MaskingThread extends Thread { 126 private volatile boolean dowork; 127 private char echochar = ' '; 128 129 /** 130 *@param prompt 131 * The prompt displayed to the user 132 */ 133 public MaskingThread(String prompt) { 134 System.out.print(prompt); 135 } 136 137 /** 138 * Begin masking until asked to stop. 139 */ 140 public void run() { 141 142 int priority = Thread.currentThread().getPriority(); 143 Thread.currentThread().setPriority(Thread.MAX_PRIORITY); 144 145 try { 146 dowork = true; 147 while (dowork) { 148 System.out.print("\010" + echochar); 149 try { 150 // attempt masking at this rate 151 Thread.currentThread().sleep(1); 152 } catch (InterruptedException iex) { 153 Thread.currentThread().interrupt(); 154 return; 155 } 156 } 157 } finally { // restore the original priority 158 Thread.currentThread().setPriority(priority); 159 } 160 } 161 162 /** 163 * Instruct the thread to stop masking. 164 */ 165 public void stopMasking() { 166 this.dowork = false; 167 } 168 } 169 170}