Java tutorial
/* * FPortecle.java * This file is part of Portecle, a multipurpose keystore and certificate tool. * * Copyright 2004 Wayne Grant, waynedgrant@hotmail.com * 2004-2014 Ville Skytt, ville.skytta@iki.fi * 2010 Lam Chau, lamchau@gmail.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.sf.portecle; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.Preferences; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; import javax.swing.Action; import javax.swing.DefaultCellEditor; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTable; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.WindowConstants; import javax.swing.border.BevelBorder; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableCellEditor; import javax.swing.table.TableColumn; import net.sf.portecle.crypto.CryptoException; import net.sf.portecle.crypto.KeyPairType; import net.sf.portecle.crypto.KeyStoreType; import net.sf.portecle.crypto.KeyStoreUtil; import net.sf.portecle.crypto.ProviderUtil; import net.sf.portecle.crypto.X509CertUtil; import net.sf.portecle.gui.DesktopUtil; import net.sf.portecle.gui.JMenuItemRecentFile; import net.sf.portecle.gui.JMenuRecentFiles; import net.sf.portecle.gui.LastDir; import net.sf.portecle.gui.SingleFileDropHelper; import net.sf.portecle.gui.SwingHelper; import net.sf.portecle.gui.about.DAbout; import net.sf.portecle.gui.crypto.DProviderInfo; import net.sf.portecle.gui.error.DThrowable; import net.sf.portecle.gui.help.FHelp; import net.sf.portecle.gui.jar.DJarInfo; import net.sf.portecle.gui.password.DChangePassword; import net.sf.portecle.gui.password.DGetNewPassword; import net.sf.portecle.gui.password.DGetPassword; import net.sf.portecle.gui.statusbar.StatusBar; import net.sf.portecle.gui.statusbar.StatusBarChangeHandler; import org.bouncycastle.openssl.PEMEncryptor; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.PasswordFinder; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; /** * Start class and main frame of Portecle. */ public class FPortecle extends JFrame implements StatusBar { /** Resource bundle base name */ private static final String RB_BASENAME = FPortecle.class.getPackage().getName() + "/resources"; /** Resource bundle */ public static final ResourceBundle RB = ResourceBundle.getBundle(RB_BASENAME); /** Logger */ public static final Logger LOG = Logger.getLogger(FPortecle.class.getName(), RB_BASENAME); /** Application preferences */ private static final Preferences PREFS = Preferences.userNodeForPackage(FPortecle.class); /** Minimum required BC version */ private static final Double REQ_BC_VERSION = new Double(1.51); /** Enable experimental features? */ private static final boolean EXPERIMENTAL = Boolean.getBoolean("portecle.experimental"); /** Default keystore table width - dictates width of this frame */ private static final int DEFAULT_TABLE_WIDTH = 600; /** Default keystore table width - dictates height of this frame */ private static final int DEFAULT_TABLE_HEIGHT = 400; /** Number of recent files to hold in the file menu */ private static final int RECENT_FILES_LENGTH = 4; /** Menu index in the file menu for recent files to be inserted at */ // EXPERIMENTAL enables/disables the PKCS #11 menu item private static final int RECENT_FILES_INDEX = EXPERIMENTAL ? 7 : 6; /** Default look & feel class name */ private static final String DEFAULT_LOOK_FEEL = UIManager.getCrossPlatformLookAndFeelClassName(); /** Default CA certificates keystore file */ /* package private */static final File DEFAULT_CA_CERTS_FILE = new File(System.getProperty("java.home"), "lib" + File.separator + "security" + File.separator + FileChooserFactory.CACERTS_FILENAME); /** The last directory accessed by the application */ private final LastDir m_lastDir = new LastDir(); /** Use CA certificates keystore file? */ private boolean m_bUseCaCerts; /** CA certificates keystore file */ private File m_fCaCertsFile; /** CA certificates keystore */ private KeyStore m_caCertsKeyStore; /** KeystoreWrapper object containing the current keystore */ private KeyStoreWrapper m_keyStoreWrap; /** The PRNG, cached for performance reasons */ private SecureRandom m_rnd; /** Frame for Help System */ private FHelp m_fHelp; /** Look & Feel setting made in options (picked up by saveAppPrefs) */ private String lookFeelClassName; /** Look & Feel setting made in options (picked up by saveAppPrefs) */ private Boolean m_bLookFeelDecorationOptions; /** Currently selected alias */ private String selectedAlias; // ////////////////////////////////////////////////////////// // Menu bar controls // ////////////////////////////////////////////////////////// /** File menu */ private JMenuRecentFiles m_jmrfFile; /** Save keystore As menu item of File menu */ private JMenuItem m_jmiSaveKeyStoreAs; /** Change keystore Type menu Tools menu */ private JMenu m_jmChangeKeyStoreType; /** JKS menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeJks; /** Case sensitive JKS menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeCaseExactJks; /** JCEKS menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeJceks; /** PKCS #12 menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypePkcs12; /** BKS menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeBks; /** BKS-V1 menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeBksV1; /** UBER menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeUber; /** GKR menu item in Change Keystore Type menu */ private JMenuItem m_jmiChangeKeyStoreTypeGkr; // ////////////////////////////////////////////////////////// // Pop-up menu controls // ////////////////////////////////////////////////////////// /** Key entry pop-up menu */ private JPopupMenu m_jpmKey; /** Key pair entry pop-up menu */ private JPopupMenu m_jpmKeyPair; /** Set Password menu item of key pair entry pop-up menu */ private JMenuItem m_jmiSetKeyPairPass; /** Trusted Certificate entry pop-up menu */ private JPopupMenu m_jpmCert; // ////////////////////////////////////////////////////////// // Keystore table controls // ////////////////////////////////////////////////////////// /** Panel to hold keystore entries table */ private JPanel m_jpKeyStoreTable; /** Keystore entries table */ private KeyStoreTable m_jtKeyStore; // ////////////////////////////////////////////////////////// // Status bar controls // ////////////////////////////////////////////////////////// /** Label to display current application status messages */ private JLabel m_jlStatusBar; // ////////////////////////////////////////////////////////// // Actions - these are shared between the menu and toolbar // ////////////////////////////////////////////////////////// /** New Keystore action */ private final NewKeyStoreAction m_newKeyStoreAction = new NewKeyStoreAction(); /** Open Keystore File action */ private final OpenKeyStoreFileAction m_openKeyStoreFileAction = new OpenKeyStoreFileAction(); /** Open CA certs keystore File action */ private final OpenCaCertsKeyStoreAction m_openCaCertsKeyStoreFileAction = new OpenCaCertsKeyStoreAction(); /** Save Keystore action */ private final SaveKeyStoreAction m_saveKeyStoreAction = new SaveKeyStoreAction(); /** Examine Certificate action */ private final ExamineCertAction m_examineCertAction = new ExamineCertAction(); /** Examine SSL/TLS Connection action */ private final ExamineCertSSLAction m_examineCertSSLAction = new ExamineCertSSLAction(); /** Examine CSR action */ private final ExamineCsrAction m_examineCsrAction = new ExamineCsrAction(); /** Examine CRL action */ private final ExamineCrlAction m_examineCrlAction = new ExamineCrlAction(); /** Generate Key Pair action */ private final GenKeyPairAction m_genKeyPairAction = new GenKeyPairAction(); /** Import Trusted Certificate action */ private final ImportTrustCertAction m_importTrustCertAction = new ImportTrustCertAction(); /** Import Key Pair action */ private final ImportKeyPairAction m_importKeyPairAction = new ImportKeyPairAction(); /** Set Keystore Password action */ private final SetKeyStorePassAction m_setKeyStorePassAction = new SetKeyStorePassAction(); /** Keystore Report action */ private final KeyStoreReportAction m_keyStoreReportAction = new KeyStoreReportAction(); /** Help action */ private final HelpAction m_helpAction = new HelpAction(); /** * Creates a new FPortecle frame. */ public FPortecle() { // Get and store non-GUI related application properties m_bUseCaCerts = PREFS.getBoolean(RB.getString("AppPrefs.UseCaCerts"), false); m_fCaCertsFile = new File( PREFS.get(RB.getString("AppPrefs.CaCertsFile"), DEFAULT_CA_CERTS_FILE.getAbsolutePath())); // Initialize GUI components initComponents(); } /** * Initialize FPortecle frame's GUI components. */ private void initComponents() { initStatusBar(); initMenu(); initToolBar(); initPopupMenus(); initTable(); // Handle application close addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent evt) { exitApplication(); } }); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); updateTitle(); pack(); // Set application position according to application preferences unless the relevant preferences are // not present or are invalid int iXPos = PREFS.getInt(RB.getString("AppPrefs.XPos"), 0); int iYPos = PREFS.getInt(RB.getString("AppPrefs.YPos"), 0); if (iXPos <= 0 || iYPos <= 0) { // Center the frame in the center of the desktop setLocationRelativeTo(null); } else { // Use application property values for positioning setLocation(new Point(iXPos, iYPos)); } // If frame is not completely visible then set it to default size and center it if (!SwingUtilities.isRectangleContainingRectangle( new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()), getBounds())) { m_jpKeyStoreTable.setPreferredSize(new Dimension(DEFAULT_TABLE_WIDTH, DEFAULT_TABLE_HEIGHT)); setLocationRelativeTo(null); } // Set its icon setIconImage(getResImage("FPortecle.Icon.image")); } /** * Initialize FPortecle frame's main menu GUI components. */ private void initMenu() { // The menu items that carry out the same function as tool bar buttons use actions // The menu bar JMenuBar jmbMenuBar = new JMenuBar(); // File menu m_jmrfFile = new JMenuRecentFiles(RB.getString("FPortecle.m_jmrfFile.text"), RECENT_FILES_LENGTH, RECENT_FILES_INDEX); m_jmrfFile.setMnemonic(RB.getString("FPortecle.m_jmrfFile.mnemonic").charAt(0)); JMenuItem jmiNewKeyStore = new JMenuItem(m_newKeyStoreAction); jmiNewKeyStore.setToolTipText(null); jmiNewKeyStore.addChangeListener( new StatusBarChangeHandler((String) m_newKeyStoreAction.getValue(Action.LONG_DESCRIPTION), this)); m_jmrfFile.add(jmiNewKeyStore); JMenuItem jmiOpenKeyStoreFile = new JMenuItem(m_openKeyStoreFileAction); jmiOpenKeyStoreFile.setToolTipText(null); jmiOpenKeyStoreFile.addChangeListener(new StatusBarChangeHandler( (String) m_openKeyStoreFileAction.getValue(Action.LONG_DESCRIPTION), this)); m_jmrfFile.add(jmiOpenKeyStoreFile); if (EXPERIMENTAL) { JMenuItem jmiOpenKeyStorePkcs11 = new JMenuItem(RB.getString("FPortecle.jmiOpenKeyStorePkcs11.text"), RB.getString("FPortecle.jmiOpenKeyStorePkcs11.mnemonic").charAt(0)); jmiOpenKeyStorePkcs11.setIcon(new ImageIcon(getResImage("FPortecle.jmiOpenKeyStorePkcs11.image"))); jmiOpenKeyStorePkcs11.setToolTipText(null); if (ProviderUtil.getPkcs11Providers().isEmpty()) { jmiOpenKeyStorePkcs11.setEnabled(false); } m_jmrfFile.add(jmiOpenKeyStorePkcs11); jmiOpenKeyStorePkcs11.addActionListener(new ActionListener() { @Override protected void act() { openKeyStorePkcs11(); } }); jmiOpenKeyStorePkcs11.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiOpenKeyStorePkcs11.statusbar"), this)); } JMenuItem jmiOpenCaCertsKeyStoreFile = new JMenuItem(m_openCaCertsKeyStoreFileAction); jmiOpenCaCertsKeyStoreFile.setToolTipText(null); jmiOpenCaCertsKeyStoreFile.addChangeListener(new StatusBarChangeHandler( (String) m_openCaCertsKeyStoreFileAction.getValue(Action.LONG_DESCRIPTION), this)); m_jmrfFile.add(jmiOpenCaCertsKeyStoreFile); m_jmrfFile.addSeparator(); JMenuItem jmiSaveKeyStore = new JMenuItem(m_saveKeyStoreAction); jmiSaveKeyStore.setToolTipText(null); jmiSaveKeyStore.addChangeListener( new StatusBarChangeHandler((String) m_saveKeyStoreAction.getValue(Action.LONG_DESCRIPTION), this)); m_jmrfFile.add(jmiSaveKeyStore); m_jmiSaveKeyStoreAs = new JMenuItem(RB.getString("FPortecle.m_jmiSaveKeyStoreAs.text"), RB.getString("FPortecle.m_jmiSaveKeyStoreAs.mnemonic").charAt(0)); m_jmiSaveKeyStoreAs.setIcon(new ImageIcon(getResImage("FPortecle.m_jmiSaveKeyStoreAs.image"))); m_jmiSaveKeyStoreAs.setEnabled(false); m_jmrfFile.add(m_jmiSaveKeyStoreAs); m_jmiSaveKeyStoreAs.addActionListener(new ActionListener() { @Override protected void act() { saveKeyStoreAs(); } }); m_jmiSaveKeyStoreAs.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiSaveKeyStoreAs.statusbar"), this)); m_jmrfFile.addSeparator(); // Add recent files to file menu for (int iCnt = RECENT_FILES_LENGTH; iCnt > 0; iCnt--) { String sRecentFile = PREFS.get(RB.getString("AppPrefs.RecentFile") + iCnt, null); if (sRecentFile != null) { m_jmrfFile.add(createRecentFileMenuItem(new File(sRecentFile))); } } JMenuItem jmiExit = new JMenuItem(RB.getString("FPortecle.jmiExit.text"), RB.getString("FPortecle.jmiExit.mnemonic").charAt(0)); jmiExit.setIcon(new ImageIcon(getResImage("FPortecle.jmiExit.image"))); m_jmrfFile.add(jmiExit); jmiExit.addActionListener(new ActionListener() { @Override protected void act() { exitApplication(); } }); jmiExit.addChangeListener(new StatusBarChangeHandler(RB.getString("FPortecle.jmiExit.statusbar"), this)); // Tools menu JMenu jmTools = new JMenu(RB.getString("FPortecle.jmTools.text")); jmTools.setMnemonic(RB.getString("FPortecle.jmTools.mnemonic").charAt(0)); JMenuItem jmiGenKeyPair = new JMenuItem(m_genKeyPairAction); jmiGenKeyPair.setToolTipText(null); jmiGenKeyPair.addChangeListener( new StatusBarChangeHandler((String) m_genKeyPairAction.getValue(Action.LONG_DESCRIPTION), this)); jmTools.add(jmiGenKeyPair); JMenuItem jmiImportTrustCert = new JMenuItem(m_importTrustCertAction); jmiImportTrustCert.setToolTipText(null); jmiImportTrustCert.addChangeListener(new StatusBarChangeHandler( (String) m_importTrustCertAction.getValue(Action.LONG_DESCRIPTION), this)); jmTools.add(jmiImportTrustCert); JMenuItem jmiImportKeyPair = new JMenuItem(m_importKeyPairAction); jmiImportKeyPair.setToolTipText(null); jmiImportKeyPair.addChangeListener( new StatusBarChangeHandler((String) m_importKeyPairAction.getValue(Action.LONG_DESCRIPTION), this)); jmTools.add(jmiImportKeyPair); jmTools.addSeparator(); JMenuItem jmiSetKeyStorePass = new JMenuItem(m_setKeyStorePassAction); jmiSetKeyStorePass.setToolTipText(null); jmiSetKeyStorePass.addChangeListener(new StatusBarChangeHandler( (String) m_setKeyStorePassAction.getValue(Action.LONG_DESCRIPTION), this)); jmTools.add(jmiSetKeyStorePass); m_jmChangeKeyStoreType = new JMenu(RB.getString("FPortecle.m_jmChangeKeyStoreType.text")); m_jmChangeKeyStoreType.setIcon(new ImageIcon(getResImage("FPortecle.m_jmChangeKeyStoreType.image"))); m_jmChangeKeyStoreType.setMnemonic(RB.getString("FPortecle.m_jmChangeKeyStoreType.mnemonic").charAt(0)); m_jmChangeKeyStoreType.setEnabled(false); jmTools.add(m_jmChangeKeyStoreType); // Add Change Keystore Type sub-menu of Tools m_jmiChangeKeyStoreTypeJks = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeJks.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeJks.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeJks.setEnabled(false); m_jmiChangeKeyStoreTypeJks.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.JKS); } }); m_jmiChangeKeyStoreTypeJks.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeJks.statusbar"), this)); m_jmiChangeKeyStoreTypeCaseExactJks = new JMenuItem( RB.getString("FPortecle.m_jmiChangeKeyStoreTypeCaseExactJks.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeCaseExactJks.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeCaseExactJks.setEnabled(false); m_jmiChangeKeyStoreTypeCaseExactJks.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.CaseExactJKS); } }); m_jmiChangeKeyStoreTypeCaseExactJks.addChangeListener(new StatusBarChangeHandler( RB.getString("FPortecle.m_jmiChangeKeyStoreTypeCaseExactJks.statusbar"), this)); m_jmiChangeKeyStoreTypeJceks = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeJceks.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeJceks.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeJceks.setEnabled(false); m_jmiChangeKeyStoreTypeJceks.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.JCEKS); } }); m_jmiChangeKeyStoreTypeJceks.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeJceks.statusbar"), this)); m_jmiChangeKeyStoreTypePkcs12 = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypePkcs12.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypePkcs12.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypePkcs12.setEnabled(false); m_jmiChangeKeyStoreTypePkcs12.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.PKCS12); } }); m_jmiChangeKeyStoreTypePkcs12.addChangeListener(new StatusBarChangeHandler( RB.getString("FPortecle.m_jmiChangeKeyStoreTypePkcs12.statusbar"), this)); m_jmiChangeKeyStoreTypeBks = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeBks.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeBks.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeBks.setEnabled(false); m_jmiChangeKeyStoreTypeBks.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.BKS); } }); m_jmiChangeKeyStoreTypeBks.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeBks.statusbar"), this)); m_jmiChangeKeyStoreTypeBksV1 = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeBksV1.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeBksV1.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeBksV1.setEnabled(false); m_jmiChangeKeyStoreTypeBksV1.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.BKS_V1); } }); m_jmiChangeKeyStoreTypeBksV1.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeBksV1.statusbar"), this)); m_jmiChangeKeyStoreTypeUber = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeUber.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeUber.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeUber.setEnabled(false); m_jmiChangeKeyStoreTypeUber.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.UBER); } }); m_jmiChangeKeyStoreTypeUber.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeUber.statusbar"), this)); m_jmiChangeKeyStoreTypeGkr = new JMenuItem(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeGkr.text"), RB.getString("FPortecle.m_jmiChangeKeyStoreTypeGkr.mnemonic").charAt(0)); m_jmiChangeKeyStoreTypeGkr.setEnabled(false); m_jmiChangeKeyStoreTypeGkr.addActionListener(new ActionListener() { @Override protected void act() { changeKeyStoreType(KeyStoreType.GKR); } }); m_jmiChangeKeyStoreTypeGkr.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiChangeKeyStoreTypeGkr.statusbar"), this)); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeJks); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypePkcs12); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeJceks); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeCaseExactJks); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeBks); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeBksV1); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeUber); m_jmChangeKeyStoreType.add(m_jmiChangeKeyStoreTypeGkr); // Others JMenuItem jmiKeyStoreReport = new JMenuItem(m_keyStoreReportAction); jmiKeyStoreReport.setToolTipText(null); jmiKeyStoreReport.addChangeListener(new StatusBarChangeHandler( (String) m_keyStoreReportAction.getValue(Action.LONG_DESCRIPTION), this)); jmTools.add(jmiKeyStoreReport); jmTools.addSeparator(); JMenuItem jmiOptions = new JMenuItem(RB.getString("FPortecle.jmiOptions.text"), RB.getString("FPortecle.jmiOptions.mnemonic").charAt(0)); jmiOptions.setIcon(new ImageIcon(getResImage("FPortecle.jmiOptions.image"))); jmTools.add(jmiOptions); jmiOptions.addActionListener(new ActionListener() { @Override protected void act() { showOptions(); } }); jmiOptions.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiOptions.statusbar"), this)); // Examine menu JMenu jmExamine = new JMenu(RB.getString("FPortecle.jmExamine.text")); jmExamine.setMnemonic(RB.getString("FPortecle.jmExamine.mnemonic").charAt(0)); JMenuItem jmiExamineCert = new JMenuItem(m_examineCertAction); jmiExamineCert.setToolTipText(null); jmiExamineCert.addChangeListener( new StatusBarChangeHandler((String) m_examineCertAction.getValue(Action.LONG_DESCRIPTION), this)); jmExamine.add(jmiExamineCert); JMenuItem jmiExamineCertSSL = new JMenuItem(m_examineCertSSLAction); jmiExamineCertSSL.setToolTipText(null); jmiExamineCertSSL.addChangeListener(new StatusBarChangeHandler( (String) m_examineCertSSLAction.getValue(Action.LONG_DESCRIPTION), this)); jmExamine.add(jmiExamineCertSSL); JMenuItem jmiExamineCsr = new JMenuItem(m_examineCsrAction); jmiExamineCsr.setToolTipText(null); jmiExamineCsr.addChangeListener( new StatusBarChangeHandler((String) m_examineCsrAction.getValue(Action.LONG_DESCRIPTION), this)); jmExamine.add(jmiExamineCsr); JMenuItem jmiExamineCrl = new JMenuItem(m_examineCrlAction); jmiExamineCrl.setToolTipText(null); jmiExamineCrl.addChangeListener( new StatusBarChangeHandler((String) m_examineCrlAction.getValue(Action.LONG_DESCRIPTION), this)); jmExamine.add(jmiExamineCrl); // Help menu JMenu jmHelp = new JMenu(RB.getString("FPortecle.jmHelp.text")); jmHelp.setMnemonic(RB.getString("FPortecle.jmHelp.mnemonic").charAt(0)); JMenuItem jmiHelp = new JMenuItem(m_helpAction); jmiHelp.setToolTipText(null); jmiHelp.addChangeListener( new StatusBarChangeHandler((String) m_helpAction.getValue(Action.LONG_DESCRIPTION), this)); jmHelp.add(jmiHelp); // Online Resources menu (sub-menu of Help) JMenu jmOnlineResources = new JMenu(RB.getString("FPortecle.jmOnlineResources.text")); jmOnlineResources.setIcon(new ImageIcon(getResImage("FPortecle.jmOnlineResources.image"))); jmOnlineResources.setMnemonic(RB.getString("FPortecle.jmOnlineResources.mnemonic").charAt(0)); jmHelp.add(jmOnlineResources); JMenuItem jmiWebsite = new JMenuItem(RB.getString("FPortecle.jmiWebsite.text"), RB.getString("FPortecle.jmiWebsite.mnemonic").charAt(0)); jmiWebsite.setIcon(new ImageIcon(getResImage("FPortecle.jmiWebsite.image"))); jmOnlineResources.add(jmiWebsite); jmiWebsite.addActionListener(new ActionListener() { @Override protected void act() { visitWebsite(); } }); jmiWebsite.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiWebsite.statusbar"), this)); JMenuItem jmiSFNetProject = new JMenuItem(RB.getString("FPortecle.jmiSFNetProject.text"), RB.getString("FPortecle.jmiSFNetProject.mnemonic").charAt(0)); jmiSFNetProject.setIcon(new ImageIcon(getResImage("FPortecle.jmiSFNetProject.image"))); jmOnlineResources.add(jmiSFNetProject); jmiSFNetProject.addActionListener(new ActionListener() { @Override protected void act() { visitSFNetProject(); } }); jmiSFNetProject.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiSFNetProject.statusbar"), this)); JMenuItem jmiMailList = new JMenuItem(RB.getString("FPortecle.jmiMailList.text"), RB.getString("FPortecle.jmiMailList.mnemonic").charAt(0)); jmiMailList.setIcon(new ImageIcon(getResImage("FPortecle.jmiMailList.image"))); jmOnlineResources.add(jmiMailList); jmiMailList.addActionListener(new ActionListener() { @Override protected void act() { visitMailListSignup(); } }); jmiMailList.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiMailList.statusbar"), this)); /* * Update check disabled for now... JMenuItem jmiCheckUpdate = new JMenuItem( * m_res.getString("FPortecle.jmiCheckUpdate.text"), * m_res.getString("FPortecle.jmiCheckUpdate.mnemonic").charAt(0)); jmiCheckUpdate.setIcon( new * ImageIcon(getResImage("FPortecle.jmiCheckUpdate.image"))); jmOnlineResources.add(jmiCheckUpdate); * jmiCheckUpdate.addActionListener(new ActionListener() { protected void act() { checkForUpdate(); * }}); new StatusBarChangeHandler( jmiCheckUpdate, * m_res.getString("FPortecle.jmiCheckUpdate.statusbar"), this); */ JMenuItem jmiDonate = new JMenuItem(RB.getString("FPortecle.jmiDonate.text"), RB.getString("FPortecle.jmiDonate.mnemonic").charAt(0)); jmiDonate.setIcon(new ImageIcon(getResImage("FPortecle.jmiDonate.image"))); jmiDonate.setToolTipText(null); jmOnlineResources.add(jmiDonate); jmiDonate.addActionListener(new ActionListener() { @Override protected void act() { makeDonation(); } }); jmiDonate .addChangeListener(new StatusBarChangeHandler(RB.getString("FPortecle.jmiDonate.statusbar"), this)); jmHelp.addSeparator(); JMenuItem jmiSecurityProviders = new JMenuItem(RB.getString("FPortecle.jmiSecurityProviders.text"), RB.getString("FPortecle.jmiSecurityProviders.mnemonic").charAt(0)); jmiSecurityProviders.setIcon(new ImageIcon(getResImage("FPortecle.jmiSecurityProviders.image"))); jmHelp.add(jmiSecurityProviders); jmiSecurityProviders.addActionListener(new ActionListener() { @Override protected void act() { showSecurityProviders(); } }); jmiSecurityProviders.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiSecurityProviders.statusbar"), this)); JMenuItem jmiJars = new JMenuItem(RB.getString("FPortecle.jmiJars.text"), RB.getString("FPortecle.jmiJars.mnemonic").charAt(0)); jmiJars.setIcon(new ImageIcon(getResImage("FPortecle.jmiJars.image"))); jmHelp.add(jmiJars); jmiJars.addActionListener(new ActionListener() { @Override protected void act() { showJarInfo(); } }); jmiJars.addChangeListener(new StatusBarChangeHandler(RB.getString("FPortecle.jmiJars.statusbar"), this)); jmHelp.addSeparator(); JMenuItem jmiAbout = new JMenuItem(RB.getString("FPortecle.jmiAbout.text"), RB.getString("FPortecle.jmiAbout.mnemonic").charAt(0)); jmiAbout.setIcon(new ImageIcon(getResImage("FPortecle.jmiAbout.image"))); jmHelp.add(jmiAbout); jmiAbout.addActionListener(new ActionListener() { @Override protected void act() { showAbout(); } }); jmiAbout.addChangeListener(new StatusBarChangeHandler(RB.getString("FPortecle.jmiAbout.statusbar"), this)); // Add the menus to the menu bar jmbMenuBar.add(m_jmrfFile); jmbMenuBar.add(jmTools); jmbMenuBar.add(jmExamine); jmbMenuBar.add(jmHelp); // Add menu bar to application frame setJMenuBar(jmbMenuBar); } /** * Create a recent file menu item for the supplied file. * * @param fRecentFile Recent file * @return Recent file menu item */ private JMenuItemRecentFile createRecentFileMenuItem(File fRecentFile) { JMenuItemRecentFile jmirfNew = new JMenuItemRecentFile(fRecentFile); jmirfNew.setIcon(new ImageIcon(getResImage("FPortecle.OpenRecent.image"))); jmirfNew.addActionListener(new RecentKeyStoreFileActionListener(fRecentFile, this)); jmirfNew.addChangeListener(new StatusBarChangeHandler( MessageFormat.format(RB.getString("FPortecle.recentfile.statusbar"), fRecentFile), this)); return jmirfNew; } /** * Initialize FPortecle frame's tool bar GUI components. */ private void initToolBar() { // Create the "new" tool bar button JButton jbNewKeyStore = new JButton(); jbNewKeyStore.setAction(m_newKeyStoreAction); jbNewKeyStore.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbNewKeyStore.setMnemonic(0); jbNewKeyStore.setFocusable(false); jbNewKeyStore.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_newKeyStoreAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "open" tool bar button JButton jbOpenKeyStoreFile = new JButton(); jbOpenKeyStoreFile.setAction(m_openKeyStoreFileAction); jbOpenKeyStoreFile.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbOpenKeyStoreFile.setMnemonic(0); jbOpenKeyStoreFile.setFocusable(false); jbOpenKeyStoreFile.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_openKeyStoreFileAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "save" tool bar button JButton jbSaveKeyStore = new JButton(); jbSaveKeyStore.setAction(m_saveKeyStoreAction); jbSaveKeyStore.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbSaveKeyStore.setMnemonic(0); jbSaveKeyStore.setFocusable(false); jbSaveKeyStore.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_saveKeyStoreAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "generate key pair" tool bar button JButton jbGenKeyPair = new JButton(); jbGenKeyPair.setAction(m_genKeyPairAction); jbGenKeyPair.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbGenKeyPair.setMnemonic(0); jbGenKeyPair.setFocusable(false); jbGenKeyPair.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_genKeyPairAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "import trusted certificate" tool bar button JButton jbImportTrustCert = new JButton(); jbImportTrustCert.setAction(m_importTrustCertAction); jbImportTrustCert.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbImportTrustCert.setMnemonic(0); jbImportTrustCert.setFocusable(false); jbImportTrustCert.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_importTrustCertAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "import key pair" tool bar button JButton jbImportKeyPair = new JButton(); jbImportKeyPair.setAction(m_importKeyPairAction); jbImportKeyPair.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbImportKeyPair.setMnemonic(0); jbImportKeyPair.setFocusable(false); jbImportKeyPair.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_importKeyPairAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "set keystore password" tool bar button JButton jbSetKeyStorePass = new JButton(); jbSetKeyStorePass.setAction(m_setKeyStorePassAction); jbSetKeyStorePass.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbSetKeyStorePass.setMnemonic(0); jbSetKeyStorePass.setFocusable(false); jbSetKeyStorePass.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_setKeyStorePassAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "keystore report" tool bar button JButton jbKeyStoreReport = new JButton(); jbKeyStoreReport.setAction(m_keyStoreReportAction); jbKeyStoreReport.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbKeyStoreReport.setMnemonic(0); jbKeyStoreReport.setFocusable(false); jbKeyStoreReport.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_keyStoreReportAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "examine certificate" tool bar button JButton jbExamineCert = new JButton(); jbExamineCert.setAction(m_examineCertAction); jbExamineCert.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbExamineCert.setMnemonic(0); jbExamineCert.setFocusable(false); jbExamineCert.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_examineCertAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "examine CRL" tool bar button JButton jbExamineCrl = new JButton(); jbExamineCrl.setAction(m_examineCrlAction); jbExamineCrl.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbExamineCrl.setMnemonic(0); jbExamineCrl.setFocusable(false); jbExamineCrl.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_examineCrlAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // Create the "help" tool bar button JButton jbHelp = new JButton(); jbHelp.setAction(m_helpAction); jbHelp.setText(null); // Don't share text from action // Get around bug with action mnemonics on tool bar buttons jbHelp.setMnemonic(0); jbHelp.setFocusable(false); jbHelp.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent evt) { setStatusBarText((String) m_helpAction.getValue(Action.LONG_DESCRIPTION)); } @Override public void mouseExited(MouseEvent evt) { setDefaultStatusBarText(); } }); // The tool bar JToolBar jtbToolBar = new JToolBar(); jtbToolBar.setFloatable(false); jtbToolBar.setRollover(true); jtbToolBar.setName(RB.getString("FPortecle.jtbToolBar.name")); // Add the buttons to the tool bar - use visible separators for all L&Fs jtbToolBar.add(jbNewKeyStore); jtbToolBar.add(jbOpenKeyStoreFile); jtbToolBar.add(jbSaveKeyStore); JSeparator separator1 = new JSeparator(SwingConstants.VERTICAL); separator1.setMaximumSize(new Dimension(3, 16)); jtbToolBar.add(separator1); jtbToolBar.add(jbGenKeyPair); jtbToolBar.add(jbImportTrustCert); jtbToolBar.add(jbImportKeyPair); jtbToolBar.add(jbSetKeyStorePass); jtbToolBar.add(jbKeyStoreReport); JSeparator separator2 = new JSeparator(SwingConstants.VERTICAL); separator2.setMaximumSize(new Dimension(3, 16)); jtbToolBar.add(separator2); jtbToolBar.add(jbExamineCert); jtbToolBar.add(jbExamineCrl); JSeparator separator3 = new JSeparator(SwingConstants.VERTICAL); separator3.setMaximumSize(new Dimension(3, 16)); jtbToolBar.add(separator3); jtbToolBar.add(jbHelp); // Add the tool bar to the frame getContentPane().add(jtbToolBar, BorderLayout.NORTH); } /** * Initialize FPortecle frame's keystore content table GUI components. */ private void initTable() { // The table data model KeyStoreTableModel ksModel = new KeyStoreTableModel(this); // The table itself m_jtKeyStore = new KeyStoreTable(ksModel); m_jtKeyStore.setShowGrid(false); m_jtKeyStore.setRowMargin(0); m_jtKeyStore.getColumnModel().setColumnMargin(0); m_jtKeyStore.getTableHeader().setReorderingAllowed(false); m_jtKeyStore.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); // Top accommodate entry icons with spare space (16 pixels tall) m_jtKeyStore.setRowHeight(18); // Add custom renderers for the table headers and cells for (int iCnt = 0; iCnt < m_jtKeyStore.getColumnCount(); iCnt++) { TableColumn column = m_jtKeyStore.getColumnModel().getColumn(iCnt); column.setHeaderRenderer(new KeyStoreTableHeadRend()); column.setCellRenderer(new KeyStoreTableCellRend()); } // Make the first column small and not resizable (it holds icons to represent the different entry // types) TableColumn typeCol = m_jtKeyStore.getColumnModel().getColumn(0); typeCol.setResizable(false); typeCol.setMinWidth(20); typeCol.setMaxWidth(20); typeCol.setPreferredWidth(20); // Set alias columns width according to the relevant application property unless the property is not // present or is invalid. int iAliasWidth = PREFS.getInt(RB.getString("AppPrefs.AliasWidth"), 0); TableColumn aliasCol = m_jtKeyStore.getColumnModel().getColumn(1); aliasCol.setMinWidth(20); aliasCol.setMaxWidth(10000); if (iAliasWidth <= 0) { aliasCol.setPreferredWidth(350); } else { aliasCol.setPreferredWidth(iAliasWidth); } // Make the table sortable m_jtKeyStore.setAutoCreateRowSorter(true); // ...and sort it by alias by default m_jtKeyStore.getRowSorter().toggleSortOrder(1); // Get usual double click edit start out of the way - we want double click to show the // entry, even in editable columns. In-place edit can be invoked with F2. TableCellEditor cellEditor = m_jtKeyStore.getDefaultEditor(String.class); if (cellEditor instanceof DefaultCellEditor) { ((DefaultCellEditor) cellEditor).setClickCountToStart(1000); } // Put the table into a scroll pane JScrollPane jspKeyStoreTable = new JScrollPane(m_jtKeyStore, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); jspKeyStoreTable.getViewport().setBackground(m_jtKeyStore.getBackground()); // Get the size of the keystore table panel from the application preferences int iWidth = PREFS.getInt(RB.getString("AppPrefs.TableWidth"), 0); int iHeight = PREFS.getInt(RB.getString("AppPrefs.TableHeight"), 0); // Put the scroll pane into a panel. The preferred size of the panel dictates the size of the entire // frame m_jpKeyStoreTable = new JPanel(new BorderLayout(10, 10)); if (iWidth <= 0 || iHeight <= 0) { m_jpKeyStoreTable.setPreferredSize(new Dimension(DEFAULT_TABLE_WIDTH, DEFAULT_TABLE_HEIGHT)); } else { m_jpKeyStoreTable.setPreferredSize(new Dimension(iWidth, iHeight)); } m_jpKeyStoreTable.add(jspKeyStoreTable, BorderLayout.CENTER); m_jpKeyStoreTable.setBorder(new EmptyBorder(3, 3, 3, 3)); // Add mouse listeners to show pop-up menus when table entries are clicked upon; maybeShowPopup for // both mousePressed and mouseReleased for cross-platform compatibility. Also add listeners to show an // entry's certificate details if it is double-clicked m_jtKeyStore.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { keyStoreTableDoubleClick(evt); } @Override public void mousePressed(MouseEvent evt) { maybeShowPopup(evt); } @Override public void mouseReleased(MouseEvent evt) { maybeShowPopup(evt); } }); m_jpKeyStoreTable.setTransferHandler(m_jtKeyStore.getTransferHandler()); getContentPane().add(m_jpKeyStoreTable, BorderLayout.CENTER); } /** * Initialize FPortecle frame's status bar GUI components. */ private void initStatusBar() { m_jlStatusBar = new JLabel(); m_jlStatusBar.setBorder(new CompoundBorder(new EmptyBorder(3, 3, 3, 3), new CompoundBorder(new BevelBorder(BevelBorder.LOWERED), new EmptyBorder(0, 2, 0, 2)))); setDefaultStatusBarText(); getContentPane().add(m_jlStatusBar, BorderLayout.SOUTH); } /** * Initialize FPortecle frame's pop-up menu GUI components. These are invoked when rows of specific types * are clicked upon in the keystore table. */ private void initPopupMenus() { // Initialize key-only entry pop-up menu including mnemonics and listeners m_jpmKey = new JPopupMenu(); JMenuItem jmiKeyDelete = new JMenuItem(RB.getString("FPortecle.jmiKeyDelete.text"), RB.getString("FPortecle.jmiKeyDelete.mnemonic").charAt(0)); jmiKeyDelete.setIcon(new ImageIcon(getResImage("FPortecle.jmiKeyDelete.image"))); jmiKeyDelete.addActionListener(new ActionListener() { @Override protected void act() { deleteSelectedEntry(); } }); jmiKeyDelete.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiKeyDelete.statusbar"), this)); m_jpmKey.add(jmiKeyDelete); // Initialize key pair entry pop-up menu including mnemonics and listeners m_jpmKeyPair = new JPopupMenu(); JMenuItem jmiKeyPairCertDetails = new JMenuItem(RB.getString("FPortecle.jmiKeyPairCertDetails.text"), RB.getString("FPortecle.jmiKeyPairCertDetails.mnemonic").charAt(0)); jmiKeyPairCertDetails.setIcon(new ImageIcon(getResImage("FPortecle.jmiKeyPairCertDetails.image"))); jmiKeyPairCertDetails.addActionListener(new ActionListener() { @Override protected void act() { showSelectedEntry(); } }); jmiKeyPairCertDetails.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiKeyPairCertDetails.statusbar"), this)); JMenuItem jmiKeyPairExport = new JMenuItem(RB.getString("FPortecle.jmiKeyPairExport.text"), RB.getString("FPortecle.jmiKeyPairExport.mnemonic").charAt(0)); jmiKeyPairExport.setIcon(new ImageIcon(getResImage("FPortecle.jmiKeyPairExport.image"))); jmiKeyPairExport.addActionListener(new ActionListener() { @Override protected void act() { exportSelectedEntry(); } }); jmiKeyPairExport.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiKeyPairExport.statusbar"), this)); JMenuItem jmiGenerateCSR = new JMenuItem(RB.getString("FPortecle.jmiGenerateCSR.text"), RB.getString("FPortecle.jmiGenerateCSR.mnemonic").charAt(0)); jmiGenerateCSR.setIcon(new ImageIcon(getResImage("FPortecle.jmiGenerateCSR.image"))); jmiGenerateCSR.addActionListener(new ActionListener() { @Override protected void act() { generateCsrSelectedEntry(); } }); jmiGenerateCSR.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiGenerateCSR.statusbar"), this)); JMenuItem jmiImportCAReply = new JMenuItem(RB.getString("FPortecle.jmiImportCAReply.text"), RB.getString("FPortecle.jmiImportCAReply.mnemonic").charAt(0)); jmiImportCAReply.setIcon(new ImageIcon(getResImage("FPortecle.jmiImportCAReply.image"))); jmiImportCAReply.addActionListener(new ActionListener() { @Override protected void act() { importCAReplySelectedEntry(); } }); jmiImportCAReply.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiImportCAReply.statusbar"), this)); JMenuItem jmiRenew = new JMenuItem(RB.getString("FPortecle.jmiRenew.text"), RB.getString("FPortecle.jmiRenew.mnemonic").charAt(0)); jmiRenew.setIcon(new ImageIcon(getResImage("FPortecle.jmiRenew.image"))); jmiRenew.addActionListener(new ActionListener() { @Override protected void act() { renewSelectedEntry(); } }); jmiRenew.addChangeListener(new StatusBarChangeHandler(RB.getString("FPortecle.jmiRenew.statusbar"), this)); m_jmiSetKeyPairPass = new JMenuItem(RB.getString("FPortecle.m_jmiSetKeyPairPass.text"), RB.getString("FPortecle.m_jmiSetKeyPairPass.mnemonic").charAt(0)); m_jmiSetKeyPairPass.setIcon(new ImageIcon(getResImage("FPortecle.m_jmiSetKeyPairPass.image"))); m_jmiSetKeyPairPass.addActionListener(new ActionListener() { @Override protected void act() { setPasswordSelectedEntry(); } }); m_jmiSetKeyPairPass.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.m_jmiSetKeyPairPass.statusbar"), this)); JMenuItem jmiKeyPairDelete = new JMenuItem(RB.getString("FPortecle.jmiKeyPairDelete.text"), RB.getString("FPortecle.jmiKeyPairDelete.mnemonic").charAt(0)); jmiKeyPairDelete.setIcon(new ImageIcon(getResImage("FPortecle.jmiKeyPairDelete.image"))); jmiKeyPairDelete.addActionListener(new ActionListener() { @Override protected void act() { deleteSelectedEntry(); } }); jmiKeyPairDelete.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiKeyPairDelete.statusbar"), this)); JMenuItem jmiKeyPairClone = new JMenuItem(RB.getString("FPortecle.jmiKeyPairClone.text"), RB.getString("FPortecle.jmiKeyPairClone.mnemonic").charAt(0)); jmiKeyPairClone.setIcon(new ImageIcon(getResImage("FPortecle.jmiKeyPairClone.image"))); jmiKeyPairClone.addActionListener(new ActionListener() { @Override protected void act() { cloneSelectedKeyEntry(); } }); jmiKeyPairClone.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiKeyPairClone.statusbar"), this)); JMenuItem jmiKeyPairRename = new JMenuItem(RB.getString("FPortecle.jmiKeyPairRename.text"), RB.getString("FPortecle.jmiKeyPairRename.mnemonic").charAt(0)); jmiKeyPairRename.setIcon(new ImageIcon(getResImage("FPortecle.jmiKeyPairRename.image"))); jmiKeyPairRename.addActionListener(new ActionListener() { @Override protected void act() { renameSelectedEntry(); } }); jmiKeyPairRename.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiKeyPairRename.statusbar"), this)); m_jpmKeyPair.add(jmiKeyPairCertDetails); m_jpmKeyPair.addSeparator(); m_jpmKeyPair.add(jmiKeyPairExport); m_jpmKeyPair.add(jmiGenerateCSR); m_jpmKeyPair.add(jmiImportCAReply); if (EXPERIMENTAL) { // TODO: should show this for self-signed key pair certificates only m_jpmKeyPair.add(jmiRenew); } m_jpmKeyPair.addSeparator(); m_jpmKeyPair.add(m_jmiSetKeyPairPass); m_jpmKeyPair.add(jmiKeyPairDelete); m_jpmKeyPair.add(jmiKeyPairClone); m_jpmKeyPair.add(jmiKeyPairRename); // Initialize Trusted Certificate entry pop-up menu including mnemonics and listeners m_jpmCert = new JPopupMenu(); JMenuItem jmiTrustCertDetails = new JMenuItem(RB.getString("FPortecle.jmiTrustCertDetails.text"), RB.getString("FPortecle.jmiTrustCertDetails.mnemonic").charAt(0)); jmiTrustCertDetails.setIcon(new ImageIcon(getResImage("FPortecle.jmiTrustCertDetails.image"))); jmiTrustCertDetails.addActionListener(new ActionListener() { @Override protected void act() { showSelectedEntry(); } }); jmiTrustCertDetails.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiTrustCertDetails.statusbar"), this)); JMenuItem jmiTrustCertExport = new JMenuItem(RB.getString("FPortecle.jmiTrustCertExport.text"), RB.getString("FPortecle.jmiTrustCertExport.mnemonic").charAt(0)); jmiTrustCertExport.setIcon(new ImageIcon(getResImage("FPortecle.jmiTrustCertExport.image"))); jmiTrustCertExport.addActionListener(new ActionListener() { @Override protected void act() { exportSelectedEntry(); } }); jmiTrustCertExport.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiTrustCertExport.statusbar"), this)); JMenuItem jmiTrustCertDelete = new JMenuItem(RB.getString("FPortecle.jmiTrustCertDelete.text"), RB.getString("FPortecle.jmiTrustCertDelete.mnemonic").charAt(0)); jmiTrustCertDelete.setIcon(new ImageIcon(getResImage("FPortecle.jmiTrustCertDelete.image"))); jmiTrustCertDelete.addActionListener(new ActionListener() { @Override protected void act() { deleteSelectedEntry(); } }); jmiTrustCertDelete.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiTrustCertDelete.statusbar"), this)); JMenuItem jmiTrustCertClone = new JMenuItem(RB.getString("FPortecle.jmiTrustCertClone.text"), RB.getString("FPortecle.jmiTrustCertClone.mnemonic").charAt(0)); jmiTrustCertClone.setIcon(new ImageIcon(getResImage("FPortecle.jmiTrustCertClone.image"))); jmiTrustCertClone.addActionListener(new ActionListener() { @Override protected void act() { cloneSelectedCertificateEntry(); } }); jmiTrustCertClone.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiTrustCertClone.statusbar"), this)); JMenuItem jmiTrustCertRename = new JMenuItem(RB.getString("FPortecle.jmiTrustCertRename.text"), RB.getString("FPortecle.jmiTrustCertRename.mnemonic").charAt(0)); jmiTrustCertRename.setIcon(new ImageIcon(getResImage("FPortecle.jmiTrustCertRename.image"))); jmiTrustCertRename.addActionListener(new ActionListener() { @Override protected void act() { renameSelectedEntry(); } }); jmiTrustCertRename.addChangeListener( new StatusBarChangeHandler(RB.getString("FPortecle.jmiTrustCertRename.statusbar"), this)); m_jpmCert.add(jmiTrustCertDetails); m_jpmCert.addSeparator(); m_jpmCert.add(jmiTrustCertExport); m_jpmCert.addSeparator(); m_jpmCert.add(jmiTrustCertDelete); m_jpmCert.add(jmiTrustCertClone); m_jpmCert.add(jmiTrustCertRename); } /** * Show the appropriate pop-up menu if the originating mouse event indicates that the user clicked upon a * keystore entry in the UI table and the entry is of type key pair or trusted certificate. * * @param evt The mouse event */ private void maybeShowPopup(MouseEvent evt) { if (evt.isPopupTrigger()) { // What row was clicked upon (if any)? Point point = new Point(evt.getX(), evt.getY()); int iRow = m_jtKeyStore.rowAtPoint(point); if (iRow != -1) { // Make the row that was clicked upon the selected one m_jtKeyStore.setRowSelectionInterval(iRow, iRow); // Show one menu if the keystore entry is of type key pair... String currEntry = m_jtKeyStore.getSelectedType(); if (currEntry.equals(KeyStoreTableModel.KEY_PAIR_ENTRY)) { m_jpmKeyPair.show(evt.getComponent(), evt.getX(), evt.getY()); } // ...and another if the type is trusted certificate else if (currEntry.equals(KeyStoreTableModel.TRUST_CERT_ENTRY)) { m_jpmCert.show(evt.getComponent(), evt.getX(), evt.getY()); } // ...and yet another for key-only entries else if (currEntry.equals(KeyStoreTableModel.KEY_ENTRY)) { m_jpmKey.show(evt.getComponent(), evt.getX(), evt.getY()); } // What's this? else { LOG.warning("Popup context menu requested for unknown entry: " + currEntry); } } } } /** * Check if a double click occurred on the keystore table. If it has show the certificate details of the * entry clicked upon. * * @param evt The mouse event */ private void keyStoreTableDoubleClick(MouseEvent evt) { if (evt.getClickCount() > 1) { // Due to the way click events work, at this point we've already received a previous single click // event which has selected a row for us and we can just show the selected entry. showSelectedEntry(); } } /** * Display the about dialog. */ private void showAbout() { // Display About Dialog in the centre of the frame DAbout dAbout = new DAbout(this); dAbout.setLocationRelativeTo(this); SwingHelper.showAndWait(dAbout); } /** * Generate a key pair (with certificate) in the currently opened keystore. * * @return True if a key pair is generated, false otherwise */ private boolean generateKeyPair() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Display the Generate Key Pair dialog to get the key pair generation parameters from the user DGenerateKeyPair dGenerateKeyPair = new DGenerateKeyPair(this); dGenerateKeyPair.setLocationRelativeTo(this); SwingHelper.showAndWait(dGenerateKeyPair); if (!dGenerateKeyPair.isSuccessful()) { return false; // User canceled the dialog } int iKeySize = dGenerateKeyPair.getKeySize(); KeyPairType keyPairType = dGenerateKeyPair.getKeyPairType(); DGeneratingKeyPair dGeneratingKeyPair = new DGeneratingKeyPair(this); // Start key pair generation in background thread SwingWorker<KeyPair, Object> worker = dGeneratingKeyPair.getKeyPairWorker(keyPairType, iKeySize); worker.execute(); // While the key pair is being generated, ask user for certificate date DGenerateCertificate dGenerateCertificate = new DGenerateCertificate(this, RB.getString("FPortecle.GenerateCertificate.Title"), keyPairType); dGenerateCertificate.setLocationRelativeTo(this); SwingHelper.showAndWait(dGenerateCertificate); if (!dGenerateCertificate.isSuccessful()) { // User canceled the dialog. Ideally we'd like to kill the background key pair generation task // here, but unfortunately cancel(true) doesn't do it. Any sane ways to accomplish that? worker.cancel(true); return false; } if (!worker.isDone()) { // Show "progress" dialog dGeneratingKeyPair.setLocationRelativeTo(this); SwingHelper.showAndWait(dGeneratingKeyPair); if (!dGeneratingKeyPair.isClosedByWorker()) { // User canceled the dialog. Ideally we'd like to kill the background key pair generation task // here, but unfortunately cancel(true) doesn't do it. Any sane ways to accomplish that? worker.cancel(true); return false; } } KeyPair keyPair; try { keyPair = worker.get(); } catch (InterruptedException e) { return false; } catch (ExecutionException e) { Throwable cause = e.getCause(); DThrowable.showAndWait(this, null, cause == null ? e : cause); return false; } X509Certificate certificate = dGenerateCertificate.generateCertificate(keyPair); if (certificate == null) { return false; // user canceled dialog or an error occurred } // Get the keystore KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Get an alias for the new keystore entry String sAlias = X509CertUtil.getCertificateAlias(certificate).toLowerCase(); try { sAlias = getNewEntryAlias(keyStore, sAlias, "DGenerateCertificate.KeyPairEntryAlias.Title", false); } catch (KeyStoreException ex) { DThrowable.showAndWait(this, null, ex); return false; } if (sAlias == null) { return false; } // Get a password for the new keystore entry if applicable char[] cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetNewPassword dGetNewPassword = new DGetNewPassword(this, RB.getString("DGenerateCertificate.KeyPairEntryPassword.Title")); dGetNewPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetNewPassword); cPassword = dGetNewPassword.getPassword(); if (cPassword == null) { return false; } } // Place the private key and certificate into the keystore and update the keystore wrapper try { // Delete old entry first if (keyStore.containsAlias(sAlias)) { keyStore.deleteEntry(sAlias); } // Store the new one keyStore.setKeyEntry(sAlias, keyPair.getPrivate(), cPassword, new X509Certificate[] { certificate }); m_keyStoreWrap.setEntryPassword(sAlias, cPassword); m_keyStoreWrap.setChanged(true); } catch (KeyStoreException ex) { DThrowable.showAndWait(this, null, ex); return false; } // Update the frame's components and title selectedAlias = sAlias; updateControls(); updateTitle(); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.KeyPairGenerationSuccessful.message"), RB.getString("FPortecle.GenerateCertificate.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } /** * Open a keystore file from disk. * * @return True if a keystore is opened, false otherwise */ private boolean openKeyStoreFile() { // Does the current keystore contain unsaved changes? if (needSave()) { // Yes - ask the user if it should be saved int iWantSave = wantSave(); if ((iWantSave == JOptionPane.YES_OPTION && !saveKeyStore()) || iWantSave == JOptionPane.CANCEL_OPTION) { return false; } } // Let the user choose a file to open from JFileChooser chooser = FileChooserFactory.getKeyStoreFileChooser(null); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.OpenKeyStoreFile.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showOpenDialog(this); if (iRtnValue == JFileChooser.APPROVE_OPTION) { File fOpenFile = chooser.getSelectedFile(); // File chosen - open the keystore if (openKeyStoreFile(fOpenFile, true)) { return true; } } return false; } /** * Open a CA certificates keystore file from disk. * * @return True if a keystore is opened, false otherwise */ private boolean openCaCertsKeyStoreFile() { // Does the current keystore contain unsaved changes? if (needSave()) { // Yes - ask the user if it should be saved int iWantSave = wantSave(); if ((iWantSave == JOptionPane.YES_OPTION && !saveKeyStore()) || iWantSave == JOptionPane.CANCEL_OPTION) { return false; } } return openKeyStoreFile(m_fCaCertsFile, false); } /** * Open the supplied keystore file from disk. * * @param fKeyStore The keystore file * @param updateLastDir Whether to update the last accessed directory * @return True if a keystore is opened, false otherwise */ /* package private */boolean openKeyStoreFile(File fKeyStore, boolean updateLastDir) { // The keystore does not exist if (!fKeyStore.exists()) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.FileNotFound.message"), fKeyStore), RB.getString("FPortecle.OpenKeyStoreFile.Title"), JOptionPane.WARNING_MESSAGE); return false; } // The keystore file is not a file else if (!fKeyStore.isFile()) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NotFile.message"), fKeyStore), RB.getString("FPortecle.OpenKeyStoreFile.Title"), JOptionPane.WARNING_MESSAGE); return false; } // Update last accessed directory if (updateLastDir) { m_lastDir.updateLastDir(fKeyStore); } // Get the user to enter the keystore's password DGetPassword dGetPassword = new DGetPassword(this, MessageFormat.format(RB.getString("FPortecle.GetKeyStorePassword.Title"), fKeyStore.getName())); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); char[] cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } try { // Load the keystore - try to open as each of the allowed types in turn until successful KeyStore openedKeyStore = null; // Types KeyStoreType[] keyStoreTypes = KeyStoreUtil.getAvailableTypes(); // Exceptions CryptoException[] cexs = new CryptoException[keyStoreTypes.length]; // Tried types StringBuilder tried = new StringBuilder(); for (int iCnt = 0; iCnt < keyStoreTypes.length; iCnt++) { tried.append(", ").append(keyStoreTypes[iCnt].toString()); try { openedKeyStore = KeyStoreUtil.loadKeyStore(fKeyStore, cPassword, keyStoreTypes[iCnt]); break; // Success } catch (CryptoException cex) { cexs[iCnt] = cex; } } if (openedKeyStore == null) { // None of the types worked - show each of the errors? if (tried.length() > 2) { tried.delete(0, 2); // Chop leading ", " } int iSelected = SwingHelper.showConfirmDialog(this, MessageFormat .format(RB.getString("FPortecle.NoOpenKeyStoreFile.message"), fKeyStore, tried), RB.getString("FPortecle.OpenKeyStoreFile.Title")); if (iSelected == JOptionPane.YES_OPTION) { for (CryptoException cex : cexs) { DThrowable.showAndWait(this, null, cex); } } return false; } // Create a keystore wrapper for the keystore m_keyStoreWrap = new KeyStoreWrapper(openedKeyStore, fKeyStore, cPassword); // Update the frame's components and title selectedAlias = null; updateControls(); updateTitle(); // Add keystore file to recent files in file menu m_jmrfFile.add(createRecentFileMenuItem(fKeyStore)); return true; } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoRead.message"), fKeyStore), RB.getString("FPortecle.OpenKeyStoreFile.Title"), JOptionPane.WARNING_MESSAGE); return false; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Open a PKCS #11 keystore. * * @return True if a keystore is opened, false otherwise */ private boolean openKeyStorePkcs11() { // Does the current keystore contain unsaved changes? if (needSave()) { // Yes - ask the user if it should be saved int iWantSave = wantSave(); if ((iWantSave == JOptionPane.YES_OPTION && !saveKeyStore()) || iWantSave == JOptionPane.CANCEL_OPTION) { return false; } } DChoosePkcs11Provider chooser = new DChoosePkcs11Provider(this, RB.getString("FPortecle.ChoosePkcs11Provider.Title"), null); chooser.setLocationRelativeTo(this); SwingHelper.showAndWait(chooser); String provider = chooser.getProvider(); return (provider == null) ? false : openKeyStorePkcs11(provider); } /** * Open the supplied PKCS #11 keystore. * * @param sPkcs11Provider The PKCS #11 provider * @return True if a keystore is opened, false otherwise */ private boolean openKeyStorePkcs11(String sPkcs11Provider) { // Get the user to enter the keystore's password DGetPassword dGetPassword = new DGetPassword(this, MessageFormat.format(RB.getString("FPortecle.GetKeyStorePassword.Title"), sPkcs11Provider)); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); char[] cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } // Load the keystore KeyStore openedKeyStore = null; try { openedKeyStore = KeyStoreUtil.loadKeyStore(sPkcs11Provider, cPassword); m_keyStoreWrap = new KeyStoreWrapper(openedKeyStore, null, cPassword); } catch (CryptoException e) { int iSelected = JOptionPane.showConfirmDialog(this, MessageFormat.format(RB.getString("FPortecle.NoOpenKeyStorePkcs11.message"), sPkcs11Provider), RB.getString("FPortecle.ChoosePkcs11Provider.Title"), JOptionPane.YES_NO_OPTION); if (iSelected == JOptionPane.YES_OPTION) { DThrowable.showAndWait(this, null, e); } return false; } // Update the frame's components and title selectedAlias = null; updateControls(); updateTitle(); return true; } /** * Save the currently opened keystore back to the file it was originally opened from. * * @return True if the keystore is saved to disk, false otherwise */ /* package private */boolean saveKeyStore() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // File to save to File fSaveFile = m_keyStoreWrap.getKeyStoreFile(); // Not saved before - use Save As if (fSaveFile == null) { return saveKeyStoreAs(); } // Get the password to protect the keystore with char[] cPassword = m_keyStoreWrap.getPassword(); // No password set for keystore - get one from the user if (cPassword == null) { cPassword = getNewKeyStorePassword(); // User canceled - cancel save if (cPassword == null) { return false; } } try { // Do the save m_keyStoreWrap .setKeyStore(KeyStoreUtil.saveKeyStore(m_keyStoreWrap.getKeyStore(), fSaveFile, cPassword)); // Update the keystore wrapper m_keyStoreWrap.setPassword(cPassword); m_keyStoreWrap.setKeyStoreFile(fSaveFile); m_keyStoreWrap.setChanged(false); // Update the frame's components and title updateControls(); updateTitle(); return true; } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fSaveFile), RB.getString("FPortecle.SaveKeyStore.Title"), JOptionPane.WARNING_MESSAGE); return false; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Get a new keystore password. * * @return The new keystore password */ private char[] getNewKeyStorePassword() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Display the get new password dialog DGetNewPassword dGetNewPassword = new DGetNewPassword(this, RB.getString("FPortecle.SetKeyStorePassword.Title")); dGetNewPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetNewPassword); // Dialog returned - retrieve the password and return it return dGetNewPassword.getPassword(); } /** * Save the currently opened keystore to disk to what may be a different file from the one it was opened * from (if any). * * @return True if the keystore is saved to disk, false otherwise */ private boolean saveKeyStoreAs() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Keystore's current password char[] cPassword = m_keyStoreWrap.getPassword(); // Get a new password if this keystore exists in another file or is an unsaved keystore for which no // password has been set yet if (m_keyStoreWrap.getKeyStoreFile() != null || (m_keyStoreWrap.getKeyStoreFile() == null && cPassword == null)) { cPassword = getNewKeyStorePassword(); if (cPassword == null) { return false; } } // Let the user choose a save file JFileChooser chooser = FileChooserFactory.getKeyStoreFileChooser(m_keyStoreWrap.getKeyStoreType()); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.SaveKeyStoreAs.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showSaveDialog(this); if (iRtnValue == JFileChooser.APPROVE_OPTION) { File fSaveFile = chooser.getSelectedFile(); try { if (!confirmOverwrite(fSaveFile, RB.getString("FPortecle.SaveKeyStoreAs.Title"))) { return false; } // Save the keystore to file m_keyStoreWrap .setKeyStore(KeyStoreUtil.saveKeyStore(m_keyStoreWrap.getKeyStore(), fSaveFile, cPassword)); // Update the keystore wrapper m_keyStoreWrap.setPassword(cPassword); m_keyStoreWrap.setKeyStoreFile(fSaveFile); m_keyStoreWrap.setChanged(false); // Update the frame's components and title updateControls(); updateTitle(); // Add keystore file to recent files in file menu m_jmrfFile.add(createRecentFileMenuItem(fSaveFile)); m_lastDir.updateLastDir(fSaveFile); return true; } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fSaveFile), RB.getString("FPortecle.SaveKeyStoreAs.Title"), JOptionPane.WARNING_MESSAGE); return false; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } return false; } /** * Check if the currently opened keystore requires to be saved. * * @return True if the keystore has been changed since the last open/save, false otherwise */ /* package private */boolean needSave() { return (m_keyStoreWrap != null && m_keyStoreWrap.isChanged()); } /** * Ask the user if they want to save the current keystore file. * * @return JOptionPane.YES_OPTION, JOptionPane.NO_OPTION or JOptionPane.CANCEL_OPTION; * JOptionPane.CLOSED_OPTION is reported as JOptionPane.CANCEL_OPTION */ /* package private */int wantSave() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; File fKeyStoreFile = m_keyStoreWrap.getKeyStoreFile(); String sKeyStoreName; if (fKeyStoreFile == null) { sKeyStoreName = RB.getString("FPortecle.Untitled"); } else { sKeyStoreName = fKeyStoreFile.getName(); } String sMessage = MessageFormat.format(RB.getString("FPortecle.WantSaveChanges.message"), sKeyStoreName); int iSelected = JOptionPane.showConfirmDialog(this, sMessage, RB.getString("FPortecle.WantSaveChanges.Title"), JOptionPane.YES_NO_CANCEL_OPTION); if (iSelected == JOptionPane.CLOSED_OPTION) { iSelected = JOptionPane.CANCEL_OPTION; } return iSelected; } /** * Create a new keystore file. * * @return True is a new keystore file is created, false otherwise */ private boolean newKeyStore() { // Does the current keystore contain unsaved changes? if (needSave()) { // Yes - ask the user if it should be saved int iWantSave = wantSave(); if (iWantSave == JOptionPane.YES_OPTION) { // Save it if (!saveKeyStore()) { return false; } } else if (iWantSave == JOptionPane.CANCEL_OPTION) { return false; } } try { // Ask user for keystore type DNewKeyStoreType dNewKeyStoreType = new DNewKeyStoreType(this); dNewKeyStoreType.setLocationRelativeTo(this); SwingHelper.showAndWait(dNewKeyStoreType); KeyStoreType keyStoreType = dNewKeyStoreType.getKeyStoreType(); // No keystore type chosen if (keyStoreType == null) { return false; } // Create new keystore KeyStore newKeyStore = KeyStoreUtil.createKeyStore(keyStoreType); // Update the keystore wrapper m_keyStoreWrap = new KeyStoreWrapper(newKeyStore); // Update the frame's components and title selectedAlias = null; updateControls(); updateTitle(); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user examine the contents of a certificate file. * * @param fCertFile File to load the certificate from; if <code>null</code>, prompt user */ private void examineCert(File fCertFile) { if (fCertFile == null) { fCertFile = chooseExamineCertFile(); } if (fCertFile == null) { return; } m_lastDir.updateLastDir(fCertFile); // Show the certificates DViewCertificate.showAndWait(this, fCertFile); } /** * Let the user examine the contents of a certificate file from a SSL connection. * * @return True if the user was able to examine the certificate, false otherwise */ private boolean examineCertSSL(InetSocketAddress ia) { if (ia == null) { ia = chooseExamineCertSSL(); } if (ia == null) { return false; } // TODO: options from user boolean bVerifyCerts = false; int timeOut = 10000; // Get the certificates received from the connection X509Certificate[] certs = null; String protocol = null; String cipherSuite = null; SSLSocket ss = null; try { SSLSocketFactory sf; if (bVerifyCerts) { sf = (SSLSocketFactory) SSLSocketFactory.getDefault(); } else { // @@@TODO: cache all this? SSLContext sc = SSLContext.getInstance("SSL"); X509TrustManager[] tm = { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { // Trust anything } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { // Trust anything } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }; if (m_rnd == null) { m_rnd = new SecureRandom(); } sc.init(null, tm, m_rnd); sf = sc.getSocketFactory(); } ss = (SSLSocket) sf.createSocket(); ss.setSoTimeout(timeOut); ss.connect(ia, timeOut); SSLSession sess = ss.getSession(); // TODO: fails with GNU Classpath: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29692 certs = (X509Certificate[]) sess.getPeerCertificates(); protocol = sess.getProtocol(); cipherSuite = sess.getCipherSuite(); sess.invalidate(); } catch (Exception e) { DThrowable.showAndWait(this, null, e); return false; } finally { if (ss != null && !ss.isClosed()) { try { ss.close(); } catch (IOException e) { DThrowable.showAndWait(this, null, e); } } } // Check what we got try { // If there are any display the view certificate dialog with them if (certs != null && certs.length != 0) { DViewCertificate dViewCertificate = new DViewCertificate(this, MessageFormat.format(RB.getString("FPortecle.CertDetailsSSL.Title"), ia.getHostName() + ":" + ia.getPort()), certs, protocol, cipherSuite); dViewCertificate.setLocationRelativeTo(this); SwingHelper.showAndWait(dViewCertificate); return true; } return false; } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user examine the contents of a CSR file. * * @param fCSRFile File to load the CSR from; if <code>null</code>, prompt user * @return True if the user was able to examine the CSR file, false otherwise */ private boolean examineCSR(File fCSRFile) { if (fCSRFile == null) { fCSRFile = chooseExamineCSRFile(); } if (fCSRFile == null) { return false; } // Get the CSR contained within the file PKCS10CertificationRequest csr = openCSR(fCSRFile); m_lastDir.updateLastDir(fCSRFile); // If a CSR is available then display the view CSR dialog with it if (csr != null) { try { DViewCSR dViewCSR = new DViewCSR(this, MessageFormat.format(RB.getString("FPortecle.CsrDetailsFile.Title"), fCSRFile.getName()), csr); dViewCSR.setLocationRelativeTo(this); SwingHelper.showAndWait(dViewCSR); return true; } catch (CryptoException e) { DThrowable.showAndWait(this, null, e); } } return false; } /** * Let the user examine the contents of a CRL file. * * @param fCRLFile File to load the CRL from; if <code>null</code>, prompt user */ private void examineCRL(File fCRLFile) { if (fCRLFile == null) { fCRLFile = chooseExamineCRLFile(); } if (fCRLFile == null) { return; } m_lastDir.updateLastDir(fCRLFile); // Show the CRL DViewCRL.showAndWait(this, fCRLFile); } /** * Let the user choose a CA reply file to import. * * @return The chosen file or null if none was chosen */ private File chooseImportCAFile() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; JFileChooser chooser = FileChooserFactory.getCertFileChooser(); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ImportCaReply.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.ImportCaReply.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a certificate file to examine. * * @return The chosen file or null if none was chosen */ private File chooseExamineCertFile() { JFileChooser chooser = FileChooserFactory.getCertFileChooser(); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExamineCertificate.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.ExamineCertificate.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a certificate to examine from a SSL connection. * * @return The chosen inet address or null if none was chosen */ private InetSocketAddress chooseExamineCertSSL() { DGetHostPort d = new DGetHostPort(this, RB.getString("FPortecle.ExamineCertificateSSL.Title"), null); d.setLocationRelativeTo(this); SwingHelper.showAndWait(d); return d.getHostPort(); } /** * Let the user choose a CSR file to examine. * * @return The chosen file or null if none was chosen */ private File chooseExamineCSRFile() { JFileChooser chooser = FileChooserFactory.getCsrFileChooser(null); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExamineCsr.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.ExamineCsr.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a CRL file to examine. * * @return The chosen file or null if none was chosen */ private File chooseExamineCRLFile() { JFileChooser chooser = FileChooserFactory.getCrlFileChooser(); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExamineCrl.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.ExamineCrl.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a trusted certificate file to import. * * @return The chosen file or null if none was chosen */ private File chooseTrustCertFile() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; JFileChooser chooser = FileChooserFactory.getX509FileChooser(null); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ImportTrustCert.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.ImportTrustCert.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a file to import from. * * @return The chosen file or null if none was chosen */ private File chooseImportFile() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; JFileChooser chooser = FileChooserFactory.getKeyPairFileChooser(null); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ImportKeyPairFile.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.ImportKeyPairFile.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a file to generate a CSR in. * * @param basename default filename (without extension) * @return The chosen file or null if none was chosen */ private File chooseGenerateCsrFile(String basename) { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; JFileChooser chooser = FileChooserFactory.getCsrFileChooser(basename); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.GenerateCsr.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.GenerateCsr.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Open a certificate file. * * @param fCertFile The certificate file * @return The certificates found in the file or null if there were none */ private X509Certificate[] openCert(File fCertFile) { try { URL url = fCertFile.toURI().toURL(); ArrayList<Exception> exs = new ArrayList<>(); X509Certificate[] certs = X509CertUtil.loadCertificates(url, exs); if (certs == null) { // None of the types worked - show each of the errors? int iSelected = SwingHelper.showConfirmDialog(this, MessageFormat.format(RB.getString("FPortecle.NoOpenCertificate.message"), fCertFile), RB.getString("FPortecle.OpenCertificate.Title")); if (iSelected == JOptionPane.YES_OPTION) { for (Exception e : exs) { DThrowable.showAndWait(this, null, e); } } } else if (certs.length == 0) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoCertsFound.message"), fCertFile), RB.getString("FPortecle.OpenCertificate.Title"), JOptionPane.WARNING_MESSAGE); } return certs; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return null; } } /** * Open a CSR file. * * @param fCSRFile The CSR file * @return The CSR found in the file or null if there wasn't one */ private PKCS10CertificationRequest openCSR(File fCSRFile) { try { return X509CertUtil.loadCSR(fCSRFile.toURI().toURL()); } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoRead.message"), fCSRFile), MessageFormat.format(RB.getString("FPortecle.CsrDetailsFile.Title"), fCSRFile.getName()), JOptionPane.WARNING_MESSAGE); return null; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return null; } } /** * Let the user import a CA reply into the selected key pair entry. * * @return True if the import is successful, false otherwise */ private boolean importCAReplySelectedEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // What entry is selected? String sAlias = m_jtKeyStore.getSelectedAlias(); if (sAlias == null) { return false; } // Get the keystore KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Let the user choose a file for the trusted certificate File fCertFile = chooseImportCAFile(); if (fCertFile == null) { return false; } // Load the certificate(s) X509Certificate[] certs = openCert(fCertFile); if (certs == null || certs.length == 0) { return false; } try { // Order the new certificates into a chain... certs = X509CertUtil.orderX509CertChain(certs); // ...and those that exist in the entry already X509Certificate[] oldCerts = X509CertUtil .orderX509CertChain(X509CertUtil.convertCertificates(keyStore.getCertificateChain(sAlias))); // Compare the public keys of the start of each chain if (!oldCerts[0].getPublicKey().equals(certs[0].getPublicKey())) { JOptionPane.showMessageDialog(this, RB.getString("FPortecle.NoMatchPubKeyCaReply.message"), RB.getString("FPortecle.ImportCaReply.Title"), JOptionPane.ERROR_MESSAGE); return false; } // If the CA certificates keystore is to be used and it has yet to be loaded then do so if (m_bUseCaCerts && m_caCertsKeyStore == null) { m_caCertsKeyStore = openCaCertsKeyStore(); if (m_caCertsKeyStore == null) { // Failed to load CA certificates keystore return false; } } // Holds the new certificate chain for the entry should the import succeed X509Certificate[] newCertChain = null; /* * PKCS #7 reply - try and match the self-signed root with any of the certificates in the CA * certificates or current keystore */ if (certs.length > 1) { X509Certificate rootCert = certs[certs.length - 1]; String sMatchAlias = null; if (m_bUseCaCerts) // Match against CA certificates keystore { sMatchAlias = X509CertUtil.matchCertificate(m_caCertsKeyStore, rootCert); } if (sMatchAlias == null) // Match against current keystore { sMatchAlias = X509CertUtil.matchCertificate(keyStore, rootCert); } // No match if (sMatchAlias == null) { // Tell the user what is happening JOptionPane.showMessageDialog(this, RB.getString("FPortecle.NoMatchRootCertCaReplyConfirm.message"), RB.getString("FPortecle.ImportCaReply.Title"), JOptionPane.INFORMATION_MESSAGE); // Display the certificate to the user DViewCertificate dViewCertificate = new DViewCertificate(this, MessageFormat.format(RB.getString("FPortecle.CertDetails.Title"), fCertFile.getName()), new X509Certificate[] { rootCert }); dViewCertificate.setLocationRelativeTo(this); SwingHelper.showAndWait(dViewCertificate); // Request confirmation that the certificate is to be trusted int iSelected = JOptionPane.showConfirmDialog(this, RB.getString("FPortecle.AcceptCaReply.message"), RB.getString("FPortecle.ImportCaReply.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { return false; } newCertChain = certs; } else { newCertChain = certs; } } // Single X.509 certificate reply - try and establish a chain of trust from the certificate and // ending with a root CA self-signed certificate else { KeyStore[] compKeyStores = null; // Establish against CA certificates keystore and current keystore if (m_bUseCaCerts) { compKeyStores = new KeyStore[] { m_caCertsKeyStore, keyStore }; } else // Establish against current keystore only { compKeyStores = new KeyStore[] { keyStore }; } X509Certificate[] trustChain = X509CertUtil.establishTrust(compKeyStores, certs[0]); if (trustChain == null) { JOptionPane.showMessageDialog(this, RB.getString("FPortecle.NoTrustCaReply.message"), RB.getString("FPortecle.ImportCaReply.Title"), JOptionPane.ERROR_MESSAGE); return false; } newCertChain = trustChain; } // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } // Replace the certificate chain Key privKey = keyStore.getKey(sAlias, cPassword); keyStore.deleteEntry(sAlias); keyStore.setKeyEntry(sAlias, privKey, cPassword, newCertChain); // Update the keystore wrapper m_keyStoreWrap.setChanged(true); m_keyStoreWrap.setEntryPassword(sAlias, cPassword); // Update the frame's components and title updateControls(); updateTitle(); m_lastDir.updateLastDir(fCertFile); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.ImportCaReplySuccessful.message"), RB.getString("FPortecle.ImportCaReply.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user renew a self-signed certificate for the selected key pair entry. * * @return True if the renewal is successful, false otherwise */ private boolean renewSelectedEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // What entry is selected? String sAlias = m_jtKeyStore.getSelectedAlias(); if (sAlias == null) { return false; } // Get the keystore KeyStore keyStore = m_keyStoreWrap.getKeyStore(); try { // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } // TODO: ask from user int renewalDays = 365; KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(sAlias, new KeyStore.PasswordProtection(cPassword)); PrivateKey privateKey = entry.getPrivateKey(); X509Certificate oldCert = (X509Certificate) entry.getCertificate(); PublicKey publicKey = oldCert.getPublicKey(); X509Certificate newCert = X509CertUtil.renewCert(oldCert, renewalDays, publicKey, privateKey); KeyStore.PrivateKeyEntry newEntry = new KeyStore.PrivateKeyEntry(privateKey, new Certificate[] { newCert }); if (keyStore.containsAlias(sAlias)) { keyStore.deleteEntry(sAlias); } keyStore.setEntry(sAlias, newEntry, new KeyStore.PasswordProtection(cPassword)); // Update the keystore wrapper m_keyStoreWrap.setChanged(true); m_keyStoreWrap.setEntryPassword(sAlias, new char[0]); // Update the frame's components and title updateControls(); updateTitle(); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.RenewSelfSignedSuccessful.message"), RB.getString("FPortecle.RenewSelfSigned.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user import a trusted certificate. * * @return True if the import is successful, false otherwise */ private boolean importTrustedCert() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Let the user choose a file for the trusted certificate File fCertFile = chooseTrustCertFile(); if (fCertFile == null) { return false; } // Load the certificate(s) X509Certificate[] certs = openCert(fCertFile); if (certs == null || certs.length == 0) { return false; } if (certs.length > 1) { // Cannot import more than one certificate JOptionPane.showMessageDialog(this, RB.getString("FPortecle.NoMultipleTrustCertImport.message"), RB.getString("FPortecle.ImportTrustCert.Title"), JOptionPane.ERROR_MESSAGE); return false; } X509Certificate trustCert = certs[0]; try { // Get the keystore KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Certificate already exists in the keystore String sMatchAlias = X509CertUtil.matchCertificate(keyStore, trustCert); if (sMatchAlias != null) { int iSelected = JOptionPane.showConfirmDialog(this, MessageFormat.format(RB.getString("FPortecle.TrustCertExistsConfirm.message"), sMatchAlias), RB.getString("FPortecle.ImportTrustCert.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { return false; } } // If the CA certificates keystore is to be used and it has yet to be loaded then do so if (m_bUseCaCerts && m_caCertsKeyStore == null) { m_caCertsKeyStore = openCaCertsKeyStore(); if (m_caCertsKeyStore == null) { // Failed to load CA certificates keystore return false; } } // If we cannot establish trust for the certificate against the CA certificates keystore or the // current keystore then, display the certificate to the user for confirmation KeyStore[] compKeyStores = null; // Establish against CA certificates keystore and current keystore if (m_bUseCaCerts) { compKeyStores = new KeyStore[] { m_caCertsKeyStore, keyStore }; } else // Establish against current keystore only { compKeyStores = new KeyStore[] { keyStore }; } if (X509CertUtil.establishTrust(compKeyStores, trustCert) == null) { // Tell the user what is happening JOptionPane.showMessageDialog(this, RB.getString("FPortecle.NoTrustPathCertConfirm.message"), RB.getString("FPortecle.ImportTrustCert.Title"), JOptionPane.INFORMATION_MESSAGE); // Display the certificate to the user DViewCertificate dViewCertificate = new DViewCertificate(this, MessageFormat.format(RB.getString("FPortecle.CertDetails.Title"), fCertFile.getName()), new X509Certificate[] { trustCert }); dViewCertificate.setLocationRelativeTo(this); SwingHelper.showAndWait(dViewCertificate); // Request confirmation that the certificate is to be trusted int iSelected = JOptionPane.showConfirmDialog(this, RB.getString("FPortecle.AcceptTrustCert.message"), RB.getString("FPortecle.ImportTrustCert.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { return false; } } String sAlias = X509CertUtil.getCertificateAlias(trustCert).toLowerCase(); sAlias = getNewEntryAlias(keyStore, sAlias, "FPortecle.TrustCertEntryAlias.Title", false); if (sAlias == null) { return false; } // Delete old entry first if (keyStore.containsAlias(sAlias)) { keyStore.deleteEntry(sAlias); } // Import the trusted certificate keyStore.setCertificateEntry(sAlias, trustCert); // Update the keystore wrapper m_keyStoreWrap.setChanged(true); // Update the frame's components and title selectedAlias = sAlias; updateControls(); updateTitle(); m_lastDir.updateLastDir(fCertFile); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.ImportTrustCertSuccessful.message"), RB.getString("FPortecle.ImportTrustCert.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user import a key pair a PKCS #12 keystore or a PEM bundle. * * @return True if the import is successful, false otherwise */ private boolean importKeyPair() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Let the user choose a file to import from File fKeyPairFile = chooseImportFile(); if (fKeyPairFile == null) { return false; } m_lastDir.updateLastDir(fKeyPairFile); // Not a file? if (!fKeyPairFile.isFile()) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NotFile.message"), fKeyPairFile), RB.getString("FPortecle.ImportKeyPair.Title"), JOptionPane.WARNING_MESSAGE); return false; } ArrayList<Exception> exceptions = new ArrayList<>(); PasswordFinder passwordFinder = new PasswordFinder() { private int passwordNumber = 1; @Override public char[] getPassword() { // Get the user to enter the private key password DGetPassword dGetPassword = new DGetPassword(FPortecle.this, MessageFormat.format(RB.getString("FPortecle.PrivateKeyPassword.Title"), new Object[] { String.valueOf(passwordNumber) })); dGetPassword.setLocationRelativeTo(FPortecle.this); SwingHelper.showAndWait(dGetPassword); char[] cPassword = dGetPassword.getPassword(); passwordNumber++; return cPassword; } }; KeyStore tempStore = null; try (PEMParser reader = new PEMParser(new FileReader(fKeyPairFile.getPath()))) { tempStore = KeyStoreUtil.loadEntries(reader, passwordFinder); if (tempStore.size() == 0) { tempStore = null; } } catch (Exception e) { exceptions.add(e); } // Treat as PKCS #12 keystore if (tempStore == null) { // Get the user to enter the PKCS #12 keystore's password DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.Pkcs12Password.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); char[] cPkcs12Password = dGetPassword.getPassword(); if (cPkcs12Password == null) { return false; } // Load the PKCS #12 keystore try { tempStore = KeyStoreUtil.loadKeyStore(fKeyPairFile, cPkcs12Password, KeyStoreType.PKCS12); } catch (Exception e) { exceptions.add(e); } } if (tempStore == null && !exceptions.isEmpty()) { int iSelected = SwingHelper.showConfirmDialog(this, MessageFormat.format(RB.getString("FPortecle.NoOpenKeyPairFile.message"), fKeyPairFile), RB.getString("FPortecle.ImportKeyPairFile.Title")); if (iSelected == JOptionPane.YES_OPTION) { for (Exception e : exceptions) { DThrowable.showAndWait(this, null, e); } } return false; } try { // Display the import key pair dialog supplying the PKCS #12 keystore to it DImportKeyPair dImportKeyPair = new DImportKeyPair(this, tempStore); dImportKeyPair.setLocationRelativeTo(this); SwingHelper.showAndWait(dImportKeyPair); // Get the private key and certificate chain of the key pair Key privateKey = dImportKeyPair.getPrivateKey(); Certificate[] certs = dImportKeyPair.getCertificateChain(); if (privateKey == null || certs == null) { // User did not select a key pair for import return false; } // Get an alias for the new keystore entry String sAlias = dImportKeyPair.getAlias(); if (sAlias == null) { sAlias = X509CertUtil.getCertificateAlias(X509CertUtil.convertCertificate(certs[0])); } sAlias = getNewEntryAlias(keyStore, sAlias, "FPortecle.KeyPairEntryAlias.Title", false); if (sAlias == null) { return false; } // Get a password for the new keystore entry if applicable char[] cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetNewPassword dGetNewPassword = new DGetNewPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetNewPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetNewPassword); cPassword = dGetNewPassword.getPassword(); if (cPassword == null) { return false; } } // Delete old entry first if (keyStore.containsAlias(sAlias)) { keyStore.deleteEntry(sAlias); } // Place the private key and certificate chain into the keystore and update the keystore wrapper keyStore.setKeyEntry(sAlias, privateKey, cPassword, certs); m_keyStoreWrap.setEntryPassword(sAlias, cPassword); m_keyStoreWrap.setChanged(true); // Update the frame's components and title selectedAlias = sAlias; updateControls(); updateTitle(); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.KeyPairImportSuccessful.message"), RB.getString("FPortecle.ImportKeyPair.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Open the CA certificates keystore from disk. * * @return The keystore if it could be opened or null otherwise */ private KeyStore openCaCertsKeyStore() { // Get the user to enter the CA certificates keystore's password DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.CaCertsKeyStorePassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); char[] cPassword = dGetPassword.getPassword(); if (cPassword == null) { return null; } try { // Load the CA certificates keystore - try to open as each of the allowed types in turn until // successful KeyStore caCertsKeyStore = null; // Types KeyStoreType[] keyStoreTypes = KeyStoreUtil.getAvailableTypes(); // Exceptions CryptoException[] cexs = new CryptoException[keyStoreTypes.length]; // Tried types StringBuilder tried = new StringBuilder(); for (int iCnt = 0; iCnt < keyStoreTypes.length; iCnt++) { tried.append(", ").append(keyStoreTypes[iCnt].toString()); try { caCertsKeyStore = KeyStoreUtil.loadKeyStore(m_fCaCertsFile, cPassword, keyStoreTypes[iCnt]); break; // Success } catch (CryptoException cex) { cexs[iCnt] = cex; } } if (caCertsKeyStore == null) { // None of the types worked - show each of the errors? if (tried.length() > 2) { tried.delete(0, 2); // Chop leading ", " } int iSelected = SwingHelper .showConfirmDialog(this, MessageFormat.format(RB.getString("FPortecle.NoOpenCaCertsKeyStore.message"), m_fCaCertsFile, tried), RB.getString("FPortecle.OpenCaCertsKeyStore.Title")); if (iSelected == JOptionPane.YES_OPTION) { for (CryptoException cex : cexs) { DThrowable.showAndWait(this, null, cex); } } return null; } return caCertsKeyStore; } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoRead.message"), m_fCaCertsFile), RB.getString("FPortecle.OpenCaCertsKeyStore.Title"), JOptionPane.WARNING_MESSAGE); return null; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return null; } } /** * Display the help dialog. */ private void showHelp() { // Create the dialog if it does not already exist if (m_fHelp == null) { URL toc; URL home; String s = RB.getString("FPortecle.Help.Contents"); if (s.startsWith("/")) { toc = FPortecle.class.getResource(s); } else { try { toc = new URL(s); } catch (MalformedURLException e) { DThrowable.showAndWait(this, null, e); return; } } s = RB.getString("FPortecle.Help.Home"); if (s.startsWith("/")) { home = FPortecle.class.getResource(s); } else { try { home = new URL(s); } catch (MalformedURLException e) { DThrowable.showAndWait(this, null, e); return; } } m_fHelp = new FHelp(RB.getString("FPortecle.Help.Title"), home, toc); m_fHelp.setLocation(getX() + 25, getY() + 25); } // Show the help dialog SwingHelper.showAndWait(m_fHelp); } /** * Display application's web site. */ private void visitWebsite() { DesktopUtil.browse(this, URI.create(RB.getString("FPortecle.WebsiteAddress"))); } /** * Display Portecle project page at SourceForge.net. */ private void visitSFNetProject() { DesktopUtil.browse(this, URI.create(RB.getString("FPortecle.SFNetProjectAddress"))); } /** * Display Portecle mailing lists' sign up page at SourceForge.net. */ private void visitMailListSignup() { DesktopUtil.browse(this, URI.create(RB.getString("FPortecle.MailListSignupAddress"))); } /** * Display donation web page. */ private void makeDonation() { DesktopUtil.browse(this, URI.create(RB.getString("FPortecle.DonateAddress"))); } /** * Display Security Provider Information dialog. */ private void showSecurityProviders() { DProviderInfo dProviderInfo = new DProviderInfo(this); dProviderInfo.setLocationRelativeTo(this); SwingHelper.showAndWait(dProviderInfo); } /** * Display JAR Information dialog. */ private void showJarInfo() { try { DJarInfo dJarInfo = new DJarInfo(this); dJarInfo.setLocationRelativeTo(this); SwingHelper.showAndWait(dJarInfo); } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); } } /** * Display the options dialog and store the user's choices. */ private void showOptions() { DOptions dOptions = new DOptions(this, m_bUseCaCerts, m_fCaCertsFile); dOptions.setLocationRelativeTo(this); SwingHelper.showAndWait(dOptions); // Store/apply the chosen options: // CA certificates file File fTmp = dOptions.getCaCertsFile(); if (!fTmp.equals(m_fCaCertsFile)) { // CA certificates file changed - any stored CA certificates keystore is now invalid m_caCertsKeyStore = null; } m_fCaCertsFile = fTmp; // Use CA certificates? m_bUseCaCerts = dOptions.isUseCaCerts(); // Look & feel String newLookFeelClassName = dOptions.getLookFeelClassName(); // Look & feel decoration boolean bLookFeelDecoration = dOptions.isLookFeelDecoration(); // Look & feel/decoration changed? // Note: UIManager.LookAndFeelInfo.getName() and LookAndFeel.getName() can be different for the same // L&F (one example is the GTK+ one in J2SE 5 RC2 (Linux), where the former is "GTK+" and the latter // is "GTK look and feel"). Therefore, compare the class names instead. if (newLookFeelClassName != null && (!newLookFeelClassName.equals(UIManager.getLookAndFeel().getClass().getName()) || bLookFeelDecoration != JFrame.isDefaultLookAndFeelDecorated())) { // Yes - save selections to be picked up by application preferences, lookFeelClassName = newLookFeelClassName; m_bLookFeelDecorationOptions = bLookFeelDecoration; saveAppPrefs(); JFrame.setDefaultLookAndFeelDecorated(bLookFeelDecoration); JDialog.setDefaultLookAndFeelDecorated(bLookFeelDecoration); try { UIManager.setLookAndFeel(lookFeelClassName); SwingUtilities.updateComponentTreeUI(getRootPane()); pack(); } catch (Exception e) { DThrowable.showAndWait(this, null, e); } } } /** * Convert the loaded keystore's type to that supplied. * * @param keyStoreType New keystore type * @return True if the keystore's type was changed, false otherwise */ private boolean changeKeyStoreType(KeyStoreType keyStoreType) { assert m_keyStoreWrap.getKeyStore() != null; // Cannot change type to current type assert !m_keyStoreWrap.getKeyStore().getType().equals(keyStoreType.name()); try { // Get current keystore and type KeyStore currentKeyStore = m_keyStoreWrap.getKeyStore(); KeyStoreType currentType = m_keyStoreWrap.getKeyStoreType(); // Create empty keystore of new type KeyStore newKeyStore = KeyStoreUtil.createKeyStore(keyStoreType); // Flag used to tell if we have warned the user about default key pair entry passwords for // keystores changed to types that don't support entry passwords boolean bWarnPasswordUnsupported = false; // Flag used to tell if we have warned the user about key entries not being carried over by the // change boolean bWarnNoChangeKey = false; // For every entry in the current keystore transfer it to the new one - get key/key pair entry // passwords from the wrapper and if not present there from the user for (Enumeration<String> aliases = currentKeyStore.aliases(); aliases.hasMoreElements();) { // Entry alias String sAlias = aliases.nextElement(); // Trusted certificate entry if (currentKeyStore.isCertificateEntry(sAlias)) { // Check and ask about alias overwriting issues if (newKeyStore.containsAlias(sAlias)) { int iSelected = JOptionPane.showConfirmDialog(this, RB.getString("FPortecle.WarnOverwriteAlias.message"), RB.getString("FPortecle.ChangeKeyStoreType.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { continue; } } // Get trusted certificate and place it in the new keystore Certificate trustedCertificate = currentKeyStore.getCertificate(sAlias); newKeyStore.setCertificateEntry(sAlias, trustedCertificate); } // Key or Key pair entry else if (currentKeyStore.isKeyEntry(sAlias)) { // Get certificate chain - will be null if entry is key Certificate[] certificateChain = currentKeyStore.getCertificateChain(sAlias); if (certificateChain == null || certificateChain.length == 0) { // Key entries are not transferred - warn the user if we haven't done so already if (!bWarnNoChangeKey) { bWarnNoChangeKey = true; int iSelected = JOptionPane.showConfirmDialog(this, RB.getString("FPortecle.WarnNoChangeKey.message"), RB.getString("FPortecle.ChangeKeyStoreType.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { return false; } } continue; } // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (currentType.isEntryPasswordSupported()) { String sTitle = MessageFormat.format( RB.getString("FPortecle.ChangeKeyStoreTypeKeyPairEntryPassword.Title"), sAlias); DGetPassword dGetPassword = new DGetPassword(this, sTitle); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } // Use password to get key pair Key key = currentKeyStore.getKey(sAlias, cPassword); // The current keystore type does not support entry passwords so the password will be set // to the "dummy value" password if (!currentType.isEntryPasswordSupported()) { // Warn the user about this if (!bWarnPasswordUnsupported) { bWarnPasswordUnsupported = true; JOptionPane.showMessageDialog(this, MessageFormat.format( RB.getString("FPortecle.ChangeFromPasswordUnsupported.message"), new String(KeyStoreUtil.DUMMY_PASSWORD)), RB.getString("FPortecle.ChangeKeyStoreType.Title"), JOptionPane.INFORMATION_MESSAGE); } } // The new keystore type does not support entry passwords so use dummy password for entry else if (!keyStoreType.isEntryPasswordSupported()) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; } // Check and ask about alias overwriting issues if (newKeyStore.containsAlias(sAlias)) { int iSelected = JOptionPane.showConfirmDialog(this, RB.getString("FPortecle.WarnOverwriteAlias.message"), RB.getString("FPortecle.ChangeKeyStoreType.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { continue; } } // Put key and (possibly null) certificate chain in new keystore newKeyStore.setKeyEntry(sAlias, key, cPassword, certificateChain); // Update wrapper with password m_keyStoreWrap.setEntryPassword(sAlias, cPassword); } } // Successful change of type - put new keystore into wrapper m_keyStoreWrap.setKeyStore(newKeyStore); File oldFile = m_keyStoreWrap.getKeyStoreFile(); if (oldFile != null) { Set<String> oldExts = m_keyStoreWrap.getKeyStoreType().getFilenameExtensions(); Set<String> newExts = keyStoreType.getFilenameExtensions(); if (oldExts.isEmpty() || newExts.isEmpty()) { m_keyStoreWrap.setKeyStoreFile(null); } else { String newExt = newExts.iterator().next(); for (String oldExt : oldExts) { String path = oldFile.getPath().toLowerCase(); if (path.endsWith("." + oldExt)) { m_keyStoreWrap.setKeyStoreFile( new File(path.substring(0, path.length() - oldExt.length()) + newExt)); } } if (oldFile.equals(m_keyStoreWrap.getKeyStoreFile())) { m_keyStoreWrap.setKeyStoreFile(null); } } } m_keyStoreWrap.setChanged(true); // Update the frame's components and title updateControls(); updateTitle(); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.ChangeKeyStoreTypeSuccessful.message"), RB.getString("FPortecle.ChangeKeyStoreType.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user set the keystore's password. * * @return True if the password was set, false otherwise */ private boolean setKeyStorePassword() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; char[] cPassword = getNewKeyStorePassword(); // User canceled if (cPassword == null) { return false; } // Update the keystore wrapper m_keyStoreWrap.setPassword(cPassword); m_keyStoreWrap.setChanged(true); // Update the frame's components and title updateControls(); updateTitle(); return true; } /** * Let the user set the password for the selected key pair entry. * * @return True if the password is set, false otherwise */ private boolean setPasswordSelectedEntry() { assert m_keyStoreWrap.getKeyStore() != null; assert m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported(); // Not valid for a certificate entry, nor a key-only one - we do a remove-store operation but the // KeyStore API won't allow us to store a PrivateKey without associated certificate chain. // TODO: Maybe it'd work for other Key types? Need testing material. if (!KeyStoreTableModel.KEY_PAIR_ENTRY.equals(m_jtKeyStore.getSelectedType())) { return false; } // Get entry alias String sAlias = m_jtKeyStore.getSelectedAlias(); // Do we already know the current password for the entry? char[] cOldPassword = m_keyStoreWrap.getEntryPassword(sAlias); // Display the change password dialog supplying the current password to it if it was available DChangePassword dChangePassword = new DChangePassword(this, RB.getString("FPortecle.SetKeyPairPassword.Title"), cOldPassword); dChangePassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dChangePassword); // Get the password settings the user made in the dialog if (cOldPassword == null) { cOldPassword = dChangePassword.getOldPassword(); } char[] cNewPassword = dChangePassword.getNewPassword(); // Dialog was canceled if (cOldPassword == null || cNewPassword == null) { return false; } KeyStore keyStore = m_keyStoreWrap.getKeyStore(); try { // Change the password by recreating the entry Certificate[] cert = keyStore.getCertificateChain(sAlias); Key key = keyStore.getKey(sAlias, cOldPassword); keyStore.deleteEntry(sAlias); keyStore.setKeyEntry(sAlias, key, cNewPassword, cert); // Update the keystore wrapper m_keyStoreWrap.setEntryPassword(sAlias, cNewPassword); m_keyStoreWrap.setChanged(true); } catch (GeneralSecurityException ex) { DThrowable.showAndWait(this, null, ex); return false; } // Update the frame's components and title updateControls(); updateTitle(); return true; } /** * Let the user export the selected entry. * * @return True if the export is successful, false otherwise */ private boolean exportSelectedEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // TODO: implement this for key-only entries String selectedType = m_jtKeyStore.getSelectedType(); if (selectedType == null || selectedType.equals(KeyStoreTableModel.KEY_ENTRY)) { return false; } // Get the entry String sAlias = m_jtKeyStore.getSelectedAlias(); try { // Display the Generate Key Pair dialog to get the key pair generation parameters from the user DExport dExport = new DExport(this, m_keyStoreWrap, sAlias); dExport.setLocationRelativeTo(this); SwingHelper.showAndWait(dExport); if (!dExport.exportSelected()) { return false; // User canceled the dialog } // Do export boolean bSuccess = false; // Export head certificate only if (dExport.exportHead()) { // Export PEM encoded format if (dExport.exportPem()) { bSuccess = exportHeadCertOnlyPem(sAlias); } // Export DER encoded format else if (dExport.exportDer()) { bSuccess = exportHeadCertOnlyDER(sAlias); } // Export PkiPath format else if (dExport.exportPkiPath()) { bSuccess = exportHeadCertOnlyPkiPath(sAlias); } // Export PKCS #7 format else // if (dExport.exportPkcs7()) { bSuccess = exportHeadCertOnlyPkcs7(sAlias); } } // Complete certification path (PKCS #7 or PkiPath) else if (dExport.exportChain()) { if (dExport.exportPkiPath()) { bSuccess = exportAllCertsPkiPath(sAlias); } else // if (dExport.exportPkcs7()) { bSuccess = exportAllCertsPkcs7(sAlias); } } // Complete certification path and private key (PKCS #12) else { if (dExport.exportPem()) { bSuccess = exportPrivKeyCertChainPEM(sAlias); } else // if (dExport.exportPkcs12()) { bSuccess = exportPrivKeyCertChainPKCS12(sAlias); } } if (bSuccess) { // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.ExportSuccessful.message"), RB.getString("FPortecle.Export.Title"), JOptionPane.INFORMATION_MESSAGE); } } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } return true; } /** * Export the head certificate of the keystore entry in a PEM encoding. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportHeadCertOnlyPem(String sEntryAlias) { X509Certificate cert = null; try { cert = getHeadCert(sEntryAlias); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } String basename = X509CertUtil.getCertificateAlias(cert); if (basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export certificate file File fExportFile = chooseExportCertFile(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } try (JcaPEMWriter pw = new JcaPEMWriter(new FileWriter(fExportFile))) { pw.writeObject(cert); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Export the head certificate of the keystore entry in a DER encoding. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportHeadCertOnlyDER(String sEntryAlias) { X509Certificate cert = null; try { // Get the head certificate cert = getHeadCert(sEntryAlias); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } String basename = X509CertUtil.getCertificateAlias(cert); if (basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export certificate file File fExportFile = chooseExportCertFile(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Do the export byte[] bEncoded; try { bEncoded = X509CertUtil.getCertEncodedDer(cert); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } try (FileOutputStream fos = new FileOutputStream(fExportFile)) { fos.write(bEncoded); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Export the head certificate of the keystore entry to a PKCS #7 file. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportHeadCertOnlyPkcs7(String sEntryAlias) { X509Certificate cert = null; try { // Get the head certificate cert = getHeadCert(sEntryAlias); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } String basename = X509CertUtil.getCertificateAlias(cert); if (basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export PKCS #7 file File fExportFile = chooseExportPKCS7File(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Do the export byte[] bEncoded; try { bEncoded = X509CertUtil.getCertEncodedPkcs7(cert); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } try (FileOutputStream fos = new FileOutputStream(fExportFile)) { fos.write(bEncoded); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Export the head certificate of the keystore entry to a PkiPath file. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportHeadCertOnlyPkiPath(String sEntryAlias) { X509Certificate cert = null; try { // Get the head certificate cert = getHeadCert(sEntryAlias); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } String basename = X509CertUtil.getCertificateAlias(cert); if (basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export PkiPath file File fExportFile = chooseExportPkiPathFile(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Do the export byte[] bEncoded; try { bEncoded = X509CertUtil.getCertEncodedPkiPath(cert); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } try (FileOutputStream fos = new FileOutputStream(fExportFile)) { fos.write(bEncoded); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Export all of the certificates of the keystore entry to a PKCS #7 file. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportAllCertsPkcs7(String sEntryAlias) { // Get the certificates KeyStore keyStore = m_keyStoreWrap.getKeyStore(); X509Certificate[] certChain = null; try { certChain = X509CertUtil.convertCertificates(keyStore.getCertificateChain(sEntryAlias)); } catch (CryptoException | KeyStoreException ex) { DThrowable.showAndWait(this, null, ex); return false; } String basename = null; if (certChain.length > 0) { basename = X509CertUtil.getCertificateAlias(certChain[0]); } if (basename == null || basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export PKCS #7 file File fExportFile = chooseExportPKCS7File(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Do the export byte[] bEncoded; try { bEncoded = X509CertUtil.getCertsEncodedPkcs7(certChain); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } try (FileOutputStream fos = new FileOutputStream(fExportFile)) { fos.write(bEncoded); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Export all of the certificates of the keystore entry to a PkiPath file. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportAllCertsPkiPath(String sEntryAlias) { // Get the certificates KeyStore keyStore = m_keyStoreWrap.getKeyStore(); X509Certificate[] certChain = null; try { certChain = X509CertUtil.convertCertificates(keyStore.getCertificateChain(sEntryAlias)); } catch (CryptoException | KeyStoreException ex) { DThrowable.showAndWait(this, null, ex); return false; } String basename = null; if (certChain.length > 0) { basename = X509CertUtil.getCertificateAlias(certChain[0]); } if (basename == null || basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export PkiPath file File fExportFile = chooseExportPkiPathFile(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Do the export byte[] bEncoded; try { bEncoded = X509CertUtil.getCertsEncodedPkiPath(certChain); } catch (CryptoException ex) { DThrowable.showAndWait(this, null, ex); return false; } try (FileOutputStream fos = new FileOutputStream(fExportFile)) { fos.write(bEncoded); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Get the keystore entry's head certificate. * * @param sEntryAlias Entry alias * @return The keystore entry's head certificate * @throws CryptoException Problem getting head certificate */ private X509Certificate getHeadCert(String sEntryAlias) throws CryptoException { try { // Get keystore KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Get the entry's head certificate X509Certificate cert; if (keyStore.isKeyEntry(sEntryAlias)) { cert = X509CertUtil.orderX509CertChain( X509CertUtil.convertCertificates(keyStore.getCertificateChain(sEntryAlias)))[0]; } else { cert = X509CertUtil.convertCertificate(keyStore.getCertificate(sEntryAlias)); } return cert; } catch (KeyStoreException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoAccessEntry.message"), sEntryAlias); throw new CryptoException(sMessage, ex); } } /** * Export the private key and certificates of the keystore entry to a PEM encoded "OpenSSL" format bundle. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportPrivKeyCertChainPEM(String sEntryAlias) { KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sEntryAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } File fExportFile = null; try { // Get the private key and certificate chain from the entry Key privKey = keyStore.getKey(sEntryAlias, cPassword); Certificate[] certs = keyStore.getCertificateChain(sEntryAlias); // Get a new password to encrypt the private key with DGetNewPassword dGetNewPassword = new DGetNewPassword(this, RB.getString("FPortecle.PrivateKeyExportPassword.Title")); dGetNewPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetNewPassword); char[] password = dGetNewPassword.getPassword(); if (password == null) { return false; } String basename = null; if (certs.length > 0 && certs[0] instanceof X509Certificate) { basename = X509CertUtil.getCertificateAlias((X509Certificate) certs[0]); } if (basename == null || basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the PEM export file fExportFile = chooseExportPEMFile(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Do the export try (JcaPEMWriter pw = new JcaPEMWriter(new FileWriter(fExportFile))) { if (password.length == 0) { pw.writeObject(privKey); } else { // TODO: make algorithm configurable/ask user? String algorithm = "DES-EDE3-CBC"; SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); PEMEncryptor encryptor = new JcePEMEncryptorBuilder(algorithm).setSecureRandom(rand) .build(password); pw.writeObject(privKey, encryptor); } for (Certificate cert : certs) { pw.writeObject(cert); } } m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (GeneralSecurityException | IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Export the private key and certificates of the keystore entry to a PKCS #12 keystore file. * * @param sEntryAlias Entry alias * @return True if the export is successful, false otherwise */ private boolean exportPrivKeyCertChainPKCS12(String sEntryAlias) { KeyStore keyStore = m_keyStoreWrap.getKeyStore(); // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sEntryAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } File fExportFile = null; try { // Get the private key and certificate chain from the entry Key privKey = keyStore.getKey(sEntryAlias, cPassword); Certificate[] certs = keyStore.getCertificateChain(sEntryAlias); // Update the keystore wrapper m_keyStoreWrap.setEntryPassword(sEntryAlias, cPassword); // Create a new PKCS #12 keystore KeyStore pkcs12 = KeyStoreUtil.createKeyStore(KeyStoreType.PKCS12); // Place the private key and certificate chain into the PKCS #12 keystore under the same alias as // it has in the loaded keystore pkcs12.setKeyEntry(sEntryAlias, privKey, new char[0], certs); // Get a new password for the PKCS #12 keystore DGetNewPassword dGetNewPassword = new DGetNewPassword(this, RB.getString("FPortecle.Pkcs12Password.Title")); dGetNewPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetNewPassword); char[] cPKCS12Password = dGetNewPassword.getPassword(); if (cPKCS12Password == null) { return false; } String basename = null; if (certs.length > 0 && certs[0] instanceof X509Certificate) { basename = X509CertUtil.getCertificateAlias((X509Certificate) certs[0]); } if (basename == null || basename.isEmpty()) { basename = sEntryAlias; } // Let the user choose the export PKCS #12 file fExportFile = chooseExportPKCS12File(basename); if (fExportFile == null) { return false; } if (!confirmOverwrite(fExportFile, getTitle())) { return false; } // Store the keystore to disk KeyStoreUtil.saveKeyStore(pkcs12, fExportFile, cPKCS12Password); m_lastDir.updateLastDir(fExportFile); return true; } catch (FileNotFoundException ex) { String sMessage = MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fExportFile.getName()); JOptionPane.showMessageDialog(this, sMessage, getTitle(), JOptionPane.WARNING_MESSAGE); return false; } catch (CryptoException | GeneralSecurityException | IOException ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user choose a certificate file to export to. * * @param basename default filename (without extension) * @return The chosen file or null if none was chosen */ private File chooseExportCertFile(String basename) { JFileChooser chooser = FileChooserFactory.getX509FileChooser(basename); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExportCertificate.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.Export.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a PKCS #7 file to export to. * * @param basename default filename (without extension) * @return The chosen file or null if none was chosen */ private File chooseExportPKCS7File(String basename) { JFileChooser chooser = FileChooserFactory.getPkcs7FileChooser(basename); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExportCertificates.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.Export.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a PkiPath file to export to. * * @param basename default filename (without extension) * @return The chosen file or null if none was chosen */ private File chooseExportPkiPathFile(String basename) { JFileChooser chooser = FileChooserFactory.getPkiPathFileChooser(basename); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExportCertificates.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.Export.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a PKCS #12 file to export to. * * @param basename default filename (without extension) * @return The chosen file or null if none was chosen */ private File chooseExportPKCS12File(String basename) { JFileChooser chooser = FileChooserFactory.getPkcs12FileChooser(basename); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExportKeyCertificates.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.Export.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user choose a PEM file to export to. * * @param basename default filename (without extension) * @return The chosen file or null if none was chosen */ private File chooseExportPEMFile(String basename) { JFileChooser chooser = FileChooserFactory.getPEMFileChooser(basename); File fLastDir = m_lastDir.getLastDir(); if (fLastDir != null) { chooser.setCurrentDirectory(fLastDir); } chooser.setDialogTitle(RB.getString("FPortecle.ExportKeyCertificates.Title")); chooser.setMultiSelectionEnabled(false); int iRtnValue = chooser.showDialog(this, RB.getString("FPortecle.Export.button")); if (iRtnValue == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Let the user generate a CSR for the selected key pair entry. * * @return True if the generation is successful, false otherwise */ private boolean generateCsrSelectedEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Not valid for a key-only or a trusted certificate entry if (!KeyStoreTableModel.KEY_PAIR_ENTRY.equals(m_jtKeyStore.getSelectedType())) { return false; } String sAlias = m_jtKeyStore.getSelectedAlias(); KeyStore keyStore = m_keyStoreWrap.getKeyStore(); File fCsrFile = null; try { // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } // Get the key pair entry's private key using the password PrivateKey privKey = (PrivateKey) keyStore.getKey(sAlias, cPassword); // Update the keystore wrapper m_keyStoreWrap.setEntryPassword(sAlias, cPassword); // Get the first certificate in the entry's certificate chain X509Certificate cert = X509CertUtil .orderX509CertChain(X509CertUtil.convertCertificates(keyStore.getCertificateChain(sAlias)))[0]; // Let the user choose the file to write the CSR to fCsrFile = chooseGenerateCsrFile(X509CertUtil.getCertificateAlias(cert)); if (fCsrFile == null) { return false; } if (!confirmOverwrite(fCsrFile, RB.getString("FPortecle.GenerateCsr.Title"))) { return false; } // Generate CSR and write it out to file try (JcaPEMWriter pw = new JcaPEMWriter(new FileWriter(fCsrFile))) { pw.writeObject(X509CertUtil.generatePKCS10CSR(cert, privKey)); } // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.CsrGenerationSuccessful.message"), RB.getString("FPortecle.GenerateCsr.Title"), JOptionPane.INFORMATION_MESSAGE); m_lastDir.updateLastDir(fCsrFile); return true; } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.NoWriteFile.message"), fCsrFile), RB.getString("FPortecle.GenerateCsr.Title"), JOptionPane.WARNING_MESSAGE); return false; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user clone the selected key entry. * * @return True if the clone is successful, false otherwise */ private boolean cloneSelectedKeyEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Not valid for a PrivateKey-only entry - the KeyStore API won't allow us to store a PrivateKey // without associated certificate chain. // TODO: Maybe it'd work for other Key types? Need testing material. if (!KeyStoreTableModel.KEY_PAIR_ENTRY.equals(m_jtKeyStore.getSelectedType())) { return false; } String sAlias = m_jtKeyStore.getSelectedAlias(); KeyStore keyStore = m_keyStoreWrap.getKeyStore(); KeyStoreType ksType = m_keyStoreWrap.getKeyStoreType(); try { // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(sAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (ksType.isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } // Update the keystore wrapper m_keyStoreWrap.setEntryPassword(sAlias, cPassword); sAlias = getNewEntryAlias(keyStore, sAlias, "FPortecle.ClonedKeyPairEntryAlias.Title", true); if (sAlias == null) { return false; } // Get key and certificates from entry Key key = keyStore.getKey(sAlias, cPassword); Certificate[] certs = keyStore.getCertificateChain(sAlias); // Get a password for the new keystore entry if applicable char[] cNewPassword = KeyStoreUtil.DUMMY_PASSWORD; if (ksType.isEntryPasswordSupported()) { DGetNewPassword dGetNewPassword = new DGetNewPassword(this, RB.getString("FPortecle.ClonedKeyPairEntryPassword.Title")); dGetNewPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetNewPassword); cNewPassword = dGetNewPassword.getPassword(); if (cNewPassword == null) { return false; } } // Delete old entry first if (keyStore.containsAlias(sAlias)) { keyStore.deleteEntry(sAlias); } // Create new entry keyStore.setKeyEntry(sAlias, key, cNewPassword, certs); // Update the keystore wrapper m_keyStoreWrap.setEntryPassword(sAlias, cNewPassword); m_keyStoreWrap.setChanged(true); // ...and update the frame's components and title selectedAlias = sAlias; updateControls(); updateTitle(); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.KeyPairCloningSuccessful.message"), RB.getString("FPortecle.CloneKeyPair.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user clone the selected certificate entry. * * @return True if the clone is successful, false otherwise */ private boolean cloneSelectedCertificateEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // Not valid for non-certificate entries if (!KeyStoreTableModel.TRUST_CERT_ENTRY.equals(m_jtKeyStore.getSelectedType())) { return false; } String sAlias = m_jtKeyStore.getSelectedAlias(); KeyStore keyStore = m_keyStoreWrap.getKeyStore(); try { // Get the alias of the new entry sAlias = getNewEntryAlias(keyStore, sAlias, "FPortecle.ClonedTrustCertEntryAlias.Title", true); if (sAlias == null) { return false; } // Get certificate from entry Certificate cert = keyStore.getCertificate(sAlias); // Delete old entry first if (keyStore.containsAlias(sAlias)) { keyStore.deleteEntry(sAlias); } // Create new entry keyStore.setCertificateEntry(sAlias, cert); // Update the keystore wrapper m_keyStoreWrap.setChanged(true); // ...and update the frame's components and title selectedAlias = sAlias; updateControls(); updateTitle(); // Display success message JOptionPane.showMessageDialog(this, RB.getString("FPortecle.CertificateCloningSuccessful.message"), RB.getString("FPortecle.CloneCertificate.Title"), JOptionPane.INFORMATION_MESSAGE); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Display a report on the currently loaded keystore. * * @return True if the keystore report was displayed successfully, false otherwise */ private boolean keyStoreReport() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; try { DKeyStoreReport dKeyStoreReport = new DKeyStoreReport(this, m_keyStoreWrap.getKeyStore()); dKeyStoreReport.setLocationRelativeTo(this); SwingHelper.showAndWait(dKeyStoreReport); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user see the certificate details of the selected keystore entry. * * @return True if the certificate details were viewed successfully, false otherwise */ private boolean showSelectedEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // TODO: implement this for key-only entries String selectedType = m_jtKeyStore.getSelectedType(); if (selectedType == null || selectedType.equals(KeyStoreTableModel.KEY_ENTRY)) { return false; } String sAlias = m_jtKeyStore.getSelectedAlias(); KeyStore keyStore = m_keyStoreWrap.getKeyStore(); try { // Get the entry's certificates X509Certificate[] certs; if (keyStore.isKeyEntry(sAlias)) { // If entry is a key pair certs = X509CertUtil.convertCertificates(keyStore.getCertificateChain(sAlias)); } else { // If entry is a trusted certificate certs = new X509Certificate[1]; certs[0] = X509CertUtil.convertCertificate(keyStore.getCertificate(sAlias)); } // Supply the certificates to the view certificate dialog DViewCertificate dViewCertificate = new DViewCertificate(this, MessageFormat.format(RB.getString("FPortecle.CertDetailsEntry.Title"), sAlias), certs); dViewCertificate.setLocationRelativeTo(this); SwingHelper.showAndWait(dViewCertificate); return true; } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } } /** * Let the user delete the selected keystore entry. * * @return True if the deletion is successful, false otherwise */ private boolean deleteSelectedEntry() { String sAlias = m_jtKeyStore.getSelectedAlias(); if (sAlias == null) { return false; } int iSelected = JOptionPane.showConfirmDialog(this, MessageFormat.format(RB.getString("FPortecle.DeleteEntry.message"), sAlias), RB.getString("FPortecle.DeleteEntry.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { return false; } assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; KeyStore keyStore = m_keyStoreWrap.getKeyStore(); try { // Delete the entry keyStore.deleteEntry(sAlias); // Update the keystore wrapper m_keyStoreWrap.removeEntryPassword(sAlias); m_keyStoreWrap.setChanged(true); } catch (KeyStoreException ex) { DThrowable.showAndWait(this, null, ex); return false; } // Update the frame's components and title selectedAlias = null; updateControls(); updateTitle(); return true; } /** * Let the user rename the selected keystore entry. * * @return True if the rename is successful, false otherwise */ private boolean renameSelectedEntry() { assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; // What entry has been selected? int iRow = m_jtKeyStore.getSelectedRow(); if (!m_jtKeyStore.getModel().isCellEditable(iRow, 1)) { return false; } String sAlias = m_jtKeyStore.getSelectedAlias(); // Get the new entry alias DGetAlias dGetAlias = new DGetAlias(this, RB.getString("FPortecle.NewEntryAlias.Title"), sAlias, true); dGetAlias.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetAlias); return renameEntry(sAlias, dGetAlias.getAlias(), false); } /** * Let the user rename the selected keystore entry. * * @param silent if true, attempt to rename to same name will be ignored without popping up an error * dialog * @return True if the rename is successful, false otherwise */ /* package private */boolean renameEntry(String oldAlias, String newAlias, boolean silent) { if (newAlias == null) { return false; } assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; KeyStore keyStore = m_keyStoreWrap.getKeyStore(); try { // Check new alias differs from the present one if (newAlias.equalsIgnoreCase(oldAlias)) { if (!silent) { JOptionPane.showMessageDialog(this, MessageFormat.format(RB.getString("FPortecle.RenameAliasIdentical.message"), oldAlias), RB.getString("FPortecle.RenameEntry.Title"), JOptionPane.ERROR_MESSAGE); } return false; } // Check entry does not already exist in the keystore if (keyStore.containsAlias(newAlias)) { String sMessage = MessageFormat.format(RB.getString("FPortecle.OverWriteEntry.message"), newAlias); int iSelected = JOptionPane.showConfirmDialog(this, sMessage, RB.getString("FPortecle.RenameEntry.Title"), JOptionPane.YES_NO_OPTION); if (iSelected != JOptionPane.YES_OPTION) { return false; } } // Create the new entry with the new name and copy the old entry across // If the entry is a key pair... if (keyStore.isKeyEntry(oldAlias)) { // Get the entry's password (we may already know it from the wrapper) char[] cPassword = m_keyStoreWrap.getEntryPassword(oldAlias); if (cPassword == null) { cPassword = KeyStoreUtil.DUMMY_PASSWORD; if (m_keyStoreWrap.getKeyStoreType().isEntryPasswordSupported()) { DGetPassword dGetPassword = new DGetPassword(this, RB.getString("FPortecle.KeyEntryPassword.Title")); dGetPassword.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetPassword); cPassword = dGetPassword.getPassword(); if (cPassword == null) { return false; } } } // Do the copy Key key = keyStore.getKey(oldAlias, cPassword); Certificate[] certs = keyStore.getCertificateChain(oldAlias); keyStore.setKeyEntry(newAlias, key, cPassword, certs); // Update the keystore wrapper m_keyStoreWrap.setEntryPassword(newAlias, cPassword); } // ...if the entry is a trusted certificate else { // Do the copy Certificate cert = keyStore.getCertificate(oldAlias); keyStore.setCertificateEntry(newAlias, cert); } // Delete the old entry keyStore.deleteEntry(oldAlias); // Update the keystore wrapper m_keyStoreWrap.removeEntryPassword(oldAlias); m_keyStoreWrap.setChanged(true); } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); return false; } // Update the frame's components and title selectedAlias = newAlias; updateControls(); updateTitle(); return true; } /** * Update the application's controls dependent on the state of its keystore (e.g. if changes to keystore * are saved disable save tool bar button). */ private void updateControls() { // keystore must have been loaded assert m_keyStoreWrap != null; assert m_keyStoreWrap.getKeyStore() != null; m_saveKeyStoreAction.setEnabled(m_keyStoreWrap.isChanged() || m_keyStoreWrap.getKeyStoreFile() == null); m_jmiSaveKeyStoreAs.setEnabled(true); m_genKeyPairAction.setEnabled(true); m_importTrustCertAction.setEnabled(true); m_importKeyPairAction.setEnabled(true); m_setKeyStorePassAction.setEnabled(true); m_keyStoreReportAction.setEnabled(true); // Show default status bar display setDefaultStatusBarText(); // Get keystore KeyStore keyStore = m_keyStoreWrap.getKeyStore(); KeyStoreType ksType = m_keyStoreWrap.getKeyStoreType(); try { // Update keystore entries table ((KeyStoreTableModel) m_jtKeyStore.getModel()).load(keyStore); } catch (KeyStoreException ex) { DThrowable.showAndWait(this, null, ex); } // Enable entry password changing only for applicable keystore types m_jmiSetKeyPairPass.setEnabled(ksType.isEntryPasswordSupported()); // Change keystore type menu items dependent on keystore type // Enable change keystore type menu m_jmChangeKeyStoreType.setEnabled(true); // Initially enable the menu items for all available types m_jmiChangeKeyStoreTypeJks.setEnabled(KeyStoreUtil.isAvailable(KeyStoreType.JKS)); m_jmiChangeKeyStoreTypeCaseExactJks.setEnabled(KeyStoreUtil.isAvailable(KeyStoreType.CaseExactJKS)); m_jmiChangeKeyStoreTypeJceks.setEnabled(KeyStoreUtil.isAvailable(KeyStoreType.JCEKS)); m_jmiChangeKeyStoreTypePkcs12.setEnabled(true); m_jmiChangeKeyStoreTypeBks.setEnabled(true); m_jmiChangeKeyStoreTypeBksV1.setEnabled(true); m_jmiChangeKeyStoreTypeUber.setEnabled(true); m_jmiChangeKeyStoreTypeGkr.setEnabled(KeyStoreUtil.isAvailable(KeyStoreType.GKR)); // Disable the menu item matching current keystore type switch (ksType) { case JKS: m_jmiChangeKeyStoreTypeJks.setEnabled(false); break; case CaseExactJKS: m_jmiChangeKeyStoreTypeCaseExactJks.setEnabled(false); break; case JCEKS: m_jmiChangeKeyStoreTypeJceks.setEnabled(false); break; case PKCS12: m_jmiChangeKeyStoreTypePkcs12.setEnabled(false); break; case BKS: m_jmiChangeKeyStoreTypeBks.setEnabled(false); break; case BKS_V1: m_jmiChangeKeyStoreTypeBksV1.setEnabled(false); break; case UBER: m_jmiChangeKeyStoreTypeUber.setEnabled(false); break; case GKR: m_jmiChangeKeyStoreTypeGkr.setEnabled(false); break; default: // Nothing } m_jtKeyStore.clearSelection(); if (selectedAlias != null) { for (int i = 0, len = m_jtKeyStore.getRowCount(); i < len; i++) { if (selectedAlias.equals(m_jtKeyStore.getValueAt(i, 1))) { m_jtKeyStore.setRowSelectionInterval(i, i); break; } } } } /** * Update the application's controls dependent on the state of its keystore. */ private void updateTitle() { // Application name String sAppName = RB.getString("FPortecle.Title"); // No keystore loaded so just display the application name if (m_keyStoreWrap == null) { setTitle(sAppName); } else { File fKeyStore = m_keyStoreWrap.getKeyStoreFile(); if (fKeyStore == null) { // A newly created keystore is loaded - display Untitled string and application name setTitle(MessageFormat.format("[{0}] - {1}", RB.getString("FPortecle.Untitled"), sAppName)); } else { // Keystore loaded - display keystore file path, "modified" indicator, and application name String modInd = m_keyStoreWrap.isChanged() ? RB.getString("FPortecle.Modified") : ""; setTitle(MessageFormat.format("{0}{1} - {2}", fKeyStore, modInd, sAppName)); } } } /** * Display the supplied text in the status bar. * * @param sStatus Text to display */ @Override public void setStatusBarText(String sStatus) { m_jlStatusBar.setText(sStatus); } /** * Set the text in the staus bar to reflect the status of the currently loaded keystore. */ @Override public void setDefaultStatusBarText() { // No keystore loaded... if (m_keyStoreWrap == null) { setStatusBarText(RB.getString("FPortecle.noKeyStore.statusbar")); } // keystore loaded... else { // Get the keystore and display information on its type and size KeyStore ksLoaded = m_keyStoreWrap.getKeyStore(); int iSize; try { iSize = ksLoaded.size(); } catch (KeyStoreException ex) { setStatusBarText(""); DThrowable.showAndWait(this, null, ex); return; } String sType = KeyStoreType.valueOfType(ksLoaded.getType()).toString(); String sProv = ksLoaded.getProvider().getName(); if (iSize == 1) { setStatusBarText(MessageFormat.format(RB.getString("FPortecle.entry.statusbar"), sType, sProv)); } else { setStatusBarText( MessageFormat.format(RB.getString("FPortecle.entries.statusbar"), sType, sProv, iSize)); } } } /** * Save the application preferences. */ private void saveAppPrefs() { try { // The size of the keystore table panel - determines the size of the main frame PREFS.putInt(RB.getString("AppPrefs.TableWidth"), m_jpKeyStoreTable.getWidth()); PREFS.putInt(RB.getString("AppPrefs.TableHeight"), m_jpKeyStoreTable.getHeight()); // The size of the keystore table's alias column - determines the size of all of the table's // columns PREFS.putInt(RB.getString("AppPrefs.AliasWidth"), m_jtKeyStore.getColumnModel().getColumn(1).getWidth()); // Application's position on the desktop PREFS.putInt(RB.getString("AppPrefs.XPos"), this.getX()); PREFS.putInt(RB.getString("AppPrefs.YPos"), this.getY()); // Use CA certificates file? PREFS.putBoolean(RB.getString("AppPrefs.UseCaCerts"), m_bUseCaCerts); // CA Certificates file PREFS.put(RB.getString("AppPrefs.CaCertsFile"), m_fCaCertsFile.toString()); // Recent files File[] fRecentFiles = m_jmrfFile.getRecentFiles(); for (int iCnt = 0; iCnt < fRecentFiles.length; iCnt++) { PREFS.put(RB.getString("AppPrefs.RecentFile") + (iCnt + 1), fRecentFiles[iCnt].toString()); } // Look & feel LookAndFeel currentLookAndFeel = UIManager.getLookAndFeel(); if (lookFeelClassName != null) { // Setting made in options PREFS.put(RB.getString("AppPrefs.LookFeel"), lookFeelClassName); } else { // Current setting if (currentLookAndFeel != null) { UIManager.LookAndFeelInfo[] lookFeelInfos = UIManager.getInstalledLookAndFeels(); for (UIManager.LookAndFeelInfo lookFeelInfo : lookFeelInfos) { // Store current look & feel class name if (currentLookAndFeel.getName().equals(lookFeelInfo.getName())) { PREFS.put(RB.getString("AppPrefs.LookFeel"), lookFeelInfo.getClassName()); break; } } } } // Use Look & Feel's decoration? if (m_bLookFeelDecorationOptions != null) { // Setting made in options PREFS.putBoolean(RB.getString("AppPrefs.LookFeelDecor"), m_bLookFeelDecorationOptions); } else { // Current setting PREFS.putBoolean(RB.getString("AppPrefs.LookFeelDecor"), JFrame.isDefaultLookAndFeelDecorated()); } PREFS.sync(); } catch (Exception ex) { DThrowable.showAndWait(this, null, ex); } } /** * Exit the application. */ private void exitApplication() { // Does the current keystore contain unsaved changes? if (needSave()) { // Yes - ask the user if it should be saved switch (wantSave()) { case JOptionPane.YES_OPTION: // Save it saveKeyStore(); break; case JOptionPane.CANCEL_OPTION: return; } } // Save application preferences saveAppPrefs(); System.exit(0); } /** * Initialize the application's look and feel. */ private static void initLookAndFeel() { try { // Use the look and feel UIManager.setLookAndFeel(PREFS.get(RB.getString("AppPrefs.LookFeel"), FPortecle.DEFAULT_LOOK_FEEL)); } // Didn't work - no matter catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) { // Ignored } // Use look & feel's decoration? boolean bLookFeelDecorated = PREFS.getBoolean(RB.getString("AppPrefs.LookFeelDecor"), false); JFrame.setDefaultLookAndFeelDecorated(bLookFeelDecorated); JDialog.setDefaultLookAndFeelDecorated(bLookFeelDecorated); } /** * Set cursor to busy and disable application input. This can be reversed by a subsequent call to * setCursorFree. */ private void setCursorBusy() { // Block all mouse events using glass pane Component glassPane = getRootPane().getGlassPane(); glassPane.addMouseListener(new MouseAdapter() { // Nothing }); glassPane.setVisible(true); // Set cursor to busy glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); } /** * Set cursor to free and enable application input. Called after a call to setCursorBusy. */ private void setCursorFree() { // Accept mouse events Component glassPane = getRootPane().getGlassPane(); glassPane.setVisible(false); // Revert cursor to default glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } /** * Gets a resource image. * * @param key the image's key * @return the Image corresponding to the key */ private Image getResImage(String key) { return Toolkit.getDefaultToolkit().createImage(FPortecle.class.getResource(RB.getString(key))); } /** * File overwrite confirmation dialog. * * @param file the file possibly being overwritten * @param title window title * @return true if the write operation should continue */ private boolean confirmOverwrite(File file, String title) { if (file.isFile()) { String sMessage = MessageFormat.format(RB.getString("FPortecle.OverWriteFile.message"), file.getName()); int iSelected = JOptionPane.showConfirmDialog(this, sMessage, title, JOptionPane.YES_NO_OPTION); return iSelected == JOptionPane.YES_OPTION; } return true; } /** * Gets a new entry alias from user, handling overwrite issues. * * @param keyStore target keystore * @param sAlias suggested alias * @param dialogTitleKey message key for dialog titles * @param selectAlias whether to pre-select alias text in text field * @return alias for new entry, null if user cancels the operation */ private String getNewEntryAlias(KeyStore keyStore, String sAlias, String dialogTitleKey, boolean selectAlias) throws KeyStoreException { while (true) { // Get the alias for the new entry DGetAlias dGetAlias = new DGetAlias(this, RB.getString(dialogTitleKey), sAlias.toLowerCase(), selectAlias); dGetAlias.setLocationRelativeTo(this); SwingHelper.showAndWait(dGetAlias); sAlias = dGetAlias.getAlias(); if (sAlias == null) { return null; } // Check an entry with the selected does not already exist in the keystore if (!keyStore.containsAlias(sAlias)) { return sAlias; } String sMessage = MessageFormat.format(RB.getString("FPortecle.OverWriteEntry.message"), sAlias); int iSelected = JOptionPane.showConfirmDialog(this, sMessage, RB.getString(dialogTitleKey), JOptionPane.YES_NO_CANCEL_OPTION); switch (iSelected) { case JOptionPane.YES_OPTION: return sAlias; case JOptionPane.NO_OPTION: // keep looping break; default: return null; } } } /** * Action to create a new keystore. */ private class NewKeyStoreAction extends AbstractAction { /** * Construct action. */ public NewKeyStoreAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.NewKeyStoreAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.NewKeyStoreAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.NewKeyStoreAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.NewKeyStoreAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.NewKeyStoreAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.NewKeyStoreAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { newKeyStore(); } } /** * Action to save a keystore. */ private class SaveKeyStoreAction extends AbstractAction { /** * Construct action. */ public SaveKeyStoreAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.SaveKeyStoreAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.SaveKeyStoreAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.SaveKeyStoreAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.SaveKeyStoreAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.SaveKeyStoreAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.SaveKeyStoreAction.image"))); setEnabled(false); } /** * Perform action. */ @Override public void act() { saveKeyStore(); } } /** * Action to open a keystore file. */ private class OpenKeyStoreFileAction extends AbstractAction { /** * Construct action. */ public OpenKeyStoreFileAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.OpenKeyStoreFileAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.OpenKeyStoreFileAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.OpenKeyStoreFileAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.OpenKeyStoreFileAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.OpenKeyStoreFileAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.OpenKeyStoreFileAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { openKeyStoreFile(); } } /** * Action to open a keystore file. */ private class OpenCaCertsKeyStoreAction extends AbstractAction { /** * Construct action. */ public OpenCaCertsKeyStoreAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.OpenCaCertsKeyStoreAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.OpenCaCertsKeyStoreAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.OpenCaCertsKeyStoreAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.OpenCaCertsKeyStoreAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.OpenCaCertsKeyStoreAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.OpenCaCertsKeyStoreAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { openCaCertsKeyStoreFile(); } } /** * Action to generate a key pair. */ private class GenKeyPairAction extends AbstractAction { /** * Construct action. */ public GenKeyPairAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.GenKeyPairAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.GenKeyPairAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.GenKeyPairAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.GenKeyPairAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.GenKeyPairAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.GenKeyPairAction.image"))); setEnabled(false); } /** * Perform action. */ @Override public void act() { generateKeyPair(); } } /** * Action to import a trusted certificate. */ private class ImportTrustCertAction extends AbstractAction { /** * Construct action. */ public ImportTrustCertAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.ImportTrustCertAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.ImportTrustCertAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.ImportTrustCertAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.ImportTrustCertAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.ImportTrustCertAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.ImportTrustCertAction.image"))); setEnabled(false); } /** * Perform action. */ @Override public void act() { importTrustedCert(); } } /** * Action to import a key pair. */ private class ImportKeyPairAction extends AbstractAction { /** * Construct action. */ public ImportKeyPairAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.ImportKeyPairAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.ImportKeyPairAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.ImportKeyPairAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.ImportKeyPairAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.ImportKeyPairAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.ImportKeyPairAction.image"))); setEnabled(false); } /** * Perform action. */ @Override public void act() { importKeyPair(); } } /** * Action to set a keystore password. */ private class SetKeyStorePassAction extends AbstractAction { /** * Construct action. */ public SetKeyStorePassAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.SetKeyStorePassAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.SetKeyStorePassAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.SetKeyStorePassAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.SetKeyStorePassAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.SetKeyStorePassAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.SetKeyStorePassAction.image"))); setEnabled(false); } /** * Perform action. */ @Override public void act() { setKeyStorePassword(); } } /** * Action to show a keystore report. */ private class KeyStoreReportAction extends AbstractAction { /** * Construct action. */ public KeyStoreReportAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.KeyStoreReportAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.KeyStoreReportAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.KeyStoreReportAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.KeyStoreReportAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.KeyStoreReportAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.KeyStoreReportAction.image"))); setEnabled(false); } /** * Perform action. */ @Override public void act() { keyStoreReport(); } } /** * Action to examine a certificate. */ private class ExamineCertAction extends AbstractAction { /** * Construct action. */ public ExamineCertAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.ExamineCertAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.ExamineCertAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.ExamineCertAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.ExamineCertAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.ExamineCertAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.ExamineCertAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { examineCert(null); } } /** * Action to examine a SSL/TLS connection. */ private class ExamineCertSSLAction extends AbstractAction { /** * Construct action. */ public ExamineCertSSLAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.ExamineCertSSLAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.ExamineCertSSLAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.ExamineCertSSLAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.ExamineCertSSLAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.ExamineCertSSLAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.ExamineCertSSLAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { examineCertSSL(null); } } /** * Action to examine a CSR. */ private class ExamineCsrAction extends AbstractAction { /** * Construct action. */ public ExamineCsrAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.ExamineCsrAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.ExamineCsrAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.ExamineCsrAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.ExamineCsrAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.ExamineCsrAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.ExamineCsrAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { examineCSR(null); } } /** * Action to examine a CRL. */ private class ExamineCrlAction extends AbstractAction { /** * Construct action. */ public ExamineCrlAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( RB.getString("FPortecle.ExamineCrlAction.accelerator").charAt(0), InputEvent.CTRL_MASK)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.ExamineCrlAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.ExamineCrlAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.ExamineCrlAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.ExamineCrlAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.ExamineCrlAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { examineCRL(null); } } /** * Action to show help. */ private class HelpAction extends AbstractAction { /** * Construct action. */ public HelpAction() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0)); putValue(LONG_DESCRIPTION, RB.getString("FPortecle.HelpAction.statusbar")); putValue(MNEMONIC_KEY, Integer.valueOf(RB.getString("FPortecle.HelpAction.mnemonic").charAt(0))); putValue(NAME, RB.getString("FPortecle.HelpAction.text")); putValue(SHORT_DESCRIPTION, RB.getString("FPortecle.HelpAction.tooltip")); putValue(SMALL_ICON, new ImageIcon(getResImage("FPortecle.HelpAction.image"))); setEnabled(true); } /** * Perform action. */ @Override public void act() { showHelp(); } } /** * Action helper class. */ private abstract class AbstractAction extends javax.swing.AbstractAction { protected abstract void act(); @Override public void actionPerformed(ActionEvent evt) { setDefaultStatusBarText(); setCursorBusy(); repaint(); try { act(); } finally { setCursorFree(); } } } /** * ActionListener helper class. */ private abstract class ActionListener implements java.awt.event.ActionListener { protected abstract void act(); @Override public void actionPerformed(ActionEvent evt) { setDefaultStatusBarText(); setCursorBusy(); repaint(); try { act(); } finally { setCursorFree(); } } } private class KeyStoreTable extends JTable { private KeyStoreTable(KeyStoreTableModel model) { super(model); getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // Install listener to keep track of last selected alias getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { String alias = getSelectedAlias(); if (alias != null) { selectedAlias = alias; } } } }); setTransferHandler(new FileTransferHander()); } private String getSelectedType() { int selectedRow = getSelectedRow(); return (selectedRow >= 0) ? (String) getValueAt(selectedRow, 0) : null; } private String getSelectedAlias() { int selectedRow = getSelectedRow(); return (selectedRow >= 0) ? (String) getValueAt(selectedRow, 1) : null; } private class FileTransferHander extends SingleFileDropHelper { @Override public boolean canImport(TransferSupport support) { if (super.canImport(support)) { m_lastDir.updateLastDir(file); return true; } return false; } @Override public boolean importData(JComponent comp, Transferable t) { if (file != null) { // Will refactor this later, taken from openKeystore(); // Does the current keystore contain unsaved changes? if (needSave()) { // Yes - ask the user if it should be saved int iWantSave = wantSave(); if ((iWantSave == JOptionPane.YES_OPTION && !saveKeyStore()) || iWantSave == JOptionPane.CANCEL_OPTION) { return false; } } return openFile(file); } return false; } } } /** * Method to determine and open generic files dropped into Portecle. * * @param file the file to open * @return true if the file can be opened, false otherwise */ private boolean openFile(File file) { if (file != null) { String fileName = file.getName().toLowerCase(Locale.ENGLISH); for (String ext : FileChooserFactory.CERT_EXTS) { if (fileName.endsWith("." + ext)) { examineCert(file); return true; } } for (String ext : FileChooserFactory.CRL_EXTS) { if (fileName.endsWith("." + ext)) { examineCRL(file); return true; } } for (String ext : FileChooserFactory.CSR_EXTS) { if (fileName.endsWith("." + ext)) { examineCSR(file); return true; } } return openKeyStoreFile(file, true); } return false; } /** * Runnable to create and show Portecle GUI. */ private static class CreateAndShowGui implements Runnable { /** File or host:port to open initially */ private final Object m_obj; /** * Construct CreateAndShowGui. * * @param obj File or host:port to open initially (supply null if none) */ public CreateAndShowGui(Object obj) { m_obj = obj; } /** * Create and show Portecle GUI. */ @Override public void run() { initLookAndFeel(); FPortecle fPortecle = new FPortecle(); fPortecle.setVisible(true); if (m_obj instanceof File) { fPortecle.openFile((File) m_obj); } else if (m_obj instanceof InetSocketAddress) { fPortecle.examineCertSSL((InetSocketAddress) m_obj); } } } /** * Start the Portecle application. Takes one optional argument - the location of a keystore file to open * upon startup. * * @param args the command line arguments */ public static void main(String[] args) { // Make Metal theme use non-bold fonts (see javax.swing.plaf.metal.MetalLookAndFeel javadoc) UIManager.put("swing.boldMetal", Boolean.FALSE); try { Provider bcProv = Security.getProvider("BC"); if (bcProv == null) { // Instantiate the Bouncy Castle provider Class<?> bcProvClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); bcProv = (Provider) bcProvClass.newInstance(); // Add BC as a security provider Security.addProvider(bcProv); } // Check BC version Double bcVer = new Double(bcProv.getVersion()); if (REQ_BC_VERSION.compareTo(bcVer) > 0) { JOptionPane.showMessageDialog(new JFrame(), MessageFormat.format(RB.getString("FPortecle.NoBcVersion.message"), REQ_BC_VERSION, bcVer), RB.getString("FPortecle.Title"), JOptionPane.WARNING_MESSAGE); } } catch (Throwable thw) { // No sign of the provider - warn the user and exit LOG.log(Level.SEVERE, "FPortecle.NoLoadBc.message", thw); JOptionPane.showMessageDialog(new JFrame(), RB.getString("FPortecle.NoLoadBc.message"), RB.getString("FPortecle.Title"), JOptionPane.ERROR_MESSAGE); System.exit(1); } // Install additional providers String[] additionalProviders = RB.getString("FPortecle.AdditionalProviders").split("[\\s,]+"); for (String addProv : additionalProviders) { String[] prov = addProv.split(":+", 2); if (Security.getProvider(prov[0]) == null) { try { Class<?> provClass = Class.forName(prov[1]); Security.addProvider((Provider) provClass.newInstance()); } catch (Throwable t) { // TODO: should maybe notify in some cases? // E.g. Throwable, but not Exception? } } } // If arguments have been supplied, treat the first one that's not "-open" (web start passes that when // opening associated files) as a keystore/certificate etc file Object toOpen = null; for (String arg : args) { if (!arg.equals("-open")) { if (arg.matches("^[\\w.]+:\\d+$")) { String host = arg.substring(0, arg.indexOf(":")); int port = Integer.parseInt(arg.substring(arg.indexOf(":") + 1)); toOpen = new InetSocketAddress(host, port); } else { toOpen = new File(arg); } break; } } // Create and show GUI on the event handler thread SwingUtilities.invokeLater(new CreateAndShowGui(toOpen)); } }