jmeter-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject svn commit: r1520704 - in /jmeter/trunk: bin/jmeter.properties src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java
Date Fri, 06 Sep 2013 21:40:25 GMT
Author: sebb
Date: Fri Sep  6 21:40:25 2013
New Revision: 1520704

URL: http://svn.apache.org/r1520704
Log:
Proxy SSL recording does not handle external embedded resources well
Add generation of keystores and aliases
Bugzilla Id: 55507

Modified:
    jmeter/trunk/bin/jmeter.properties
    jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java

Modified: jmeter/trunk/bin/jmeter.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/jmeter.properties?rev=1520704&r1=1520703&r2=1520704&view=diff
==============================================================================
--- jmeter/trunk/bin/jmeter.properties (original)
+++ jmeter/trunk/bin/jmeter.properties Fri Sep  6 21:40:25 2013
@@ -539,14 +539,17 @@ upgrade_properties=/bin/upgrade.properti
 #---------------------------------------------------------------------------
 # JMeter Proxy Server configuration
 #---------------------------------------------------------------------------
+
 #proxy.cert.directory=<JMeter bin directory>
 #proxy.cert.file=proxyserver.jks
 #proxy.cert.type=JKS
 #proxy.cert.keystorepass=password
 #proxy.cert.keypassword=password
 #proxy.cert.factory=SunX509
-# define this property if the keystore contains multiple aliases
+# define this property if you wish to use your own keystore
 #proxy.cert.alias=<none>
+# The default validity for certificates created by JMeter
+#proxy.cert.validity=7
 
 # SSL configuration
 #proxy.ssl.protocol=SSLv3

Modified: jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java?rev=1520704&r1=1520703&r2=1520704&view=diff
==============================================================================
--- jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java (original)
+++ jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java Fri Sep
 6 21:40:25 2013
@@ -36,6 +36,7 @@ import java.security.GeneralSecurityExce
 import java.security.KeyStore;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.prefs.Preferences;
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
@@ -44,6 +45,9 @@ import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.JavaVersion;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.SystemUtils;
 import org.apache.jmeter.protocol.http.control.HeaderManager;
 import org.apache.jmeter.protocol.http.parser.HTMLParseException;
 import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
@@ -52,6 +56,7 @@ import org.apache.jmeter.protocol.http.u
 import org.apache.jmeter.samplers.SampleResult;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.exec.KeyToolUtils;
 import org.apache.jorphan.logging.LoggingManager;
 import org.apache.jorphan.util.JMeterException;
 import org.apache.jorphan.util.JOrphanUtils;
@@ -105,17 +110,51 @@ public class Proxy extends Thread {
 
     private static final String CERT_ALIAS = JMeterUtils.getProperty("proxy.cert.alias");
// $NON-NLS-1$
 
+    // The alias to be used if dynamic host names are not possible
+    private static final String JMETER_SERVER_ALIAS = ":jmeter:"; // $NON-NLS-1$
+
+    private static final int CERT_VALIDITY = JMeterUtils.getPropDefault("proxy.cert.validity",
7); // $NON-NLS-1$
+
+    // Are we running on a system that supports keytool -gencert and -ext options ?
+    private static final boolean isAtLeastJava7 = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_7);
+
     private static final String DEFAULT_PASSWORD = "password"; // $NON-NLS-1$
 
     private static final SamplerCreatorFactory factory = new SamplerCreatorFactory();
 
+    // Keys for user preferences
+    private static final String USER_PASSWORD_KEY = "proxy_cert_PASSWORD";
+
+    private static final Preferences prefs = Preferences.userNodeForPackage(Proxy.class);
+    // Note: Windows user preferences are stored relative to: HKEY_CURRENT_USER\Software\JavaSoft\Prefs
+
     // Use with SSL connection
     private OutputStream outStreamClient = null;
 
