/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.plugin.certconfig;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import javax.security.auth.callback.*;
import javax.swing.*;
import javax.swing.event.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.util.Logger;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
/**
* Dialog window to add/edit client certificate configuration entries.
*
* @author Ingo Bauersachs
*/
public class CertConfigEntryDialog
extends SIPCommDialog
implements ActionListener, ItemListener, ChangeListener
{
// ------------------------------------------------------------------------
// Fields and services
// ------------------------------------------------------------------------
private static final long serialVersionUID = 8361336563239745007L;
private static final Logger logger = Logger
.getLogger(CertConfigEntryDialog.class);
private ResourceManagementService R = CertConfigActivator.R;
private CertificateService cs = CertConfigActivator.getCertService();
private CertificateConfigEntry entry;
private boolean success = false;
// ------------------------------------------------------------------------
// GUI members
// ------------------------------------------------------------------------
private JButton cmdOk;
private JButton cmdCancel;
private JButton cmdBrowse;
private JTextField txtDisplayName;
private JTextField txtKeyStore;
private JComboBox cboKeyStoreTypes;
private JCheckBox chkSavePassword;
private JPasswordField txtKeyStorePassword;
private JComboBox cboAlias;
private JButton cmdShowCert;
private KeyStore keyStore;
// ------------------------------------------------------------------------
// Initialization
// ------------------------------------------------------------------------
/**
* Constructor.
*
* @param e the CertificateConfigEntry
*/
public CertConfigEntryDialog(CertificateConfigEntry e)
{
super(false);
entry = e;
initComponents();
setPreferredSize(new Dimension(650, 270));
try
{
if(entry.getKeyStore() != null)
{
txtKeyStorePassword.setText(entry.getKeyStorePassword());
chkSavePassword.setSelected(entry.isSavePassword());
cboKeyStoreTypes.setEnabled(true);
cboKeyStoreTypes.setSelectedItem(entry.getKeyStoreType());
if(keyStore == null)
keyStore = loadKeyStore();
cboAlias.setEnabled(true);
loadAliases();
cboAlias.setSelectedItem(entry.getAlias());
}
}
catch (KeyStoreException ex)
{
logger.error("Unable to load all data", ex);
showGenericError("plugin.certconfig.KEYSTORE_EXCEPTION", ex);
}
catch (ProviderException ex)
{
logger.error("Unable to load all data", ex);
showGenericError("plugin.certconfig.KEYSTORE_EXCEPTION", ex);
}
}
private void initComponents()
{
setTitle(R.getI18NString("plugin.certconfig.EDIT_ENTRY"));
setLayout(new BorderLayout());
JPanel fields = new TransparentPanel();
fields.setLayout(new GridBagLayout());
JLabel lblDisplayName = new JLabel();
lblDisplayName.setText(R.getI18NString("service.gui.DISPLAY_NAME"));
txtDisplayName = new JTextField();
txtDisplayName.setText(entry.getDisplayName());
JLabel lblKeyStore = new JLabel();
lblKeyStore.setText(R.getI18NString("plugin.certconfig.KEYSTORE"));
txtKeyStore = new JTextField();
txtKeyStore.setText(entry.getKeyStore());
txtKeyStore.setEditable(false);
cmdBrowse = new JButton();
cmdBrowse.setText(R.getI18NString("service.gui.BROWSE"));
cmdBrowse.addActionListener(this);
JLabel lblKeyStorePassword = new JLabel();
lblKeyStorePassword.setText(
R.getI18NString("plugin.certconfig.KEYSTORE_PASSWORD"));
txtKeyStorePassword = new JPasswordField();
txtKeyStorePassword.setEditable(false);
chkSavePassword = new SIPCommCheckBox();
chkSavePassword.setText(
R.getI18NString("service.gui.REMEMBER_PASSWORD"));
chkSavePassword.addChangeListener(this);
chkSavePassword.setEnabled(false);
JLabel lblKeyStoreType = new JLabel();
lblKeyStoreType.setText(
R.getI18NString("plugin.certconfig.KEYSTORE_TYPE"));
cboKeyStoreTypes =
new JComboBox(cs.getSupportedKeyStoreTypes().toArray());
cboKeyStoreTypes.addItemListener(this);
cboKeyStoreTypes.setEnabled(false);
JLabel lblAlias = new JLabel();
lblAlias.setText(R.getI18NString("plugin.certconfig.ALIAS"));
cboAlias = new JComboBox();
cboAlias.addItemListener(this);
cboAlias.setEnabled(false);
cmdShowCert = new JButton();
cmdShowCert.setText(R.getI18NString("service.gui.SHOW_CERT") + "...");
cmdShowCert.addActionListener(this);
cmdShowCert.setEnabled(false);
cmdCancel = new JButton();
cmdCancel.setText(R.getI18NString("service.gui.CANCEL"));
cmdCancel.addActionListener(this);
cmdOk = new JButton();
cmdOk.setText(R.getI18NString("service.gui.OK"));
cmdOk.addActionListener(this);
cmdOk.setPreferredSize(cmdCancel.getPreferredSize());
TransparentPanel buttons = new TransparentPanel();
buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
buttons.add(cmdOk);
buttons.add(cmdCancel);
GridBagConstraints first = new GridBagConstraints();
first.gridx = 0;
first.gridy = 0;
first.weightx = 0;
first.anchor = GridBagConstraints.LINE_START;
first.gridwidth = 1;
first.insets = new Insets(2,4,2,4);
first.fill = GridBagConstraints.HORIZONTAL;
GridBagConstraints second = new GridBagConstraints();
second.gridx = 1;
second.gridy = 0;
second.weightx = 2;
second.anchor = GridBagConstraints.LINE_START;
second.gridwidth = 1; //GridBagConstraints.REMAINDER;
second.insets = first.insets;
second.fill = GridBagConstraints.HORIZONTAL;
GridBagConstraints third = new GridBagConstraints();
third.gridx = 2;
third.gridy = 0;
third.weightx = 1;
third.anchor = GridBagConstraints.LINE_END;
third.gridwidth = 1;
third.insets = first.insets;
third.fill = GridBagConstraints.HORIZONTAL;
fields.add(lblDisplayName, first);
fields.add(txtDisplayName, second);
first.gridy = second.gridy = ++third.gridy;
fields.add(lblKeyStore, first);
fields.add(txtKeyStore, second);
fields.add(cmdBrowse, third);
first.gridy = second.gridy = ++third.gridy;
fields.add(lblKeyStoreType, first);
fields.add(cboKeyStoreTypes, second);
first.gridy = second.gridy = ++third.gridy;
fields.add(lblKeyStorePassword, first);
fields.add(txtKeyStorePassword, second);
first.gridy = second.gridy = ++third.gridy;
fields.add(chkSavePassword, second);
first.gridy = second.gridy = ++third.gridy;
fields.add(lblAlias, first);
fields.add(cboAlias, second);
fields.add(cmdShowCert, third);
add(fields, BorderLayout.CENTER);
add(buttons, BorderLayout.SOUTH);
}
// ------------------------------------------------------------------------
// Event handling
// ------------------------------------------------------------------------
@Override
protected void close(boolean escaped)
{
cmdCancel.doClick();
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == cmdOk)
{
if(cboAlias.getSelectedItem() == null
|| StringUtils.isNullOrEmpty(txtDisplayName.getText())
|| StringUtils.isNullOrEmpty(txtKeyStore.getText()))
{
JOptionPane.showMessageDialog(this,
R.getI18NString("plugin.certconfig.INCOMPLETE"),
R.getI18NString("service.gui.ERROR"),
JOptionPane.ERROR_MESSAGE);
return;
}
entry.setAlias(cboAlias.getSelectedItem().toString());
entry.setDisplayName(txtDisplayName.getText());
entry.setSavePassword(chkSavePassword.isSelected());
entry.setKeyStorePassword(
new String(txtKeyStorePassword.getPassword()));
entry.setKeyStoreType(
(KeyStoreType) cboKeyStoreTypes.getSelectedItem());
entry.setKeyStore(txtKeyStore.getText());
success = true;
dispose();
}
if(e.getSource() == cmdCancel)
{
dispose();
}
if(e.getSource() == cmdBrowse)
{
browseKeyStore();
}
if(e.getSource() == cmdShowCert)
{
showSelectedCertificate();
}
}
private void showSelectedCertificate()
{
try
{
@SuppressWarnings("serial")
SIPCommDialog dlg = new SIPCommDialog(this, false)
{
private JButton cmdClose;
{
setTitle(cboAlias.getSelectedItem().toString());
setLayout(new BorderLayout());
final JScrollPane certScroll =
new JScrollPane(new X509CertificatePanel(
(X509Certificate) keyStore.getCertificate(cboAlias
.getSelectedItem().toString())));
certScroll.setPreferredSize(new Dimension(600, 300));
certScroll.getVerticalScrollBar().setValue(0);
add(certScroll, BorderLayout.CENTER);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
certScroll.getVerticalScrollBar().setValue(0);
}
});
cmdClose = new JButton();
cmdClose.setText(R.getI18NString("service.gui.CLOSE"));
cmdClose.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dispose();
}
});
TransparentPanel buttons =
new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
buttons.add(cmdClose);
add(buttons, BorderLayout.SOUTH);
setLocationRelativeTo(cmdShowCert);
}
@Override
protected void close(boolean escaped)
{
cmdClose.doClick();
}
};
dlg.setModal(true);
dlg.setVisible(true);
}
catch (KeyStoreException e1)
{
logger.error("Unable to show the selected certificate", e1);
showGenericError("plugin.certconfig.SHOW_CERT_EXCEPTION", e1);
}
}
/**
* Opens a FileChoserDialog to let the user pick a keystore and tries to
* auto-detect the keystore type using the file extension
*/
private void browseKeyStore()
{
SipCommFileChooser dlg =
GenericFileDialog.create(null,
R.getI18NString("plugin.certconfig.BROWSE_KEYSTORE"),
SipCommFileChooser.LOAD_FILE_OPERATION);
dlg.setSelectionMode(SipCommFileChooser.FILES_ONLY);
dlg.addFilter(new SipCommFileFilter()
{
@Override
public String getDescription()
{
return R
.getI18NString("plugin.certconfig.FILE_TYPE_DESCRIPTION");
}
@Override
public boolean accept(File f)
{
for(KeyStoreType kt : cs.getSupportedKeyStoreTypes())
for(String ext : kt.getFileExtensions())
if(f.getName().endsWith(ext))
return true;
return false;
}
});
File f = dlg.getFileFromDialog();
if(f != null)
{
cboKeyStoreTypes.setEnabled(true);
cboKeyStoreTypes.setSelectedItem(null);
cboAlias.setEnabled(true);
txtKeyStore.setText(f.getAbsolutePath());
for(KeyStoreType kt: cs.getSupportedKeyStoreTypes())
for(String ext : kt.getFileExtensions())
if(f.getName().endsWith(ext))
cboKeyStoreTypes.setSelectedItem(kt);
}
}
/**
* Open the keystore selected by the user. If the type is set as PKCS#11,
* the file is loaded as a provider. If the store is protected by a
* password, the user is being asked by an authentication dialog.
*
* @return The loaded keystore
* @throws KeyStoreException when something goes wrong
*/
private KeyStore loadKeyStore() throws KeyStoreException
{
final File f = new File(txtKeyStore.getText());
final KeyStoreType kt =
(KeyStoreType) cboKeyStoreTypes.getSelectedItem();
if("PKCS11".equals(kt.getName()))
{
String config =
"name=" + f.getName() + "\nlibrary=" + f.getAbsoluteFile();
try
{
Class> pkcs11c =
Class.forName("sun.security.pkcs11.SunPKCS11");
Constructor> c = pkcs11c.getConstructor(InputStream.class);
Provider p =
(Provider) c.newInstance(new ByteArrayInputStream(config
.getBytes()));
Security.insertProviderAt(p, 0);
}
catch (Exception e)
{
logger.error("Tried to access the PKCS11 provider on an "
+ "unsupported platform or the load failed", e);
}
}
KeyStore.Builder ksBuilder = KeyStore.Builder.newInstance(
kt.getName(),
null,
f,
new KeyStore.CallbackHandlerProtection(new CallbackHandler()
{
public void handle(Callback[] callbacks)
throws IOException,
UnsupportedCallbackException
{
for(Callback cb : callbacks)
{
if(!(cb instanceof PasswordCallback))
throw new UnsupportedCallbackException(cb);
PasswordCallback pwcb = (PasswordCallback)cb;
if(
(
txtKeyStorePassword.getPassword() != null
&& txtKeyStorePassword.getPassword().length>0
)
|| chkSavePassword.isSelected())
{
pwcb.setPassword(txtKeyStorePassword.getPassword());
return;
}
AuthenticationWindow aw = new AuthenticationWindow(
CertConfigEntryDialog.this,
f.getName(),
null,
kt.getName(),
false,
null
);
aw.setAllowSavePassword(!"PKCS11".equals(kt.getName()));
aw.setVisible(true);
if(!aw.isCanceled())
{
pwcb.setPassword(aw.getPassword());
if (!"PKCS11".equals(kt.getName())
&& aw.isRememberPassword())
{
txtKeyStorePassword.setText(new String(aw
.getPassword()));
}
chkSavePassword.setSelected(aw
.isRememberPassword());
}
else
throw new IOException("User cancel");
}
}
}));
return ksBuilder.getKeyStore();
}
/**
* Load the certificate entry aliases from the chosen keystore.
*/
private void loadAliases()
{
String currentDisplayName = txtDisplayName.getText();
String currentAlias =
cboAlias.getSelectedItem() == null ? null : cboAlias
.getSelectedItem().toString();
try
{
cboAlias.removeAllItems();
Enumeration e = keyStore.aliases();
while(e.hasMoreElements())
{
cboAlias.addItem(e.nextElement());
}
// if the display name is empty or identical to the alias, set it
// to the alias of the newly selected cert
if(
(
StringUtils.isNullOrEmpty(currentDisplayName)
|| (
currentDisplayName != null
&& currentDisplayName.equals(currentAlias)
)
)
&& cboAlias.getSelectedItem() != null)
{
txtDisplayName.setText(cboAlias.getSelectedItem().toString());
}
}
catch (KeyStoreException e)
{
cboAlias.removeAllItems();
logger.error("Unable to obtain aliases from keystore", e);
showGenericError("plugin.certconfig.ALIAS_LOAD_EXCEPTION", e);
}
}
private void showGenericError(String msg, Throwable e)
{
JOptionPane.showMessageDialog(
this,
R.getI18NString(msg, new String[]{e.getMessage()}),
R.getI18NString("service.gui.ERROR"),
JOptionPane.ERROR_MESSAGE
);
}
/**
* Show this dialog.
*
* @return true if OK has been pressed, false otherwise
*/
public boolean showDialog()
{
setModal(true);
setVisible(true);
setVisible(false);
return success;
}
public void itemStateChanged(ItemEvent e)
{
if(e.getStateChange() != ItemEvent.SELECTED)
return;
if(e.getSource() == cboKeyStoreTypes)
{
KeyStoreType kt = (KeyStoreType)cboKeyStoreTypes.getSelectedItem();
if(kt == null)
return;
try
{
if(!"PKCS11".equals(kt.getName()))
chkSavePassword.setEnabled(true);
txtKeyStorePassword.setEditable(kt.hasKeyStorePassword()
&& chkSavePassword.isSelected());
keyStore = loadKeyStore();
loadAliases();
}
catch (KeyStoreException ex)
{
cboAlias.removeAllItems();
showGenericError("plugin.certconfig.INVALID_KEYSTORE_TYPE", ex);
}
}
if(e.getSource() == cboAlias)
{
cmdShowCert.setEnabled(cboAlias.getSelectedItem() != null);
}
}
public void stateChanged(ChangeEvent e)
{
if(e.getSource() == chkSavePassword)
{
txtKeyStorePassword.setEditable(
chkSavePassword.isSelected()
&& ((KeyStoreType) cboKeyStoreTypes.getSelectedItem())
.hasKeyStorePassword()
);
}
}
}