All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.key.passwd.swing.CreateKeyPanel Maven / Gradle / Ivy

/*
 * Copyright (C) 2006-2010 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.schlichtherle.key.passwd.swing;

import de.schlichtherle.swing.EnhancedPanel;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * This panel prompts the user for a key to create or overwrite a protected
 * resource.
 * It currently supports password and key file authentication, but is
 * extensible for use with certificate based authentication, too.
 *
 * @author Christian Schlichtherle
 * @since TrueZIP 6.0
 * @version $Id: CreateKeyPanel.java 5e709f50671d 2010/11/05 11:52:07 christian $
 */
public class CreateKeyPanel extends EnhancedPanel {

    private static final String CLASS_NAME
            = "de.schlichtherle.key.passwd.swing.CreateKeyPanel";
    private static final ResourceBundle resources
            = ResourceBundle.getBundle(CLASS_NAME);

    /** The minimum acceptable length of a password. */
    private static final int MIN_PASSWD_LEN = 6;

    private final Color defaultForeground;

    private JComponent extraDataUI;

    private Feedback feedback;

    /**
     * Creates new form CreateKeyPanel
     */
    public CreateKeyPanel() {
        initComponents();
        final DocumentListener dl = new DocumentListener() {
            public void insertUpdate(DocumentEvent e) {
                setError(null);
            }

            public void removeUpdate(DocumentEvent e) {
                setError(null);
            }

            public void changedUpdate(DocumentEvent e) {
                setError(null);
            }
        };
        newPasswd1.getDocument().addDocumentListener(dl);
        newPasswd2.getDocument().addDocumentListener(dl);
        authenticationPanel.getKeyFileDocument().addDocumentListener(dl);
        defaultForeground = resourceID.getForeground();
    }

    private Font getBoldFont() {
        Font font = resourceID.getFont();
        return new Font(font.getName(), Font.BOLD, font.getSize());
    }

    /**
     * Getter for property {@code resourceID}.
     *
     * @return Value of property {@code resourceID}.
     */
    public String getResourceID() {
        return resourceID.getText();
    }
    
    /**
     * Setter for property {@code resourceID}.
     *
     * @param resourceID New value of property {@code resourceID}.
     */
    public void setResourceID(final String resourceID) {
        final String lastResourceID = PromptingKeyProviderUI.lastResourceID;
        if (!lastResourceID.equals(resourceID) && !"".equals(lastResourceID)) {
            this.resourceID.setForeground(Color.RED);
        } else {
            this.resourceID.setForeground(defaultForeground);
        }
        this.resourceID.setText(resourceID);
        PromptingKeyProviderUI.lastResourceID = resourceID;
    }
    
    /**
     * Getter for property {@code createKey}.
     *
     * @return Value of property {@code createKey}.
     *         This is {@code null} if the user hasn't entered two equal
     *         passwords or if the password is weak.
     *         May also be {@code null} if a key file is selected and
     *         accessing it results in an exception.
     */
    public Object getCreateKey() {
        try {
            switch (authenticationPanel.getAuthenticationMethod()) {
                case AuthenticationPanel.AUTH_PASSWD:
                    final char[] newPasswd1Content = newPasswd1.getPassword();
                    final char[] newPasswd2Content = newPasswd2.getPassword();
                    if (Arrays.equals(newPasswd1Content, newPasswd2Content)) {
                        Arrays.fill(newPasswd2Content, (char) 0);
                        checkPasswdCreateKey(newPasswd1Content);
                        setError(null);
                        return newPasswd1Content;
                    } else {
                        setError(resources.getString("passwd.noMatch"));
                        return null;
                    }

                case AuthenticationPanel.AUTH_KEY_FILE:
                    final String keyFilePathname
                            = authenticationPanel.getKeyFilePath();
                    if (new File(keyFilePathname).canWrite()) {
                        setError(resources.getString("keyFile.canWrite"));
                        return null;
                    }

                    final byte[] key;
                    try {
                        key = PromptingKeyProviderUI.readKeyFile(keyFilePathname);
                    } catch (EOFException failure) {
                        setError(resources.getString("keyFile.eofException"));
                        return null;
                    } catch (FileNotFoundException failure) {
                        setError(resources.getString("keyFile.fileNotFoundException"));
                        return null;
                    } catch (IOException failure) {
                        setError(resources.getString("keyFile.ioException"));
                        return null;
                    }
                    checkKeyFileCreateKey(key);
                    setError(null);
                    return key;

                default:
                    throw new AssertionError("Unsupported authentication method!");
            }
        } catch (WeakKeyException failure) {
            setError(failure.getLocalizedMessage());
            return null;
        }
    }

