/* * VerifyKeyStore.java * * * ==================================================== Professional Data Security (PDS) http://crypto.brettlee.com ==================================================== Copyright (c) 2009-2011, Brett Lee All rights reserved. Portions Copyright (C) 1995-2008, Sun Microsystems, Inc Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ORGANIZATION nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================================================================= */ package com.brettlee.crypto; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.Vector; import java.util.regex.*; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.border.TitledBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; /** * VerifyKeyStore * * Verify that the KeyStore can be found, either via its absolute path * or via its shortname in ./pdsData/mykeys/, or via the KSP * * */ class VerifyKeyStore { // verifyLocation args: CryptoFile, int // // int, 0 = check initiated to recover the KeyStore (and Key) path // 1 = recursive check to verify the KeyStore Location // - if not found via Absolute path, then searches // KeyStore Search Path, then offers JFileChooser // for a manual search. /** * */ private final Gui mGui; /** * @param theGui */ VerifyKeyStore(Gui theGui) { mGui = theGui; } public boolean verifyLocation (CryptoFile cryptoFile, int type) { // System.out.println("Beginning VerifyKeyStore().verifyLocation()"); if ( type == 0 ) { // System.out.println("Beginning verify KeyStore from file"); boolean pdsBinary = false; ObjectInputStream ois = null; // Try binary file format first. // try { if ( cryptoFile instanceof CryptoFileExternal ) { CryptoFileExternal cryptoFileExternal = (CryptoFileExternal) cryptoFile; if ( cryptoFileExternal.getDeviceType() == CryptoFileExternal.kDEVICE_RAW ) { ois = new ObjectInputStream( new BufferedInputStream( new FileInputStream( cryptoFileExternal.getFullyPathedFileName() ), 2*1024*1024 )); } else { ois = new ObjectInputStream( new BufferedInputStream( new FileInputStream( cryptoFileExternal.getFullyPathedFileName() ))); } cryptoFile = cryptoFileExternal; } Object oisObject = ois.readObject(); if (oisObject instanceof String) { if (((String) oisObject).equals("PDS")) { // System.out.println("File in PDS binary format"); pdsBinary = true; // get File Header oisObject = ois.readObject(); if (oisObject instanceof FileHeader) { // System.out.println("Have file header"); // get Data Header oisObject = ois.readObject(); if (oisObject instanceof DataHeader) { // System.out.println("Have data header"); DataHeader dataHeader = (DataHeader) oisObject; cryptoFile.setKsName ( dataHeader.getKsPathAndName() ); // System.out.println("VerifyKeyStore found in the header: " + dataHeader.getKsPathAndName()); cryptoFile.setKeyAlias ( dataHeader.getKeyAlias() ); ois.close(); return (new VerifyKeyStore(mGui).verifyLocation (cryptoFile, 1)); } else { System.out.println("Verify KeyStore - Error - Invalid data header"); } } else { System.out.println("Verify KeyStore - Error - Invalid file header"); } } else { // System.out.println("Verify KeyStore - Not a PDS Binary - maybe written Base64"); } // System.out.println("Verify KeyStore - Not a PDS Binary - found a string, but not PDS"); } // System.out.println("Verify KeyStore - Not a PDS Binary - String not recovered."); } catch (Exception e) { // May not be an error exception, as it might be in Base64 format // System.out.println("Exception in VerifyKeyStore - Checking Binary: " + e); //e.printStackTrace(); } finally { try { ois.close(); } catch (Exception ex) { // System.out.println("Unable to close: " + cryptoFile.getFullyPathedFileName()); } } // Not a PDS Binary - Check if PDS Base64 if ( ! pdsBinary ) { BufferedReader br = null; // Get the KeyStore name and Key Alias from existing file try { mGui.mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); // System.out.println("Reading encoded file"); br = new BufferedReader( new FileReader( cryptoFile.getFullyPathedFileName() )); String currentLine; while ( ((currentLine = br.readLine()) != null) && (currentLine.getBytes().length != 0) ) { // System.out.println(currentLine); Pattern pattern = Pattern.compile("(.*?)#(.*)"); Matcher matcher = pattern.matcher(currentLine); if( matcher.matches() ) { // System.out.println("Matching line: " + currentLine); // System.out.println("Matcher 1: : " + matcher.group(1) ); // System.out.println("Matcher 2: : " + matcher.group(2) ); if ( matcher.group(1).equals("KeyStore") ) { String tmpKeyStorePath = matcher.group(2); // For DOS paths, strip leading slash and sub the rest with backslash. pattern = Pattern.compile("/[A-Z]:/(.*)", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher( tmpKeyStorePath ); if ( matcher.matches() ) { // System.out.println("DOS World."); // strip leading slash tmpKeyStorePath = tmpKeyStorePath.replaceFirst( "/", "" ); // substitute backslash for slash cryptoFile.setKsName( tmpKeyStorePath.replaceAll( "/", "\\\\" ) ); } else { cryptoFile.setKsName( tmpKeyStorePath ); } } else if (matcher.group(1).equals("Alias")) { cryptoFile.setKeyAlias(matcher.group(2)); } else if (matcher.group(1).equals("CryptoFile")) { // ignore } else if (matcher.group(1).equals("CryptoFileNativeFormat")) { // ignore } else { System.out.println("Unknown line in header. Parsing header for Key Alias: " + matcher.group(1) ); } } } // System.out.println("KeyStore : " + cryptoFile.getKsName()); // System.out.println("Key Alias: " + cryptoFile.getKeyAlias()); } catch (FileNotFoundException e) { System.out.println("Error - Encrypted File Not Found: " + e.getMessage()); return false; } catch (IOException e) { System.out.println("Error - I/O: " + e.getMessage()); return false; } finally { mGui.mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); try { br.close(); } catch (Exception e) { System.out.println("Error - I/O: " + e.getMessage()); return false; } } // Have tried to get the KeyStore (and Key) info with what was provided. // Using what was collected (if anything), call this method again with the "1" arg. // // System.out.println("Done trying to extract the fully pathed KeyStore name / key alias from the metadata."); // System.out.println("About to see if the provided values can be used to find a KeyStore."); return (new VerifyKeyStore(mGui).verifyLocation (cryptoFile, 1)); } } if ( type == 1 ) { // The "1" arg is called from the "0" arg // Will attempt to see if valid KeyStore data was recovered. // If so, will attempt to identify the current location of the KeyStore. // If provided information does not work, search the KeyStore Search Path // and provide a list of all KeyStores that match by filename. // If that still doesn't work, fall back to a File Chooser. // System.out.println("Do we have a KeyStore entry?"); // Were we able to pull a KeyStore entry from the provided file? // if ( cryptoFile.getKsName() == null ) { System.out.println("Invalid file format: KeyStore entry was not found."); // Make a popup here JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Invalid File Format." + mGui.newline + "KeyStore entry was not found in file.", "Error Decrypting File", JOptionPane.ERROR_MESSAGE); return false; } // System.out.println("Does KeyStore exist fully pathed?"); // Does KeyStore exist as returned by getKsName()? // try { if ( new java.io.File(cryptoFile.getKsName()).exists() ) { // System.out.println("KeyStore exists fully pathed."); return true; } } catch (Exception e) { System.out.println("Exception checking for fully pathed KeyStore."); return false; } // KeyStore was not found via the path specified in the PDS file. // Check KSP // System.out.println("Does KeyStore exist in the KeyStore Path (KSP)?"); // System.out.println("KeyStore: " + cryptoFile.getKsName()); // Strip off path info Pattern pattern = Pattern.compile("^(.*[/\\\\])(.*)$"); Matcher matcher = pattern.matcher(cryptoFile.getKsName()); if( matcher.matches() ) { // System.out.println("Entire KeyStore Filename: " + cryptoFile.getKsName()); // System.out.println("Short KeyStore Filename : " + matcher.group(2)); // Get the KSP Vector keyStoreSearchPath = mGui.stateObject.getKeyStoreSearchPath(); // Get Vector containing all KSP matches for the short name Vector keyStoreSearchPathMatches = new Vector(); Iterator i = keyStoreSearchPath.iterator(); String searchPathItem; while ( i.hasNext() ) { searchPathItem = (String) i.next(); // System.out.println("Checking: " + searchPathItem + matcher.group(2)); if ( (new java.io.File( (searchPathItem + matcher.group(2))).exists() )) { keyStoreSearchPathMatches.add(searchPathItem); } } // If Vector has data, display in a table if ( keyStoreSearchPathMatches.size() > 0 ) { // i = keyStoreSearchPathMatches.iterator(); // while ( i.hasNext() ) { // System.out.println("Match: " + i.next().toString()); // } JFrame frame = new JFrame(); new createKeySearchPathPanel( frame, true, cryptoFile, keyStoreSearchPathMatches, matcher.group(2) ); if ( StateObject.getReturnFromKSP() ) { StateObject.setReturnFromKSP( false ); // System.out.println("Returning true"); return true; } else { // System.out.println("Returning false"); return false; } } else { // System.out.println("Unable to find the KeyStore as specified in the PDS file." + newline); // System.out.println("Also unable to find the KeyStore in the KeyStore Search Path." + newline); // notify if cannot find KeyStore JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, mGui.newline + "The KeyStore \"" + matcher.group(2) + "\" was not found at the expected location:" + mGui.newline + mGui.newline + cryptoFile.getKsName() + mGui.newline + mGui.newline + mGui.newline + "It was also not found in the KeyStore Search Path." + mGui.newline + mGui.newline + "Please browse to the KeyStore and select the KeyStore to use." + mGui.newline + mGui.newline , "KeyStore not found", JOptionPane.WARNING_MESSAGE); // Display a JFileChooser here JDialog findKeyStoreDialog = new JDialog(); JFileChooser fileChooser; if ( mGui.stateObject.getDefaultKeyStoreDir() != null ) { fileChooser = new JFileChooser ( mGui.stateObject.getDefaultKeyStoreDir() ); } else { fileChooser = new JFileChooser ( mGui.runTimePath ); } fileChooser.setDialogTitle( "KeyStore Location" ); fileChooser.setApproveButtonText("Select"); FileNameExtensionFilter filterPDS = new FileNameExtensionFilter("Professional Data Security Files (PDS)", "PDS"); FileNameExtensionFilter filterJCEKS = new FileNameExtensionFilter("Java Cryptographic Extension (JCE) KeyStores (JCEKS)", "JCEKS"); FileNameExtensionFilter filterJKS = new FileNameExtensionFilter("Java KeyStores (JKS)", "JKS"); FileNameExtensionFilter filterKeyStores = new FileNameExtensionFilter("Java KeyStores (JCEKS, JKS)", "JCEKS", "JKS"); fileChooser.addChoosableFileFilter( filterJKS ); fileChooser.addChoosableFileFilter( filterJCEKS ); fileChooser.addChoosableFileFilter( filterPDS ); fileChooser.addChoosableFileFilter(filterKeyStores); int returnVal = fileChooser.showOpenDialog(findKeyStoreDialog); if ( returnVal == 0 ) { // Set the KeyStore name in the new CryptoFile object cryptoFile.setKsName( fileChooser.getSelectedFile().getPath() ); // System.out.println("KeyStore Name: "); // System.out.println(cryptoFile.getKsName()); // System.out.print("KeyStore: " + cryptoFile.getKsName() + newline); return true; } else if ( returnVal == 1 ) { // System.out.print("Canceling..." + newline); StateObject.setFileChooserCancel( true ); return false; } else { System.out.print("Error: " + returnVal + mGui.newline); return false; } } } else { // System.out.println("Unable to find the KeyStore as specified in the PDS file." + mGui.newline); // System.out.println("Also unable to find the KeyStore in the KeyStore Search Path." + mGui.newline); // notify if cannot find KeyStore JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "KeyStore not found." + mGui.newline + mGui.newline + "The KeyStore that was listed in the PDS file was not found:" + mGui.newline + "KeyStore: " + matcher.group(2) + mGui.newline + mGui.newline + "Location:" + mGui.newline + cryptoFile.getKsName() + mGui.newline + mGui.newline + "Please browse to the KeyStore and select the KeyStore to use.", "KeyStore not found", JOptionPane.WARNING_MESSAGE); // Display a JFileChooser here JDialog findKeyStoreDialog = new JDialog(); JFileChooser fileChooser; if ( mGui.stateObject.getDefaultKeyStoreDir() != null ) { fileChooser = new JFileChooser ( mGui.stateObject.getDefaultKeyStoreDir() ); } else { fileChooser = new JFileChooser ( mGui.runTimePath ); } fileChooser.setDialogTitle( "KeyStore Location" ); fileChooser.setApproveButtonText("Select"); FileNameExtensionFilter filterPDS = new FileNameExtensionFilter("Professional Data Security Files (PDS)", "PDS"); FileNameExtensionFilter filterJCEKS = new FileNameExtensionFilter("Java Cryptographic Extension (JCE) KeyStores (JCEKS)", "JCEKS"); FileNameExtensionFilter filterJKS = new FileNameExtensionFilter("Java KeyStores (JKS)", "JKS"); FileNameExtensionFilter filterKeyStores = new FileNameExtensionFilter( "Java KeyStores (JCEKS, JKS)", "JCEKS", "JKS"); fileChooser.addChoosableFileFilter( filterJKS ); fileChooser.addChoosableFileFilter( filterJCEKS ); fileChooser.addChoosableFileFilter( filterPDS ); fileChooser.addChoosableFileFilter(filterKeyStores); int returnVal = fileChooser.showOpenDialog(findKeyStoreDialog); if ( returnVal == 0 ) { // Set the KeyStore name in the new CryptoFile object cryptoFile.setKsName( fileChooser.getSelectedFile().getPath() ); // System.out.println("KeyStore Name: "); // System.out.println(cryptoFile.getKsName()); // System.out.print("KeyStore: " + cryptoFile.getKsName() + newline); return true; } else if ( returnVal == 1 ) { // System.out.print("Canceling..." + newline); return false; } else { System.out.print("Error: " + returnVal + mGui.newline); return false; } } } return false; } class createKeySearchPathPanel extends JDialog { public createKeySearchPathPanel ( final JFrame frame, boolean modal, CryptoFile cryptoFile, Vector keyStoreSearchPathMatches, String keyStoreName ) { ///////////////////////////////////////////////////////////// // KeyStore Search Panel super(frame, modal); initComponents(cryptoFile, keyStoreSearchPathMatches, keyStoreName); pack(); setLocationRelativeTo(frame); setTitle("Select a KeyStore"); setVisible(true); } void initComponents( final CryptoFile cryptoFile, Vector keyStoreSearchPathMatches, final String keyStoreName ) { JPanel contentPane = new JPanel(); add(contentPane); final JButton jButtonOk = new JButton("Ok"); ///////////////////////////////////////////////////////////// // Build the Dialog GroupLayout layout = new GroupLayout(contentPane); contentPane.setLayout(layout); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); FlowLayout flowLayoutLeft = new FlowLayout(); flowLayoutLeft.setAlignment(FlowLayout.LEFT); JPanel p1 = new JPanel(); JPanel p2 = new JPanel(flowLayoutLeft); JPanel p3 = new JPanel(); JPanel p4 = new JPanel(); layout.setHorizontalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(p1) .addComponent(p2) .addComponent(p3) .addComponent(p4) ) ); layout.setVerticalGroup( layout.createSequentialGroup() .addComponent(p1) .addComponent(p2) .addComponent(p3) .addComponent(p4) ); ///////////////////////////////////////////////////////////// // p1 - Title JLabel jlabel = new JLabel("KeyStore Search"); p1.add(jlabel); // p2 - Paths JTextArea textArea = new JTextArea(); textArea.setEditable(false); textArea.setOpaque(false); textArea.append("The KeyStore \"" + keyStoreName + "\" was not found at the expected location." + mGui.newline); textArea.append(mGui.newline); textArea.append("The list below contains directories in the KeyStore Search Path" + mGui.newline); textArea.append("that contain a KeyStore with the same name." + mGui.newline); textArea.append(mGui.newline); textArea.append("Select one of these paths to try that KeyStore." + mGui.newline); p2.add(textArea); // p2 - Paths JPanel p3SubPanel = new JPanel(new FlowLayout()); p3.add(p3SubPanel); p3.setBorder(new TitledBorder("Paths that may hold the key:")); DefaultTableModel model = new DefaultTableModel(); model.addColumn("Paths containing a KeyStore named \"" + keyStoreName + "\"", keyStoreSearchPathMatches); final JTable table = new JTable( model ) { public boolean isCellEditable(int row, int column) { return false; } public Dimension getPreferredScrollableViewportSize() { Dimension size = super.getPreferredScrollableViewportSize(); return new Dimension(size.width, Math.min(getPreferredSize().height, 110)); } public void valueChanged(ListSelectionEvent e) { super.valueChanged( e ); jButtonOk.setEnabled(true); } }; table.setAutoResizeMode ( JTable.AUTO_RESIZE_ALL_COLUMNS ); table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); table.setRowSelectionAllowed(true); // Add ability to 2x-click one of the rows table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { // class SharedListSelectionHandler implements ListSelectionListener { // public void valueChanged(ListSelectionEvent e) { // ListSelectionModel lsm = (ListSelectionModel) e.getSource(); // } // } @Override public void valueChanged(ListSelectionEvent arg0) { } } ); MouseListener mouseListener = new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { // Get the Path int selected = table.getSelectedRow(); // System.out.println ("Row:Value: " + selected + ":" + (String) table.getValueAt(selected, 0) ); // Set KeyStore cryptoFile.setKsName( (String) (table.getValueAt(selected, 0) + keyStoreName) ); // Indicate Success StateObject.setReturnFromKSP( true ); dispose(); } } }; table.addMouseListener(mouseListener); // Sorting table.setAutoCreateRowSorter(true); table.setUpdateSelectionOnSort(true); TableRowSorter sorter = new TableRowSorter(table.getModel()); table.setRowSorter(sorter); java.util.List sortKeys = new ArrayList(); sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING)); sorter.setSortKeys(sortKeys); sorter.sort(); JScrollPane scrollPane = new JScrollPane(table); TableColumnModel colModel = table.getColumnModel(); for(int j = 0; j < colModel.getColumnCount(); j++) colModel.getColumn(j).setCellRenderer(new RowRenderer()); p3SubPanel.add(scrollPane); // p4 - Buttons - Ok & Cancel // // jButton created earlier as listener on another object enables this button jButtonOk.setMnemonic(KeyEvent.VK_O); Gui.enterPressesWhenFocused(jButtonOk); jButtonOk.setEnabled(false); jButtonOk.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // System.out.println("A new KeyStore Path was selected."); // Get the Path int selected = table.getSelectedRow(); // System.out.println ("Row:Value: " + selected + ":" + (String) table.getValueAt(selected, 0) ); // Set KeyStore cryptoFile.setKsName( (String) (table.getValueAt(selected, 0) + keyStoreName) ); // Indicate Success StateObject.setReturnFromKSP( true ); dispose(); } }); p4.add(jButtonOk); JButton jButtonCancel = new JButton("Cancel"); jButtonCancel.setMnemonic(KeyEvent.VK_C); Gui.enterPressesWhenFocused(jButtonCancel); jButtonCancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // System.out.println("Canceling..."); StateObject.setFileChooserCancel( true ); dispose(); } }); p4.add(jButtonCancel); } } // RowRenderer from: http://www.javalobby.org/java/forums/t84905.html class RowRenderer extends DefaultTableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); setToolTipText((String)table.getValueAt(row, 0)); return this; } } }