+    static enum KEYSTORE_IMPL {
+        USER_KEYSTORE,   // user-provided keystore
+        JMETER_KEYSTORE, // keystore generated by JMeter; single entry
+        DYNAMIC_KEYSTORE
+    }
+
+    private static final KEYSTORE_IMPL keystoreType;
+
     static {
         String removeList = JMeterUtils.getPropDefault(PROXY_HEADERS_REMOVE,PROXY_HEADERS_REMOVE_DEFAULT);
         headersToRemove = JOrphanUtils.split(removeList,PROXY_HEADERS_REMOVE_SEPARATOR);
         log.info("Proxy will remove the headers: "+removeList);
+        if (CERT_ALIAS != null) {
+            log.info("Proxy Server will use the specified SSL keystore with the alias: '"
+ CERT_ALIAS + "'");
+            keystoreType = KEYSTORE_IMPL.USER_KEYSTORE;
+        } else {
+            if (isAtLeastJava7) {
+                keystoreType = KEYSTORE_IMPL.DYNAMIC_KEYSTORE;
+                log.info("Java 7 detected: Proxy Server SSL Proxy will use keys that support
embedded 3rd party resources");                
+            } else {
+                keystoreType = KEYSTORE_IMPL.JMETER_KEYSTORE;
+               log.warn("Java 7 not detected: Proxy Server SSL Proxy will use keys that may
not work for embedded resources");
+            }
+        }
     }
 
     /** Socket to client. */
@@ -311,7 +350,7 @@ public class Proxy extends Thread {
             }
             try {
                 SSLContext sslcontext = SSLContext.getInstance(SSLCONTEXT_PROTOCOL);
-                sslcontext.init(getKeyManagers(CERT_ALIAS), null, null);
+                sslcontext.init(getKeyManagers(host), null, null);
                 SSLSocketFactory sslFactory = sslcontext.getSocketFactory();
                 hashHost.put(host, sslFactory);
                 log.info(port + "KeyStore for SSL loaded OK and put host in map ("+host+")");
@@ -329,14 +368,36 @@ public class Proxy extends Thread {
      * Return the key managers, wrapped if necessary to return a specific alias
      * 
      * @param serverAlias the alias to return, or null to use whatever is present
+     * @param host the target host
      * @return the key managers
      * @throws GeneralSecurityException
      * @throws IOException if the store cannot be opened or read or the alias is missing
      */
-    private KeyManager[] getKeyManagers(String serverAlias) throws GeneralSecurityException,
IOException {
+    private KeyManager[] getKeyManagers(String host) throws GeneralSecurityException, IOException
{
+        final KeyStore ks;
+        final String serverAlias;
+        String keyPass;
+        switch(keystoreType) {
+        case JMETER_KEYSTORE:
+            ks = getJMeterKeyStore(getPassword(), (String) null);
+            keyPass = getPassword(); // above call may have updated the stored password
+            serverAlias = JMETER_SERVER_ALIAS;
+            break;
+        case DYNAMIC_KEYSTORE:
+            ks = getJMeterKeyStore(getPassword(), host);
+            keyPass = getPassword(); // above call may have updated the stored password
+            serverAlias = host;
+            break;
+        case USER_KEYSTORE:
+        default: // Not really needed, but avoids complaints about non-init password strings
+            String keyStorePass = JMeterUtils.getPropDefault("proxy.cert.keystorepass", DEFAULT_PASSWORD);
// $NON-NLS-1$
+            ks = getKeyStore(keyStorePass.toCharArray());
+            keyPass = JMeterUtils.getPropDefault("proxy.cert.keypassword", DEFAULT_PASSWORD);
// $NON-NLS-1$
+            serverAlias = CERT_ALIAS;
+            break;
+        }
         KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEYMANAGERFACTORY);
-        KeyStore ks = getKeyStore(JMeterUtils.getPropDefault("proxy.cert.keystorepass", DEFAULT_PASSWORD).toCharArray());
// $NON-NLS-1$
-        kmf.init(ks, JMeterUtils.getPropDefault("proxy.cert.keypassword", DEFAULT_PASSWORD).toCharArray());
// $NON-NLS-1$
+        kmf.init(ks, keyPass.toCharArray());
         final KeyManager[] keyManagers = kmf.getKeyManagers();
         if (serverAlias == null) {
             return keyManagers;
@@ -372,6 +433,52 @@ public class Proxy extends Thread {
         }
     }
 
+    // If host == null, we are not using dynamic keys
+    private KeyStore getJMeterKeyStore(String keyStorePass, String host) throws GeneralSecurityException,
IOException {
+        final File certFile = new File(CERT_DIRECTORY, CERT_FILE);
+        final String subject = host == null ? JMETER_SERVER_ALIAS : host;
+        KeyStore keyStore = null;
+        final String canonicalPath = certFile.getCanonicalPath();
+        if (keyStorePass != null) { // Assume we have already created the store
+            try {
+                keyStore = getKeyStore(keyStorePass.toCharArray());
+            } catch (Exception e) { // store is faulty, we need to recreate it
+                log.warn(port + "Could not open expected file " + canonicalPath + " " + e.getMessage());
           
+            }
+        }
+        if (keyStore == null) { // no existing file or not valid
+            keyStorePass = RandomStringUtils.randomAscii(20);
+            setPassword(keyStorePass);
+            try {
+                if (host != null) { // i.e. Java 7
+                    log.info(port + "Creating Proxy CA in " + canonicalPath);
+                    KeyToolUtils.generateProxyCA(certFile, keyStorePass, CERT_VALIDITY);
+                    log.info(port + "Creating entry " + subject + " in " + canonicalPath);
+                    KeyToolUtils.generateHostCert(certFile, keyStorePass, subject, CERT_VALIDITY);
+                    log.info(port + "Created keystore in " + canonicalPath);
+                } else {
+                    log.info(port + "Generating standard keypair in " + canonicalPath);
+                    certFile.delete(); // Must not exist
+                    KeyToolUtils.genkeypair(certFile, JMETER_SERVER_ALIAS, keyStorePass,
CERT_VALIDITY, null, null);                    
+                }
+                keyStore = getKeyStore(keyStorePass.toCharArray()); // This should now work
+            } catch (InterruptedException e) {
+                throw new IOException("Could not create Proxy CA keystore", e);
+            }
+        }
+        // keyStorePass should not be null here; checking it avoids a possible NPE warning
below
+        if (keyStorePass != null && host != null && !keyStore.containsAlias(host))
{
+            log.info(port + "Creating entry '" + host + "' in " + canonicalPath);
+            try {
+                // Requires Java 7
+                KeyToolUtils.generateHostCert(certFile, keyStorePass, host, CERT_VALIDITY);
+                keyStore = getKeyStore(keyStorePass.toCharArray()); // reload
+            } catch (InterruptedException e) {
+                throw new IOException("Could not create entry for subject '" + host + "'",
e);
+            }            
+        }
+        return keyStore;
+    }
     /**
      * Negotiate a SSL connection.
      *
@@ -565,4 +672,13 @@ public class Proxy extends Thread {
         }
         return urlWithoutQuery;
     }
+
+    private String getPassword() {
+        return prefs.get(USER_PASSWORD_KEY, null);
+    }
+
+    private void setPassword(String password) {
+        prefs.put(USER_PASSWORD_KEY, password);        
+    }
+
 }



Mime
View raw message