    /**
     * Clears the password fields and the error message (but doesn't touch
     * the pathname in case a key file has been selected).
     *
     * @deprecated Create a new {@code CreateKeyPanel} instead.
     */
    public void resetCreateKey() {
        resetPasswd();
        setError(null);
    }

    /**
     * Clears the password fields.
     *
     * @deprecated Create a new {@code CreateKeyPanel} instead.
     */
    public void resetPasswd() {
        newPasswd1.setText(null);
        newPasswd2.setText(null);
    }

    /** Check the data entropy in the new key. */
    protected void checkKeyFileCreateKey(byte[] createKey)
    throws WeakKeyException {
        Deflater def = new Deflater();
        def.setInput(createKey);
        def.finish();
        assert def.getTotalOut() == 0;
        final int n = def.deflate(new byte[createKey.length * 2]);
        assert def.getTotalOut() == n;
        def.end();
        if (n < 2 * 256 / 8) // see RandomAccessEncryptionSpecification
            throw new WeakKeyException(
                    localizedMessage(resources, "keyFile.badEntropy", null));
    }

    protected void checkPasswdCreateKey(char[] createKey)
    throws WeakKeyException {
        if (createKey.length < MIN_PASSWD_LEN)
            throw new WeakKeyException(
                    localizedMessage(resources, "passwd.tooShort", new Integer(MIN_PASSWD_LEN)));
    }

    // TODO: Make this private.
    static final String localizedMessage(
            final ResourceBundle resources,
            final String key,
            final Object param) {
        return param != null
                ? MessageFormat.format(resources.getString(key), new Object[] { param })
                : resources.getString(key);
    }

    /**
     * Getter for property {@code error}.
     */
    public String getError() {
        final String error = this.error.getText();
        return error.trim().length() > 0 ? error : null;
    }
    
    /**
     * Setter for property error.
     *
     * @param error New value of property error.
     */
    public void setError(final String error) {
        // Fix layout issue with GridBagLayout:
        // If null is set, the layout seems to ignore the widthy = 1.0
        // constraint for the component.
        this.error.setText(error != null ? error : " ");
    }
    
    /**
     * Getter for property {@code extraDataUI}.
     * 
     * @return Value of property {@code extraDataUI}.
     */
    public JComponent getExtraDataUI() {
        return extraDataUI;
    }
    
    /**
     * Setter for property {@code extraDataUI}.
     * This component is placed below the two password fields and above the
     * error label.
     * It may be used to prompt the user for additional data which may form
     * part of the key or is separately stored in the key provider.
     * The panel is automatically revalidated.
     * 
     * @param extraDataUI New value of property {@code extraDataUI}.
     */
    public void setExtraDataUI(final JComponent extraDataUI) {
        if (this.extraDataUI == extraDataUI)
            return;

        if (this.extraDataUI != null) {
            remove(this.extraDataUI);
        }
        if (extraDataUI != null) {
            GridBagConstraints gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
            gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
            gridBagConstraints.insets = new Insets(15, 0, 0, 0);
            add(extraDataUI, gridBagConstraints);
        }
        this.extraDataUI = extraDataUI;

        revalidate();
    }

    /**
     * Returns the feedback to run when this panel is shown in its ancestor
     * window.
     */
    public Feedback getFeedback() {
        return feedback;
    }

