From 97dfeb759a973b8fea9643089410048f78f88050 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Tue, 7 Apr 2026 15:28:37 +0200 Subject: [PATCH] better password generator --- .../java/cz/kamma/jkeepass/KeepassApp.java | 114 +++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/src/main/java/cz/kamma/jkeepass/KeepassApp.java b/src/main/java/cz/kamma/jkeepass/KeepassApp.java index 33d2a98..e18d335 100644 --- a/src/main/java/cz/kamma/jkeepass/KeepassApp.java +++ b/src/main/java/cz/kamma/jkeepass/KeepassApp.java @@ -80,6 +80,7 @@ public class KeepassApp extends JFrame { private static final String PREF_WINDOW_HEIGHT = "window_height"; private static final String PREF_MAIN_DIVIDER = "main_divider_location"; private static final String PREF_RIGHT_DIVIDER = "right_divider_location"; + private static final String PREF_SPECIAL_CHARS = "password_generator_special_chars"; private static final int MAX_RECENT_FILES = 5; private File currentFile; private String currentPassword; @@ -737,6 +738,108 @@ public class KeepassApp extends JFrame { } } + /** + * Dialog for generating passwords + */ + private static class PasswordGeneratorDialog extends JDialog { + private final JCheckBox upperCheck = new JCheckBox("A-Z", true); + private final JCheckBox lowerCheck = new JCheckBox("a-z", true); + private final JCheckBox digitCheck = new JCheckBox("0-9", true); + private final JTextField specialField; + private final JPanel listPanel = new JPanel(); + private String selectedPassword = null; + private final Preferences prefs = Preferences.userNodeForPackage(KeepassApp.class); + + public PasswordGeneratorDialog(JDialog owner) { + super(owner, "Password Generator", true); + setLayout(new BorderLayout()); + + String savedSpecial = prefs.get(PREF_SPECIAL_CHARS, "!@#$%^&*()-_=+[]{}|;:,.<>?"); + specialField = new JTextField(savedSpecial, 15); + + JPanel optionsPanel = new JPanel(new FlowLayout()); + optionsPanel.add(upperCheck); + optionsPanel.add(lowerCheck); + optionsPanel.add(digitCheck); + optionsPanel.add(new JLabel("Special:")); + optionsPanel.add(specialField); + + add(optionsPanel, BorderLayout.NORTH); + + listPanel.setLayout(new GridBagLayout()); + JScrollPane scrollPane = new JScrollPane(listPanel); + add(scrollPane, BorderLayout.CENTER); + + JButton regenBtn = new JButton("Regenerate"); + regenBtn.addActionListener(e -> { + prefs.put(PREF_SPECIAL_CHARS, specialField.getText()); + generateList(); + }); + JButton closeBtn = new JButton("Close"); + closeBtn.addActionListener(e -> { + prefs.put(PREF_SPECIAL_CHARS, specialField.getText()); + setVisible(false); + }); + + JPanel btnPanel = new JPanel(); + btnPanel.add(regenBtn); + btnPanel.add(closeBtn); + add(btnPanel, BorderLayout.SOUTH); + + generateList(); + + setSize(450, 450); + setLocationRelativeTo(owner); + } + + private void generateList() { + listPanel.removeAll(); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(2, 5, 2, 5); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1.0; + gbc.gridx = 0; + + String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + String lower = "abcdefghijklmnopqrstuvwxyz"; + String digits = "0123456789"; + String special = specialField.getText(); + + StringBuilder chars = new StringBuilder(); + if (upperCheck.isSelected()) chars.append(upper); + if (lowerCheck.isSelected()) chars.append(lower); + if (digitCheck.isSelected()) chars.append(digits); + chars.append(special); + + if (chars.length() == 0) { + listPanel.add(new JLabel("Select at least one option or enter special chars"), gbc); + } else { + SecureRandom random = new SecureRandom(); + for (int i = 0; i < 10; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < 16; j++) { + sb.append(chars.charAt(random.nextInt(chars.length()))); + } + String pass = sb.toString(); + JButton passBtn = new JButton(pass); + passBtn.setHorizontalAlignment(JButton.LEFT); + passBtn.addActionListener(e -> { + selectedPassword = pass; + setVisible(false); + }); + gbc.gridy = i; + listPanel.add(passBtn, gbc); + } + } + listPanel.revalidate(); + listPanel.repaint(); + } + + public String getSelectedPassword() { + return selectedPassword; + } + } + /** * Dialog for adding/editing entries */ @@ -786,12 +889,21 @@ public class KeepassApp extends JFrame { showPassCheck.addActionListener(e -> { if (showPassCheck.isSelected()) { passField.setEchoChar((char) 0); + passField.putClientProperty("JPasswordField.cutCopyAllowed", Boolean.TRUE); } else { passField.setEchoChar('•'); + passField.putClientProperty("JPasswordField.cutCopyAllowed", Boolean.FALSE); } }); JButton genBtn = new JButton("Generate"); - genBtn.addActionListener(e -> passField.setText(generateRandomPassword(16))); + genBtn.addActionListener(e -> { + PasswordGeneratorDialog pgd = new PasswordGeneratorDialog(this); + pgd.setVisible(true); + String pass = pgd.getSelectedPassword(); + if (pass != null) { + passField.setText(pass); + } + }); passBtnOptionPanel.add(showPassCheck); passBtnOptionPanel.add(genBtn); gbc.gridx = 2;