/*
*
* Copyright (c) 2013 - 2017 Lijun Liao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
*
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the XiPKI software without
* disclosing the source code of your own applications.
*
* For more information, please contact Lijun Liao at this
* address: lijun.liao@gmail.com
*/
package org.xipki.commons.password;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
/**
* @author Lijun Liao
* @since 2.0.0
*/
public class SecurePasswordInputPanel extends Panel {
public class MyActionListener implements ActionListener {
@Override
public void actionPerformed(final ActionEvent event) {
JButton btn = (JButton) event.getSource();
String pressedKey = (String) btn.getClientProperty("key");
if (CAPS.equals(pressedKey)) {
for (JButton button : buttons) {
String text = button.getText();
text = caps ? text.toLowerCase() : text.toUpperCase();
button.setText(text);
}
caps = !caps;
return;
}
if (BACKSPACE.equals(pressedKey)) {
if (password.length() > 0) {
password = password.substring(0, password.length() - 1);
}
} else if (CLEAR.equals(pressedKey)) {
password = "";
} else {
password += btn.getText();
}
passwordField.setText(password);
} // method actionPerformed
} // class MyActionListener
private static final long serialVersionUID = 1L;
// CHECKSTYLE:SKIP
private static final String BACKSPACE = "\u21E6";
// CHECKSTYLE:SKIP
private static final String CAPS = "\u21E7";
private static final String CLEAR = "Clear";
private static final String OK = "OK";
private static final Map<Integer, String[]> KEYS_MAP = new HashMap<Integer, String[]>();
private final JPasswordField passwordField;
private final Set<JButton> buttons = new HashSet<JButton>();
private String password = "";
private boolean caps;
static {
int idx = 0;
KEYS_MAP.put(idx++, new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"});
KEYS_MAP.put(idx++, new String[]{"!", "@", "ยง" , "#", "$", "%", "^", "&", "*",
"(", ")", "{", "}"});
KEYS_MAP.put(idx++, new String[]{"'", "\"", "=", "_", ":", ";", "?", "~", "|", ",",
".", "-", "/"});
KEYS_MAP.put(idx++, new String[]{"q", "w", "e", "r", "z", "y", "u", "i", "o", "p"});
KEYS_MAP.put(idx++, new String[]{"a", "s", "d", "f", "g", "h", "j", "k", "j", BACKSPACE});
KEYS_MAP.put(idx++, new String[] {CAPS, "z", "x", "c", "v", "b", "n", "m", CLEAR});
}
private SecurePasswordInputPanel() {
super(new GridLayout(0, 1));
this.passwordField = new JPasswordField(10);
passwordField.setEditable(false);
add(passwordField);
Set<Integer> rows = new HashSet<Integer>(KEYS_MAP.keySet());
final int n = rows.size();
SecureRandom random = new SecureRandom();
while (!rows.isEmpty()) {
int row = random.nextInt() % n;
if (!rows.contains(row)) {
continue;
}
String[] keys = KEYS_MAP.get(row);
rows.remove(row);
JPanel panel = new JPanel();
for (int column = 0; column < keys.length; column++) {
String text = keys[column];
JButton button = new JButton(text);
button.setFont(button.getFont().deriveFont(Font.TRUETYPE_FONT));
if (CLEAR.equalsIgnoreCase(text)) {
button.setBackground(Color.red);
} else if (CAPS.equalsIgnoreCase(text) || BACKSPACE.equalsIgnoreCase(text)) {
button.setBackground(Color.lightGray);
} else {
buttons.add(button);
}
button.putClientProperty("key", text);
button.addActionListener(new MyActionListener());
panel.add(button);
} // end for
add(panel);
} // end while(!rows.isEmpty())
//setVisible(true);
} // constructor
public char[] getPassword() {
return password.toCharArray();
}
public static char[] readPassword(final String prompt) {
LookAndFeel currentLookAndFeel = UIManager.getLookAndFeel();
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) { // CHECKSTYLE:SKIP
}
try {
SecurePasswordInputPanel gui = new SecurePasswordInputPanel();
String[] options = new String[]{OK};
String tmpPrompt = prompt;
if (tmpPrompt == null || tmpPrompt.isEmpty()) {
tmpPrompt = "Password required";
}
int option = JOptionPane.showOptionDialog(null, gui, tmpPrompt, JOptionPane.NO_OPTION,
JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
if (option == 0) { // pressing OK button
return gui.getPassword();
} else {
return null;
}
} finally {
try {
UIManager.setLookAndFeel(currentLookAndFeel);
} catch (UnsupportedLookAndFeelException ex) { // CHECKSTYLE:SKIP
}
}
} // method readPassword
}