    /**
     * Sets the feedback to run when this panel is shown in its ancestor
     * window.
     */
    public void setFeedback(final Feedback feedback) {
        this.feedback = feedback;
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // //GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        newPasswdPanel = new de.schlichtherle.swing.EnhancedPanel();
        newPasswd1Label = new javax.swing.JLabel();
        newPasswd1 = new javax.swing.JPasswordField();
        newPasswd2Label = new javax.swing.JLabel();
        newPasswd2 = new javax.swing.JPasswordField();
        final javax.swing.JLabel prompt = new javax.swing.JLabel();
        resourceID = new javax.swing.JTextPane();
        authenticationPanel = new de.schlichtherle.key.passwd.swing.AuthenticationPanel();
        error = new javax.swing.JLabel();

        newPasswdPanel.setLayout(new java.awt.GridBagLayout());

        newPasswdPanel.addPanelListener(new de.schlichtherle.swing.event.PanelListener() {
            public void ancestorWindowShown(de.schlichtherle.swing.event.PanelEvent evt) {
                newPasswdPanelAncestorWindowShown(evt);
            }
            public void ancestorWindowHidden(de.schlichtherle.swing.event.PanelEvent evt) {
            }
        });

        newPasswd1Label.setDisplayedMnemonic(resources.getString("newPasswd1").charAt(0));
        newPasswd1Label.setLabelFor(newPasswd1);
        newPasswd1Label.setText(resources.getString("newPasswd1")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
        newPasswdPanel.add(newPasswd1Label, gridBagConstraints);

        newPasswd1.setColumns(20);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
        newPasswdPanel.add(newPasswd1, gridBagConstraints);

        newPasswd2Label.setDisplayedMnemonic(resources.getString("newPasswd2").charAt(0));
        newPasswd2Label.setLabelFor(newPasswd2);
        newPasswd2Label.setText(resources.getString("newPasswd2")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5);
        newPasswdPanel.add(newPasswd2Label, gridBagConstraints);

        newPasswd2.setColumns(20);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        newPasswdPanel.add(newPasswd2, gridBagConstraints);

        setLayout(new java.awt.GridBagLayout());

        addPanelListener(new de.schlichtherle.swing.event.PanelListener() {
            public void ancestorWindowShown(de.schlichtherle.swing.event.PanelEvent evt) {
                formAncestorWindowShown(evt);
            }
            public void ancestorWindowHidden(de.schlichtherle.swing.event.PanelEvent evt) {
            }
        });

        prompt.setLabelFor(resourceID);
        prompt.setText(resources.getString("prompt")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
        add(prompt, gridBagConstraints);

        resourceID.setBorder(javax.swing.BorderFactory.createCompoundBorder(javax.swing.BorderFactory.createEtchedBorder(), javax.swing.BorderFactory.createEmptyBorder(2, 2, 2, 2)));
        resourceID.setEditable(false);
        resourceID.setFont(getBoldFont());
        resourceID.setOpaque(false);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 15, 0);
        add(resourceID, gridBagConstraints);

        authenticationPanel.setPasswdPanel(newPasswdPanel);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        add(authenticationPanel, gridBagConstraints);

        error.setForeground(new java.awt.Color(255, 0, 0));
        error.setText(" ");
        error.setName("error");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0);
        add(error, gridBagConstraints);

    }// //GEN-END:initComponents

    private void formAncestorWindowShown(de.schlichtherle.swing.event.PanelEvent evt) {//GEN-FIRST:event_formAncestorWindowShown
        final Feedback feedback = getFeedback();
        if (feedback != null) {
            feedback.setPanel(this);
            feedback.run();
        }
    }//GEN-LAST:event_formAncestorWindowShown

    private void newPasswdPanelAncestorWindowShown(de.schlichtherle.swing.event.PanelEvent evt) {//GEN-FIRST:event_newPasswdPanelAncestorWindowShown
        // These are the things I hate Swing for: All I want to do here is to
        // set the focus to the newPasswd1 field in this panel when it shows.
        // However, this can't be done in the constructor since the panel is
        // not yet placed in a window which is actually showing.
        // Right, then we use this event listener to do it. This listener
        // method is called when the ancestor window is showing (and coding
        // the event generation was a less than trivial task).
        // But wait, simply setting the focus in this event listener here is
        // not possible on Linux because the containing window (now we have
        // one) didn't gain the focus yet.
        // Strangely enough, this works on Windows.
        // Even more strange, not even calling passwd.requestFocus() makes it
        // work on Linux!
        // So we add a window focus listener here and remove it when we
        // receive a Focus Gained Event.
        // But wait, then we still can't request the focus: This time it
        // doesn't work on Windows, while it works on Linux.
        // I still don't know the reason why, but it seems we're moving too
        // fast, so I have to post a new event to the event queue which finally
        // sets the focus.
        // But wait, requesting the focus could still fail for some strange,
        // undocumented reason - I wouldn't be surprised anymore.
        // So we add a conditional to select the entire contents of the field
        // only if we can really transfer the focus to it.
        // Otherwise, users could get easily confused.
        // If you carefully read the documentation for requestFocusInWindow()
        // however, then you know that even if it returns true, there is still
        // no guarantee that the focus gets actually transferred...
        // This mess is insane (and I can hardly abstain from writing down
        // all the other insulting scatology which comes to my mind)!
        final Window window = evt.getAncestorWindow();
        window.addWindowFocusListener(new WindowFocusListener() {
            public void windowGainedFocus(WindowEvent e) {
                window.removeWindowFocusListener(this);
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        if (newPasswd1.requestFocusInWindow()) {
                            newPasswd1.selectAll();
                            newPasswd2.selectAll();
                        }
                    }
                });
            }

            public void windowLostFocus(WindowEvent e) {
            }
        });
    }//GEN-LAST:event_newPasswdPanelAncestorWindowShown
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private de.schlichtherle.key.passwd.swing.AuthenticationPanel authenticationPanel;
    private javax.swing.JLabel error;
    private javax.swing.JPasswordField newPasswd1;
    private javax.swing.JLabel newPasswd1Label;
    private javax.swing.JPasswordField newPasswd2;
    private javax.swing.JLabel newPasswd2Label;
    private de.schlichtherle.swing.EnhancedPanel newPasswdPanel;
    private javax.swing.JTextPane resourceID;
    // End of variables declaration//GEN-END:variables
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy