blob: fac5e156dfbf4b333bc3e33ac2d04bd5abc69738 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}