| /******************************************************************************* |
| * Copyright (C) 2007 The University of Manchester |
| * |
| * Modifications to the initial code base are copyright of their |
| * respective authors, or their employers as appropriate. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public License |
| * as published by the Free Software Foundation; either version 2.1 of |
| * the License, or (at your option) any later version. |
| * |
| * 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| ******************************************************************************/ |
| package net.sf.taverna.t2.workbench.ui.credentialmanager; |
| |
| import java.awt.BorderLayout; |
| import java.awt.FlowLayout; |
| import java.awt.Font; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.WindowAdapter; |
| import java.awt.event.WindowEvent; |
| import java.security.GeneralSecurityException; |
| import java.security.Key; |
| import java.security.KeyStore; |
| import java.security.cert.Certificate; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Set; |
| import java.util.Iterator; |
| |
| import javax.swing.JButton; |
| import javax.swing.JDialog; |
| import javax.swing.JFrame; |
| import javax.swing.JLabel; |
| import javax.swing.JList; |
| import javax.swing.DefaultListModel; |
| import javax.swing.JOptionPane; |
| import javax.swing.JPanel; |
| import javax.swing.JScrollPane; |
| import javax.swing.BoxLayout; |
| import javax.swing.ListSelectionModel; |
| import javax.swing.border.CompoundBorder; |
| import javax.swing.border.EmptyBorder; |
| import javax.swing.border.EtchedBorder; |
| import javax.swing.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| |
| import net.sf.taverna.t2.security.credentialmanager.CMException; |
| import net.sf.taverna.t2.security.credentialmanager.CMX509Util; |
| import net.sf.taverna.t2.security.credentialmanager.CredentialManager; |
| import net.sf.taverna.t2.workbench.ui.credentialmanager.GetServiceURLDialog; |
| import net.sf.taverna.t2.workbench.ui.credentialmanager.ViewCertDetailsDialog; |
| |
| /** |
| * Dialog that displays the details of all key pairs from a PKCS #12 |
| * keystore allowing the user to pick one for import. |
| * |
| * @author Alex Nenadic |
| */ |
| @SuppressWarnings("serial") |
| class NewKeyPairEntryDialog extends JDialog |
| { |
| // List of key pairs available for import |
| private JList jltKeyPairs; |
| |
| // Service URL text field for user to enter |
| private JList jltServiceURLs; |
| |
| // Service URL (associated with the key pair) |
| private ArrayList<String> serviceURLs; |
| |
| // PKCS #12 keystore */ |
| private KeyStore pkcs12KeyStore; |
| |
| // Private key part of key pair chosen by the user for import |
| private Key privateKey; |
| |
| // Certificate chain part of key pair chosen by the user for import |
| private Certificate[] certificateChain; |
| |
| // Key pair alias to be used for this entry in the Keystore |
| private String alias; |
| |
| /** |
| * Creates new form NewKeyPairEntryDialog where the parent is a frame. |
| */ |
| public NewKeyPairEntryDialog(JFrame parent, String title, boolean modal, KeyStore pkcs12KS) |
| throws CMException |
| { |
| super(parent, title, modal); |
| pkcs12KeyStore = pkcs12KS; |
| initComponents(); |
| } |
| |
| /** |
| * Creates new form NewKeyPairEntryDialog where the parent is a dialog. |
| */ |
| public NewKeyPairEntryDialog(JDialog parent, String title, boolean modal, KeyStore pkcs12KS) |
| throws CMException |
| { |
| super(parent, title, modal); |
| pkcs12KeyStore = pkcs12KS; |
| initComponents(); |
| } |
| |
| /** |
| * Get the private part of the key pair chosen by the user for import. |
| * |
| * @return The private key or null if the user has not chosen a key pair |
| */ |
| public Key getPrivateKey() |
| { |
| return privateKey; |
| } |
| |
| /** |
| * Get the certificate chain part of the key pair chosen by the |
| * user for import. |
| * |
| * @return The certificate chain or null if the user has not |
| * chosen a key pair |
| */ |
| public Certificate[] getCertificateChain() |
| { |
| return certificateChain; |
| } |
| |
| /** |
| * Get the alias of the key pair chosen by the user for import. |
| * |
| * @return the alias |
| */ |
| public String getAlias() |
| { |
| return alias; |
| } |
| |
| /** |
| * Get the service URLs entered by the user. |
| * |
| * @return list of service URLs |
| */ |
| public ArrayList<String> getServiceURLs() |
| { |
| return serviceURLs; |
| } |
| |
| /** |
| * Initialise the dialog's GUI components. |
| * |
| * @throws CMException A problem was encountered importing a key pair |
| */ |
| private void initComponents() |
| throws CMException |
| { |
| // Instructions |
| JLabel jlInstructions = new JLabel("Select a key pair for import:"); |
| jlInstructions.setFont(new Font(null, Font.PLAIN, 11)); |
| jlInstructions.setBorder(new EmptyBorder(5,5,5,5)); |
| JPanel jpInstructions = new JPanel(new BorderLayout()); |
| jpInstructions.add(jlInstructions, BorderLayout.WEST); |
| |
| // Import button |
| final JButton jbImport = new JButton("Import"); |
| jbImport.setEnabled(false); |
| jbImport.addActionListener(new ActionListener() |
| { |
| public void actionPerformed(ActionEvent evt) |
| { |
| importPressed(); |
| } |
| }); |
| |
| // Certificate details button |
| final JButton jbCertificateDetails = new JButton("Certificate Details"); |
| jbCertificateDetails.setEnabled(false); |
| jbCertificateDetails.addActionListener(new ActionListener() |
| { |
| public void actionPerformed(ActionEvent evt) |
| { |
| certificateDetailsPressed(); |
| } |
| }); |
| |
| // List to hold keystore's key pairs |
| jltKeyPairs = new JList(); |
| jltKeyPairs.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| jltKeyPairs.addListSelectionListener(new ListSelectionListener() |
| { |
| public void valueChanged(ListSelectionEvent evt) |
| { |
| if (jltKeyPairs.getSelectedIndex() == -1) { |
| jbImport.setEnabled(false); |
| jbCertificateDetails.setEnabled(false); |
| } |
| else { |
| jbImport.setEnabled(true); |
| jbCertificateDetails.setEnabled(true); |
| } |
| } |
| }); |
| |
| // Put the key list into a scroll pane |
| JScrollPane jspKeyPairs = new JScrollPane(jltKeyPairs, |
| JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, |
| JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); |
| jspKeyPairs.getViewport().setBackground(jltKeyPairs.getBackground()); |
| |
| // Service URLs list |
| // Label |
| JLabel jlServiceURL = new JLabel ("Service URLs the key pair will be used for:"); |
| jlServiceURL.setFont(new Font(null, Font.PLAIN, 11)); |
| jlServiceURL.setBorder(new EmptyBorder(5,5,5,5)); |
| // New empty service URLs list |
| DefaultListModel jltModel = new DefaultListModel(); |
| jltServiceURLs = new JList(jltModel); |
| // 'Add' service URL button |
| JButton addButton = new JButton("Add"); |
| addButton.addActionListener(new ActionListener(){ |
| public void actionPerformed(ActionEvent evt) |
| { |
| addServiceURLPressed(); |
| } |
| }); |
| addButton.setEnabled(true); |
| // 'Remove' service URL button |
| final JButton removeButton = new JButton("Remove"); |
| removeButton.addActionListener(new ActionListener(){ |
| public void actionPerformed(ActionEvent evt) |
| { |
| // get selected indices |
| int[] selected = jltServiceURLs.getSelectedIndices(); |
| for (int i = selected.length -1; i>=0 ; i--){ |
| ((DefaultListModel) jltServiceURLs.getModel()).remove(selected[i]); |
| } |
| } |
| }); |
| removeButton.setEnabled(false); |
| jltServiceURLs.addListSelectionListener(new ListSelectionListener() |
| { |
| public void valueChanged(ListSelectionEvent evt) |
| { |
| if (jltServiceURLs.getSelectedIndex() == -1) { |
| removeButton.setEnabled(false); |
| } |
| else { |
| removeButton.setEnabled(true); |
| } |
| } |
| }); |
| // Scroll pane for service URLs list |
| JScrollPane jspServiceURLs = new JScrollPane(jltServiceURLs, |
| JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, |
| JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); |
| jspServiceURLs.getViewport().setBackground(jltServiceURLs.getBackground()); |
| |
| // Panel for Add and Remove buttons |
| JPanel jpServiceURLsButtons = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
| jpServiceURLsButtons.add(addButton); |
| jpServiceURLsButtons.add(removeButton); |
| |
| // Panel to hold the list scroll pane and Add/Remove buttons panel |
| JPanel jpServiceURLs = new JPanel(new BorderLayout()); |
| jpServiceURLs.add(jlServiceURL, BorderLayout.NORTH); |
| jpServiceURLs.add(jspServiceURLs, BorderLayout.CENTER); |
| jpServiceURLs.add(jpServiceURLsButtons, BorderLayout.SOUTH); |
| |
| // Put all the key pair components together |
| JPanel jpKeyPairs = new JPanel(); // BoxLayout |
| jpKeyPairs.setLayout(new BoxLayout(jpKeyPairs, BoxLayout.Y_AXIS)); |
| //jpKeyPairs.setPreferredSize(new Dimension(400, 200)); |
| jpKeyPairs.setBorder(new CompoundBorder(new CompoundBorder( |
| new EmptyBorder(5, 5, 5, 5), new EtchedBorder()), new EmptyBorder( |
| 5, 5, 5, 5))); |
| |
| jpInstructions.setAlignmentY(JPanel.LEFT_ALIGNMENT); |
| jpKeyPairs.add(jpInstructions); |
| jspKeyPairs.setAlignmentY(JPanel.LEFT_ALIGNMENT); |
| jpKeyPairs.add(jspKeyPairs); |
| jbCertificateDetails.setAlignmentY(JPanel.RIGHT_ALIGNMENT); |
| jpKeyPairs.add(jbCertificateDetails); |
| jpServiceURLs.setAlignmentY(JPanel.LEFT_ALIGNMENT); |
| jpKeyPairs.add(jpServiceURLs); |
| |
| // Cancel button |
| final JButton jbCancel = new JButton("Cancel"); |
| jbCancel.addActionListener(new ActionListener() |
| { |
| public void actionPerformed(ActionEvent evt) |
| { |
| cancelPressed(); |
| } |
| }); |
| |
| JPanel jpButtons = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
| jpButtons.add(jbImport); |
| jpButtons.add(jbCancel); |
| |
| getContentPane().setLayout(new BorderLayout()); |
| getContentPane().add(jpKeyPairs, BorderLayout.CENTER); |
| getContentPane().add(jpButtons, BorderLayout.SOUTH); |
| |
| // Populate the list |
| populateList(); |
| |
| addWindowListener(new WindowAdapter() |
| { |
| public void windowClosing(WindowEvent evt) |
| { |
| closeDialog(); |
| } |
| }); |
| |
| setResizable(false); |
| |
| getRootPane().setDefaultButton(jbImport); |
| |
| pack(); |
| |
| } |
| |
| /** |
| * Populate the key pair list with the PKCS #12 keystore's key |
| * pair aliases. |
| * |
| * @throws CMException Problem accessing the keystore's entries |
| */ |
| private void populateList() |
| throws CMException |
| { |
| try { |
| ArrayList<String> vKeyPairAliases = new ArrayList<String>(); |
| |
| // For each entry in the keystore... |
| for (Enumeration<String> aliases = pkcs12KeyStore.aliases(); aliases.hasMoreElements();) |
| { |
| // Get alias |
| String sAlias = (String) aliases.nextElement(); |
| |
| // Add the alias to the list if the entry has a key |
| // and certificates |
| if (pkcs12KeyStore.isKeyEntry(sAlias)) { |
| pkcs12KeyStore.getKey(sAlias, new char[] {}); |
| Certificate[] certs = pkcs12KeyStore.getCertificateChain(sAlias); |
| |
| if (certs != null && certs.length != 0) { |
| vKeyPairAliases.add(sAlias); |
| } |
| } |
| } |
| |
| if (vKeyPairAliases.size() > 0) { |
| jltKeyPairs.setListData(vKeyPairAliases.toArray()); |
| jltKeyPairs.setSelectedIndex(0); |
| } |
| else { |
| // No key pairs available... |
| jltKeyPairs.setListData(new String[] { "-- No key pairs present in the Credential Store --" }); |
| jltKeyPairs.setEnabled(false); |
| } |
| } |
| catch (GeneralSecurityException ex) { |
| throw new CMException("Problem occured while accessing PKCS #12 keystore's entries.", |
| ex); |
| } |
| } |
| |
| /** |
| * 'Certificate Details' button pressed. Display the selected key |
| * pair's certificate. |
| */ |
| private void certificateDetailsPressed() |
| { |
| try { |
| |
| String sAlias = (String) jltKeyPairs.getSelectedValue(); |
| |
| assert sAlias != null; |
| |
| //Convert the certificate object into an X509Certificate object. |
| X509Certificate cert = CMX509Util.convertCertificate(pkcs12KeyStore.getCertificate(sAlias)); |
| |
| // Supply the certificate to the view certificate dialog |
| ViewCertDetailsDialog viewCertificateDialog = new ViewCertDetailsDialog(this, |
| "Certificate details", |
| true, |
| (X509Certificate) cert, |
| null); |
| viewCertificateDialog.setLocationRelativeTo(this); |
| viewCertificateDialog.setVisible(true); |
| |
| } |
| catch (Exception ex) { |
| |
| JOptionPane.showMessageDialog(this, |
| "Failed to obtain certificate details to show.", |
| "Credential Manager Alert", |
| JOptionPane.WARNING_MESSAGE); |
| closeDialog(); |
| } |
| } |
| |
| |
| /** |
| * Import button pressed by user. Store the selected key pair's |
| * private and public parts and service URLs and close the dialog. |
| */ |
| public void importPressed() |
| { |
| // Get Service URLs |
| serviceURLs = new ArrayList<String>(); |
| Enumeration<?> URLs = (((DefaultListModel) jltServiceURLs.getModel()).elements()); |
| for( ; URLs.hasMoreElements(); ){ |
| serviceURLs.add((String) URLs.nextElement()); |
| } |
| |
| String sAlias = (String) jltKeyPairs.getSelectedValue(); |
| |
| assert sAlias != null; |
| |
| try { |
| privateKey = pkcs12KeyStore.getKey(sAlias, new char[] {}); |
| certificateChain = pkcs12KeyStore.getCertificateChain(sAlias); |
| alias = sAlias; |
| } |
| catch (Exception ex) { |
| JOptionPane.showMessageDialog(this, |
| "Failed to load the private key and certificate chain from PKCS #12 file.", |
| "Credential Manager Error", |
| JOptionPane.ERROR_MESSAGE); |
| closeDialog(); |
| } |
| |
| closeDialog(); |
| } |
| |
| /** |
| * Add Service URL button pressed. |
| */ |
| public void addServiceURLPressed(){ |
| |
| // Display the dialog for entering service URL |
| GetServiceURLDialog dGetServiceURL = new GetServiceURLDialog(this, true); |
| |
| dGetServiceURL.setLocationRelativeTo(this); |
| dGetServiceURL.setVisible(true); |
| |
| String sURL = dGetServiceURL.getServiceURL(); |
| |
| if (sURL == null){ // user cancelled |
| return; |
| } |
| |
| if (sURL.length() == 0){ // user entered an empty URL |
| // Warn the user |
| JOptionPane.showMessageDialog( |
| this, |
| "Service URL cannot be empty", |
| "Credential Manager Alert", |
| JOptionPane.INFORMATION_MESSAGE); |
| return; |
| } |
| |
| // Check if the entered URL already exist in the URL list for this key entry |
| if (((DefaultListModel) jltServiceURLs.getModel()).contains(sURL)){ |
| |
| // Warn the user |
| JOptionPane.showMessageDialog( |
| this, |
| "The entered URL already exists in the list of URLs for this key pair entry", |
| "Credential Manager Alert", |
| JOptionPane.INFORMATION_MESSAGE); |
| return; |
| } |
| |
| // Check if the entered URL is already associated with another key pair entry in the Keystore |
| CredentialManager credManager = null; |
| try { |
| credManager = CredentialManager.getInstance(); |
| } catch (CMException cme) { |
| // Failed to instantiate Credential Manager - warn the user and exit |
| String sMessage = "Failed to instantiate Credential Manager. " + cme.getMessage(); |
| cme.printStackTrace(); |
| JOptionPane.showMessageDialog(new JFrame(), sMessage, |
| "Credential Manager Error", JOptionPane.ERROR_MESSAGE); |
| return; |
| } |
| // Get the lists of URLs for the alias |
| HashMap<String, ArrayList<String>> serviceURLsMap = credManager.getServiceURLsforKeyPairs(); |
| if (serviceURLsMap != null){ // should not be null really (although can be empty). Check anyway. |
| Set<String> aliases = serviceURLsMap.keySet(); |
| for (Iterator<String> i = aliases.iterator(); i.hasNext(); ){ |
| String alias = (String) i.next(); |
| // Check if service URL list for this alias contains the newly entered URL |
| ArrayList<String> urls = (ArrayList<String>) serviceURLsMap.get(alias); |
| if (urls.contains(sURL)){ |
| // Warn the user and exit |
| JOptionPane.showMessageDialog( |
| this, |
| "The entered URL is already associated with another key pair entry", |
| "Credential Manager Alert", |
| JOptionPane.INFORMATION_MESSAGE); |
| return; |
| } |
| } |
| } |
| |
| // Check if the entered URL is already associated with a password entry in the Keystore |
| // ArrayList<String> urlList = (ArrayList<String>) ((CredentialManagerUI) this.getParent()).getURLsForPasswords(); |
| // // Check if this url list contains the newly entered url |
| // if (urlList.contains(sURL)){ |
| // // Warn the user and exit |
| // JOptionPane.showMessageDialog( |
| // this, |
| // "The entered URL is already associated with a password entry", |
| // "Credential Manager Alert", |
| // JOptionPane.INFORMATION_MESSAGE); |
| // return; |
| // } |
| |
| // Otherwise - the entered URL is not already associated with a different entry in the Keystore, |
| // so add this URL to the list of URLs for this key pair entry |
| ((DefaultListModel) jltServiceURLs.getModel()).addElement(sURL); |
| int index = ((DefaultListModel) jltServiceURLs.getModel()).getSize() - 1; |
| // Element is appended to the list - get its index |
| jltServiceURLs.setSelectedIndex(index); |
| // Insure the newly added URL is visible |
| jltServiceURLs.ensureIndexIsVisible(index); |
| } |
| |
| /** |
| * Cancel button pressed - close the dialog. |
| */ |
| public void cancelPressed() |
| { |
| // set everything to null, just in case some of the values have been set previously and |
| // the user pressed 'cancel' after that |
| privateKey = null; |
| certificateChain = null; |
| serviceURLs = null; |
| closeDialog(); |
| } |
| |
| /** |
| * Closes the dialog. |
| */ |
| private void closeDialog() |
| { |
| setVisible(false); |
| dispose(); |
| } |
| |
| } |
| |