bloritsch    01/09/05 05:21:46

  Added:       src/org/apache/jmeter/gui/action SSLManagerCommand.java
               src/org/apache/jmeter/util SSLManager.java
  Log:
  Add SSL Manager code
  
  Revision  Changes    Path
  1.1                  
jakarta-jmeter/src/org/apache/jmeter/gui/action/SSLManagerCommand.java
  
  Index: SSLManagerCommand.java
  ===================================================================
  /*
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *
   * 2. 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.
   *
   * 3. The end-user documentation included with the redistribution,
   * if any, must include the following acknowledgment:
   * "This product includes software developed by the
   * Apache Software Foundation (http://www.apache.org/)."
   * Alternately, this acknowledgment may appear in the software itself,
   * if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and
   * "Apache JMeter" must not be used to endorse or promote products
   * derived from this software without prior written permission. For
   * written permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   * "Apache JMeter", nor may "Apache" appear in their name, without
   * prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
   * ITS 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.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.jmeter.gui.action;
  
  import java.awt.event.ActionEvent;
  import java.util.Set;
  import java.util.HashSet;
  import java.util.Collections;
  import javax.swing.*;
  import java.awt.*;
  import java.awt.event.*;
  import javax.swing.border.*;
  import org.apache.jmeter.gui.GuiPackage;
  import org.apache.jmeter.util.JMeterUtils;
  import org.apache.jmeter.util.SSLManager;
  
  import java.io.File;
  import javax.swing.filechooser.FileFilter;
  
  /**
   * SSL Manager Command.  The SSL Manager provides a mechanism to change your
   * client authentication if required by the server.  If you have JSSE 1.0.2
   * installed, you can select your client identity from a list of installed keys.
   * You can also change your keystore.  JSSE 1.0.2 allows you to export a PKCS#12
   * key from Netscape 4.04 or higher and use it in a read only format.  You must
   * supply a password that is greater than six characters due to limitations in
   * the keytool program--and possibly the rest of the system.
   *
   * <p>
   * By selecting a *.p12 file as your keystore (your PKCS#12) format file, you can
   * have a whopping one key keystore.  The advantage is that you can test a
   * connection using the assigned Certificate from a Certificate Authority.
   * </p>
   *
   * @author <a href="[EMAIL PROTECTED]">Berin Loritsch</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/09/05 12:21:46 $
   */
  
  public class SSLManagerCommand implements Command {
      private static Set commandSet;
      private JFileChooser keyStoreChooser;
  
      static {
          HashSet commands = new HashSet();
          commands.add("sslManager");
          SSLManagerCommand.commandSet = Collections.unmodifiableSet(commands);
          System.setProperty("java.protocol.handler.pkgs", 
"com.sun.net.ssl.internal.www.protocol");
          System.setProperty("javax.net.ssl.debug", "all");
      }
  
      /**
       * Handle the "sslmanager" action by displaying the "SSL CLient Manager"
       * dialog box.  The Dialog Box is NOT modal, because those should be avoided
       * if at all possible.
       */
      public void doAction(ActionEvent e) {
          if (e.getActionCommand().equals("sslManager")) {
              this.sslManager();
          }
      }
  
      /**
       * Provide the list of Action names that are available in this command.
       */
      public Set getActionNames() {
          return SSLManagerCommand.commandSet;
      }
  
      /**
       * Called by sslManager button. Raises sslManager dialog.  Currently the 
sslManager box has
       * the product image and the copyright notice.  The dialog box is centered
       * over the MainFrame.
       */
      private void sslManager() {
          SSLManager.reset();
  
          keyStoreChooser = new 
JFileChooser(JMeterUtils.getJMeterProperties().getProperty("user.dir"));
          keyStoreChooser.addChoosableFileFilter(new AcceptPKCS12FileFilter());
          keyStoreChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
          int retVal = 
keyStoreChooser.showOpenDialog(GuiPackage.getInstance().getMainFrame());
  
          if (JFileChooser.APPROVE_OPTION == retVal) {
              File selectedFile = keyStoreChooser.getSelectedFile();
              try {
                  
JMeterUtils.getJMeterProperties().setProperty("javax.net.ssl.keyStore", 
selectedFile.getCanonicalPath());
              } catch (Exception e) {
              }
          }
  
          keyStoreChooser = null;
          SSLManager.getInstance();
      }
  
      /**
       * Internal class to add a PKCS12 file format filter for JFileChooser.
       */
      static private class AcceptPKCS12FileFilter extends FileFilter {
          /**
           * Get the description that shows up in JFileChooser filter menu.
           *
           * @return description
           */
          public String getDescription() {
              return "PKCS 12 Key (*.p12)";
          }
  
          /**
           * Tests to see if the file ends with "*.p12" or "*.P12".
           *
           * @param testfile file to test
           * @return         true if file is accepted, false otherwise
           */
          public boolean accept(File testFile) {
              return testFile.isDirectory() ||
                     testFile.getName().endsWith(".p12") ||
                     testFile.getName().endsWith(".P12");
          }
      }
  }
  
  
  
  1.1                  jakarta-jmeter/src/org/apache/jmeter/util/SSLManager.java
  
  Index: SSLManager.java
  ===================================================================
  /*
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *
   * 2. 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.
   *
   * 3. The end-user documentation included with the redistribution,
   * if any, must include the following acknowledgment:
   * "This product includes software developed by the
   * Apache Software Foundation (http://www.apache.org/)."
   * Alternately, this acknowledgment may appear in the software itself,
   * if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and
   * "Apache JMeter" must not be used to endorse or promote products
   * derived from this software without prior written permission. For
   * written permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   * "Apache JMeter", nor may "Apache" appear in their name, without
   * prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
   * ITS 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.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.jmeter.util;
  
  import java.io.File;
  import java.io.FileInputStream;
  
  import java.security.Principal;
  import java.security.SecureRandom;
  import java.security.KeyStore;
  import java.security.KeyStoreException;
  import java.security.cert.Certificate;
  import java.security.cert.X509Certificate;
  import java.security.PrivateKey;
  import java.security.Key;
  
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.List;
  
  import javax.swing.JOptionPane;
  
  import com.sun.net.ssl.SSLContext;
  import com.sun.net.ssl.X509KeyManager;
  import com.sun.net.ssl.KeyManager;
  import com.sun.net.ssl.KeyManagerFactory;
  import com.sun.net.ssl.HttpsURLConnection;
  
  import org.apache.jmeter.gui.GuiPackage;
  
  /**
   * The SSLManager handles the KeyStore information for JMeter.  Basically, it
   * handles all the logic for loading and initializing all the JSSE parameters
   * and selecting the alias to authenticate against if it is available.  SSLManager
   * will try to automatically select the client certificate for you, but if it can't
   * make a decision, it will pop open a dialog asking you for more information.
   *
   * @author <a href="[EMAIL PROTECTED]">Berin Loritsch</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/09/05 12:21:46 $
   */
  public class SSLManager {
      /** Singleton instance of the manager */
      private static SSLManager manager;
      /** Cache the SecureRandom instance because it takes a long time to create */
      private static SecureRandom rand;
  
      /** Cache the KeyStore instance */
      private KeyStore keyStore;
      /** Have the password available */
      private String defaultpw = 
JMeterUtils.getJMeterProperties().getProperty("javax.net.ssl.keyStorePassword");
      /** Cache the Context so we can retrieve it from other places */
      private SSLContext context = null;
  
      /**
       * Resets the SSLManager so that we can create a new one with a new keystore
       */
      static public void reset() {
          SSLManager.manager = null;
      }
  
      /**
       * Returns the SSLContext we are using.  It is useful for obtaining the 
SSLSocketFactory
       * so that your created sockets are authenticated.
       */
      public SSLContext getContext() {
          return this.context;
      }
  
      /**
       * Opens and initializes the KeyStore.  If the password for the KeyStore is
       * not set, this method will prompt you to enter it.  Unfortunately, there is
       * no PasswordEntryField available from JOptionPane.
       */
      private KeyStore getKeyStore() {
          String password = this.defaultpw;
  
          if (null == this.keyStore) {
              String defaultName = 
JMeterUtils.getJMeterProperties().getProperty("user.home") + File.separator +
                                   ".keystore";
              String fileName = 
JMeterUtils.getJMeterProperties().getProperty("javax.net.ssl.keyStore", defaultName);
              System.setProperty("javax.net.ssl.keyStore", fileName);
  
              try {
                  if (fileName.endsWith(".p12") || fileName.endsWith(".P12")) {
                      this.keyStore = KeyStore.getInstance("pkcs12");
                      System.out.println("KeyStore Type: PKCS 12");
                      System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
                  } else {
                      this.keyStore = KeyStore.getInstance("JKS");
                      System.out.println("KeyStore Type: JKS");
                  }
              } catch (KeyStoreException e) {
                  
JOptionPane.showMessageDialog(GuiPackage.getInstance().getMainFrame(),
                                                e,
                                                "KeyStore Problem",
                                                JOptionPane.ERROR_MESSAGE);
                  this.keyStore = null;
                  throw new RuntimeException("KeyStore Problem");
              }
  
              if (null == password) {
                  if (null == defaultpw) {
                      this.defaultpw = 
JMeterUtils.getJMeterProperties().getProperty("javax.net.ssl.keyStorePassword");
  
                      if (null == defaultpw) {
                          synchronized(this) {
                              this.defaultpw = 
JOptionPane.showInputDialog(GuiPackage.getInstance().getMainFrame(),
                                                          "KeyStore Password",
                                                          "Input Password",
                                                          
JOptionPane.QUESTION_MESSAGE);
                              
JMeterUtils.getJMeterProperties().setProperty("javax.net.ssl.keyStorePassword", 
this.defaultpw);
                          }
                      }
                  }
  
                  password = this.defaultpw;
                  System.setProperty("javax.net.ssl.keyStorePassword", password);
              }
  
              try {
                  File initStore = new File(fileName);
  
                  if (initStore.exists()) {
                      try {
                          this.keyStore.load(new FileInputStream(initStore), 
password.toCharArray());
                      } catch (Exception e) {
                          throw new RuntimeException("Can't load KeyStore!!!  " + 
e.getMessage());
                      }
                  } else {
                      this.keyStore.load(null, password.toCharArray());
                  }
              } catch (Exception e) {
              }
          }
  
          return this.keyStore;
      }
  
      /**
       * Private Constructor to remove the possibility of directly instantiating
       * this object.  Create the SSLContext, and wrap all the X509KeyManagers with
       * our X509KeyManager so that we can choose our alias.
       */
      private SSLManager() {
          if (null == this.rand) {
              this.rand = new SecureRandom();
          }
  
          try {
              this.context = SSLContext.getInstance("TLS");
              KeyManagerFactory managerFactory = 
KeyManagerFactory.getInstance("SunX509");
              KeyStore keys = this.getKeyStore();
              managerFactory.init(keys, this.defaultpw.toCharArray());
              KeyManager[] managers = managerFactory.getKeyManagers();
  
              for (int i = 0; i < managers.length; i++) {
                  if (managers[i] instanceof X509KeyManager) {
                      X509KeyManager manager = (X509KeyManager) managers[i];
                      managers[i] = new WrappedX509KeyManager(manager, keys, 
this.defaultpw);
                  }
              }
  
              context.init(managers, null, this.rand);
              
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
          } catch (Exception e) {
          }
      }
  
      /**
       * Static accessor for the SSLManager object.  The SSLManager is a singleton.
       */
      public static final SSLManager getInstance() {
          if (null == SSLManager.manager) {
              SSLManager.manager = new SSLManager();
          }
  
          return SSLManager.manager;
      }
  
      /**
       * This is the X509KeyManager we have defined for the sole purpose of selecing
       * the proper key and certificate based on the keystore available.
       */
      private static class WrappedX509KeyManager implements X509KeyManager {
          /** The parent X509KeyManager */
          private final X509KeyManager manager;
          /** The default password passed from the instantiator */
          private String defaultpw = 
JMeterUtils.getJMeterProperties().getProperty("javax.net.ssl.keyStorePassword");
          /** The default alias discovered automatically or from user input */
          private String defaultAlias = 
JMeterUtils.getJMeterProperties().getProperty("jmeter.ssl.default.alias");
          /** The KeyStore this KeyManager uses */
          private final KeyStore store;
  
          /**
           * Instantiate a new WrappedX509KeyManager.
           *
           * @param parent    The parent X509KeyManager
           * @param ks        The KeyStore we derive our client certs and keys from
           * @param password  The password used for the KeyStore and the private key.
           */
          public WrappedX509KeyManager(X509KeyManager parent, KeyStore ks, String 
password) {
              if (null != password) {
                  this.defaultpw = password;
              }
  
              this.manager = parent;
              this.store = ks;
          }
  
          /**
           * Select the Alias we will authenticate as if Client authentication is
           * required by the server we are connecting to.  We get the list of aliases,
           * and if there is only one alias we automatically select it.  If there are
           * more than one alias that has a private key, we prompt the user to choose
           * which alias using a combo box.  Otherwise, we simply provide a text box,
           * which may or may not work.  The alias does have to match one in the 
keystore.
           *
           * @param keyType  The type of private key the server expects (RSA, DSA, 
etc.)
           * @param issuers  The CA certificates we are narrowing our selection on.
           */
          public String chooseClientAlias(String keyType, Principal[] issuers) {
              if (null == this.defaultAlias) {
                  String[] aliases = this.getClientAliases(keyType, issuers);
                  if (null != aliases && aliases.length == 1) {
                      this.defaultAlias = aliases[0];
                  } else if (aliases.length > 1) {
                      synchronized(this) {
                          this.defaultAlias = (String)
                              
JOptionPane.showInputDialog(GuiPackage.getInstance().getMainFrame(),
                                                      "Select Your Alias for the test",
                                                      "Client Alias",
                                                      JOptionPane.QUESTION_MESSAGE,
                                                      null,
                                                      aliases,
                                                      aliases[0]);
                      }
                  } else {
                      synchronized(this) {
                          this.defaultAlias =
                              
JOptionPane.showInputDialog(GuiPackage.getInstance().getMainFrame(),
                                                          "Please type your prefered 
alias",
                                                          "Client Alias",
                                                          
JOptionPane.QUESTION_MESSAGE);
                      }
                  }
              }
              System.out.println("Alias: " + this.defaultAlias);
  
              return this.defaultAlias;
          }
  
          /**
           * Compiles the list of all client aliases with a private key.  Currently,
           * keyType and issuers are both ignored.
           *
           * @param keyType  The type of private key the server expects (RSA, DSA, 
etc.)
           * @param issuers  The CA certificates we are narrowing our selection on.
           */
          public String[] getClientAliases(String keyType, Principal[] issuers) {
              List aliasList = new ArrayList();
  
              try {
                  Enumeration aliases = this.store.aliases();
  
                  while (aliases.hasMoreElements()) {
                      String alias = (String) aliases.nextElement();
  
                      if (this.store.isKeyEntry(alias)) {
                          aliasList.add(alias);
                      }
                  }
              } catch (Exception e) {
              }
  
              return (String[]) aliasList.toArray(new String[] {});
          }
  
          /**
           * Choose the server alias for the SSLServerSockets.  This are not used
           * in JMeter.
           *
           * @param keyType  The type of private key the server expects (RSA, DSA, 
etc.)
           * @param issuers  The CA certificates we are narrowing our selection on.
           */
          public String chooseServerAlias(String keyType, Principal[] issuers) {
              return this.manager.chooseServerAlias(keyType, issuers);
          }
  
          /**
           * Get the list of server aliases for the SSLServerSockets.  This is not
           * used in JMeter.
           *
           * @param keyType  The type of private key the server expects (RSA, DSA, 
etc.)
           * @param issuers  The CA certificates we are narrowing our selection on.
           */
          public String[] getServerAliases(String keyType, Principal[] issuers) {
              return this.manager.getServerAliases(keyType, issuers);
          }
  
          /**
           * Get the Certificate chain for a particular alias
           *
           * @param alias  The client alias
           */
          public X509Certificate[] getCertificateChain(String alias) {
              Certificate[] certs;
  
              try {
                  certs = this.store.getCertificateChain(alias);
              } catch (Exception e) {
                  return null;
              }
  
              X509Certificate[] x509certs = new X509Certificate[certs.length];
              for (int i = 0; i < certs.length; i++) {
                  x509certs[i] = (X509Certificate)certs[i];
              }
  
              return x509certs;
          }
  
          /**
           * Get the Private Key for a particular alias
           *
           * @param alias  The client alias
           */
          public PrivateKey getPrivateKey(String alias) {
              try {
                  return (PrivateKey) this.store.getKey(alias, 
this.defaultpw.toCharArray());
              } catch (Exception e) {
              }
  
              return null;
          }
      }
  }
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to