| /******************************************************************************* |
| * 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 static java.awt.BorderLayout.CENTER; |
| import static java.awt.BorderLayout.SOUTH; |
| import static java.awt.BorderLayout.WEST; |
| import static java.awt.Font.PLAIN; |
| import static javax.swing.BoxLayout.Y_AXIS; |
| import static javax.swing.JOptionPane.ERROR_MESSAGE; |
| import static javax.swing.JOptionPane.WARNING_MESSAGE; |
| import static javax.swing.JOptionPane.showMessageDialog; |
| import static javax.swing.ListSelectionModel.SINGLE_SELECTION; |
| import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; |
| import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; |
| import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE; |
| import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; |
| |
| 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.List; |
| |
| import javax.swing.BoxLayout; |
| import javax.swing.JButton; |
| import javax.swing.JDialog; |
| import javax.swing.JFrame; |
| import javax.swing.JLabel; |
| import javax.swing.JList; |
| import javax.swing.JPanel; |
| import javax.swing.JScrollPane; |
| import javax.swing.border.EmptyBorder; |
| 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.DistinguishedNameParser; |
| import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; |
| |
| /** |
| * Allows the user import a key pair from a PKCS #12 file (keystore). |
| */ |
| @SuppressWarnings("serial") |
| class NewKeyPairEntryDialog extends NonBlockedHelpEnabledDialog { |
| //private static final Logger logger = Logger.getLogger(NewKeyPairEntryDialog.class); |
| |
| /** List of key pairs available for import */ |
| private JList<String> keyPairsJList; |
| /** PKCS #12 keystore */ |
| private KeyStore pkcs12KeyStore; |
| /** Private key part of the key pair chosen by the user for import */ |
| private Key privateKey; |
| /** Certificate chain part of the 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; |
| private final DistinguishedNameParser dnParser; |
| |
| public NewKeyPairEntryDialog(JFrame parent, String title, boolean modal, |
| KeyStore pkcs12KeyStore, DistinguishedNameParser dnParser) |
| throws CMException { |
| super(parent, title, modal); |
| this.pkcs12KeyStore = pkcs12KeyStore; |
| this.dnParser = dnParser; |
| initComponents(); |
| } |
| |
| public NewKeyPairEntryDialog(JDialog parent, String title, boolean modal, |
| KeyStore pkcs12KeyStore, DistinguishedNameParser dnParser) |
| throws CMException { |
| super(parent, title, modal); |
| this.pkcs12KeyStore = pkcs12KeyStore; |
| this.dnParser = dnParser; |
| initComponents(); |
| } |
| |
| /** |
| * Get the private part of the key pair. |
| */ |
| public Key getPrivateKey() { |
| return privateKey; |
| } |
| |
| /** |
| * Get the certificate chain part of the key pair. |
| */ |
| public Certificate[] getCertificateChain() { |
| return certificateChain; |
| } |
| |
| /** |
| * Get the keystore alias of the key pair. |
| */ |
| public String getAlias() { |
| return alias; |
| } |
| |
| private void initComponents() throws CMException { |
| // Instructions |
| JLabel instructionsLabel = new JLabel("Select a key pair to import:"); |
| instructionsLabel.setFont(new Font(null, PLAIN, 11)); |
| instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); |
| JPanel instructionsPanel = new JPanel(new BorderLayout()); |
| instructionsPanel.add(instructionsLabel, WEST); |
| |
| // Import button |
| final JButton importButton = new JButton("Import"); |
| importButton.setEnabled(false); |
| importButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent evt) { |
| importPressed(); |
| } |
| }); |
| |
| // Certificate details button |
| final JButton certificateDetailsButton = new JButton("Details"); |
| certificateDetailsButton.setEnabled(false); |
| certificateDetailsButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent evt) { |
| certificateDetailsPressed(); |
| } |
| }); |
| |
| // List to hold keystore's key pairs |
| keyPairsJList = new JList<>(); |
| keyPairsJList.setSelectionMode(SINGLE_SELECTION); |
| keyPairsJList.addListSelectionListener(new ListSelectionListener() { |
| @Override |
| public void valueChanged(ListSelectionEvent evt) { |
| boolean enabled = keyPairsJList.getSelectedIndex() >= 0; |
| importButton.setEnabled(enabled); |
| certificateDetailsButton.setEnabled(enabled); |
| } |
| }); |
| |
| // Put the key list into a scroll pane |
| JScrollPane keyPairsScrollPane = new JScrollPane(keyPairsJList, |
| VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); |
| keyPairsScrollPane.getViewport().setBackground( |
| keyPairsJList.getBackground()); |
| |
| JPanel keyPairsPanel = new JPanel(); |
| keyPairsPanel.setLayout(new BoxLayout(keyPairsPanel, Y_AXIS)); |
| keyPairsPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); |
| |
| instructionsPanel.setAlignmentY(LEFT_ALIGNMENT); |
| keyPairsPanel.add(instructionsPanel); |
| keyPairsScrollPane.setAlignmentY(LEFT_ALIGNMENT); |
| keyPairsPanel.add(keyPairsScrollPane); |
| |
| // Cancel button |
| final JButton cancelButton = new JButton("Cancel"); |
| cancelButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent evt) { |
| cancelPressed(); |
| } |
| }); |
| |
| JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
| buttonsPanel.add(certificateDetailsButton); |
| buttonsPanel.add(importButton); |
| buttonsPanel.add(cancelButton); |
| |
| getContentPane().setLayout(new BorderLayout()); |
| getContentPane().add(keyPairsPanel, CENTER); |
| getContentPane().add(buttonsPanel, SOUTH); |
| |
| // Populate the list |
| populateKeyPairList(); |
| |
| addWindowListener(new WindowAdapter() { |
| @Override |
| public void windowClosing(WindowEvent evt) { |
| closeDialog(); |
| } |
| }); |
| |
| setResizable(false); |
| getRootPane().setDefaultButton(importButton); |
| pack(); |
| } |
| |
| /** |
| * Populate the key pair list with the PKCS #12 keystore's key pair aliases. |
| */ |
| private void populateKeyPairList() throws CMException { |
| try { |
| List<String> keyPairAliases = new ArrayList<>(); |
| |
| Enumeration<String> aliases = pkcs12KeyStore.aliases(); |
| while (aliases.hasMoreElements()) { |
| String alias = aliases.nextElement(); |
| |
| if (pkcs12KeyStore.isKeyEntry(alias)) { |
| pkcs12KeyStore.getKey(alias, new char[] {}); |
| Certificate[] certs = pkcs12KeyStore |
| .getCertificateChain(alias); |
| if (certs != null && certs.length != 0) |
| keyPairAliases.add(alias); |
| } |
| } |
| |
| if (!keyPairAliases.isEmpty()) { |
| keyPairsJList.setListData(keyPairAliases.toArray(new String[0])); |
| keyPairsJList.setSelectedIndex(0); |
| } else |
| // No key pairs were found - warn the user |
| showMessageDialog(this, |
| "No private key pairs were found in the file", |
| ALERT_TITLE, WARNING_MESSAGE); |
| } catch (GeneralSecurityException ex) { |
| throw new CMException("Problem occured while reading the PKCS #12 file.", |
| ex); |
| } |
| } |
| |
| /** |
| * Display the selected key pair's certificate. |
| */ |
| private void certificateDetailsPressed() { |
| try { |
| String alias = (String) keyPairsJList.getSelectedValue(); |
| |
| // Convert the certificate object into an X509Certificate object. |
| X509Certificate cert = dnParser.convertCertificate(pkcs12KeyStore |
| .getCertificate(alias)); |
| |
| ViewCertDetailsDialog viewCertificateDialog = new ViewCertDetailsDialog( |
| this, "Certificate details", true, (X509Certificate) cert, |
| null, dnParser); |
| viewCertificateDialog.setLocationRelativeTo(this); |
| viewCertificateDialog.setVisible(true); |
| } catch (Exception ex) { |
| showMessageDialog(this, |
| "Failed to obtain certificate details to show", |
| ALERT_TITLE, WARNING_MESSAGE); |
| closeDialog(); |
| } |
| } |
| |
| public void importPressed() { |
| String alias = (String) keyPairsJList.getSelectedValue(); |
| try { |
| privateKey = pkcs12KeyStore.getKey(alias, new char[] {}); |
| certificateChain = pkcs12KeyStore.getCertificateChain(alias); |
| this.alias = alias; |
| } catch (Exception ex) { |
| showMessageDialog( |
| this, |
| "Failed to load the private key and certificate chain from the PKCS #12 file.", |
| ERROR_TITLE, ERROR_MESSAGE); |
| } |
| |
| closeDialog(); |
| } |
| |
| 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; |
| closeDialog(); |
| } |
| |
| private void closeDialog() { |
| setVisible(false); |
| dispose(); |
| } |
| |
| } |