View | Details | Raw Unified | Return to bug 54977
Collapse All | Expand All

(-)xdocs/usermanual/component_reference.xml (-3 / +12 lines)
Lines 3826-3832 Link Here
3826
</component>
3826
</component>
3827
3827
3828
3828
3829
<component name="Keystore Configuration" index="&sect-num;.4.9"  width="530" height="171" screenshot="keystore_config.png">
3829
<component name="Keystore Configuration" index="&sect-num;.4.9"  width="441" height="189" screenshot="keystore_config.png">
3830
<description><p>The Keystore Config Element lets you configure how Keystore will be loaded and which keys it will use.
3830
<description><p>The Keystore Config Element lets you configure how Keystore will be loaded and which keys it will use.
3831
This component is typically used in HTTPS scenarios where you don't want to take into account keystore initialization into account in response time.</p>
3831
This component is typically used in HTTPS scenarios where you don't want to take into account keystore initialization into account in response time.</p>
3832
<p>To use this element, you need to setup first a Java Key Store with the client certificates you want to test, to do that:
3832
<p>To use this element, you need to setup first a Java Key Store with the client certificates you want to test, to do that:
Lines 3843-3853 Link Here
3843
</p>
3843
</p>
3844
</description>
3844
</description>
3845
3845
3846
preload.shortDescription=Preload Keystore before test. Setting is to true is usually the best option.
3847
startIndex.displayName=Alias Start index (0-based)
3848
startIndex.shortDescription=First index of Alias in Keystore
3849
endIndex.displayName=Alias End index (0-based)
3850
endIndex.shortDescription=Last index of Alias in Keystore. When using Variable name ensure it is large enough so that all keys are loaded at startup.
3851
clientCertAliasVarName.displayName=Variable name holding certificate alias
3852
clientCertAliasVarName.shortDescription=Variable name that will contain the alias to use for Cert authentication. Var content can come from CSV Data Set.
3853
3846
<properties>
3854
<properties>
3847
  <property name="Name" required="No">Descriptive name for this element that is shown in the tree. </property>
3855
  <property name="Name" required="No">Descriptive name for this element that is shown in the tree. </property>
3848
  <property name="Preload" required="Yes">Wether or not to preload Keystore.</property>
3856
  <property name="Preload" required="Yes">Wether or not to preload Keystore. Setting is to true is usually the best option.</property>
3857
  <property name="Variable name holding certificate alias" required="False">Variable name that will contain the alias to use for authentication by client certificate. Variable value will be filled from CSV Data Set for example. In the screenshot, "certificat_ssl" will also be a variable in CSV Data Set.</property>
3849
  <property name="Alias Start Index" required="Yes">The index of the first key to use in Keystore, 0-based.</property>
3858
  <property name="Alias Start Index" required="Yes">The index of the first key to use in Keystore, 0-based.</property>
3850
  <property name="Alias End Index" required="Yes">The index of the last key to use in Keystore, 0-based.</property>
3859
  <property name="Alias End Index" required="Yes">The index of the last key to use in Keystore, 0-based. When using "Variable name holding certificate alias" ensure it is large enough so that all keys are loaded at startup.</property>
3851
</properties>
3860
</properties>
3852
<note>
3861
<note>
3853
To make JMeter use more than one certificate you need to ensure that:
3862
To make JMeter use more than one certificate you need to ensure that:
(-)src/core/org/apache/jmeter/util/keystore/JmeterKeyStore.java (-40 / +40 lines)
Lines 26-32 Link Here
26
import java.security.cert.X509Certificate;
26
import java.security.cert.X509Certificate;
27
import java.util.ArrayList;
27
import java.util.ArrayList;
28
import java.util.Enumeration;
28
import java.util.Enumeration;
29
import java.util.HashMap;
30
import java.util.Map;
29
31
32
import org.apache.commons.lang3.StringUtils;
33
import org.apache.jmeter.threads.JMeterContextService;
30
import org.apache.jorphan.logging.LoggingManager;
34
import org.apache.jorphan.logging.LoggingManager;
31
import org.apache.log.Logger;
35
import org.apache.log.Logger;
32
36
Lines 41-61 Link Here
41
    private final KeyStore store;
45
    private final KeyStore store;
42
    private final int startIndex;
46
    private final int startIndex;
43
    private final int endIndex;
47
    private final int endIndex;
48
    private String clientCertAliasVarName;
44
49
45
    private X509Certificate[][] certChains;
46
    private PrivateKey[] keys;
47
    private String[] names = new String[0]; // default empty array to prevent NPEs
50
    private String[] names = new String[0]; // default empty array to prevent NPEs
48
51
    private Map<String, PrivateKey> privateKeyByAlias = new HashMap<String, PrivateKey>();
52
    private Map<String, X509Certificate[]> certsByAlias = new HashMap<String, X509Certificate[]>();
53
    
49
    //@GuardedBy("this")
54
    //@GuardedBy("this")
50
    private int last_user;
55
    private int last_user;
51
56
52
    private JmeterKeyStore(String type, int startIndex, int endIndex) throws Exception {
57
58
    private JmeterKeyStore(String type, int startIndex, int endIndex, String clientCertAliasVarName) throws Exception {
53
        if (startIndex < 0 || endIndex < 0 || endIndex < startIndex) {
59
        if (startIndex < 0 || endIndex < 0 || endIndex < startIndex) {
54
            throw new IllegalArgumentException("Invalid index(es). Start="+startIndex+", end="+endIndex);
60
            throw new IllegalArgumentException("Invalid index(es). Start="+startIndex+", end="+endIndex);
55
        }
61
        }
56
        this.store = KeyStore.getInstance(type);
62
        this.store = KeyStore.getInstance(type);
57
        this.startIndex = startIndex;
63
        this.startIndex = startIndex;
58
        this.endIndex = endIndex;
64
        this.endIndex = endIndex;
65
        this.clientCertAliasVarName = clientCertAliasVarName;
59
    }
66
    }
60
67
61
    /**
68
    /**
Lines 66-74 Link Here
66
        store.load(is, pw);
73
        store.load(is, pw);
67
    
74
    
68
        ArrayList<String> v_names = new ArrayList<String>();
75
        ArrayList<String> v_names = new ArrayList<String>();
69
        ArrayList<PrivateKey> v_keys = new ArrayList<PrivateKey>();
76
        this.privateKeyByAlias = new HashMap<String, PrivateKey>();
70
        ArrayList<X509Certificate[]> v_certChains = new ArrayList<X509Certificate[]>();
77
        this.certsByAlias = new HashMap<String, X509Certificate[]>();
71
    
78
72
        if (null != is){ // No point checking an empty keystore
79
        if (null != is){ // No point checking an empty keystore
73
            PrivateKey _key = null;
80
            PrivateKey _key = null;
74
            int index = 0;
81
            int index = 0;
Lines 86-97 Link Here
86
                            throw new Exception("No certificate chain found for alias: " + alias);
93
                            throw new Exception("No certificate chain found for alias: " + alias);
87
                        }
94
                        }
88
                        v_names.add(alias);
95
                        v_names.add(alias);
89
                        v_keys.add(_key);
90
                        X509Certificate[] x509certs = new X509Certificate[chain.length];
96
                        X509Certificate[] x509certs = new X509Certificate[chain.length];
91
                        for (int i = 0; i < x509certs.length; i++) {
97
                        for (int i = 0; i < x509certs.length; i++) {
92
                            x509certs[i] = (X509Certificate)chain[i];
98
                            x509certs[i] = (X509Certificate)chain[i];
93
                        }
99
                        }
94
                        v_certChains.add(x509certs);
100
101
                        privateKeyByAlias.put(alias, _key);
102
                        certsByAlias.put(alias, x509certs);
95
                    }
103
                    }
96
                    index++;
104
                    index++;
97
                }
105
                }
Lines 101-123 Link Here
101
                throw new Exception("No key(s) found");
109
                throw new Exception("No key(s) found");
102
            }
110
            }
103
            if (index <= endIndex-startIndex) {
111
            if (index <= endIndex-startIndex) {
104
                LOG.warn("Did not find all requested aliases. Start="+startIndex+", end="+endIndex+", found="+v_certChains.size());
112
                LOG.warn("Did not find all requested aliases. Start="+startIndex+", end="+endIndex+", found="+certsByAlias.size());
105
            }
113
            }
106
        }
114
        }
107
    
115
    
108
        /*
116
        /*
109
         * Note: if is == null, the arrays will be empty
117
         * Note: if is == null, the arrays will be empty
110
         */
118
         */
111
        int v_size = v_names.size();
119
        this.names = v_names.toArray(new String[v_names.size()]);
112
    
113
        this.names = new String[v_size];
114
        this.names = v_names.toArray(names);
115
    
116
        this.keys = new PrivateKey[v_size];
117
        this.keys = v_keys.toArray(keys);
118
    
119
        this.certChains = new X509Certificate[v_size][];
120
        this.certChains = v_certChains.toArray(certChains);
121
    }
120
    }
122
121
123
122
Lines 125-136 Link Here
125
     * Get the ordered certificate chain for a specific alias.
124
     * Get the ordered certificate chain for a specific alias.
126
     */
125
     */
127
    public X509Certificate[] getCertificateChain(String alias) {
126
    public X509Certificate[] getCertificateChain(String alias) {
128
        int entry = findAlias(alias);
127
        X509Certificate[] result = this.certsByAlias.get(alias);
129
        if (entry >=0) {
128
        if(result != null) {
130
            return this.certChains[entry];
129
            return result;
131
        }
130
        }
132
        // API expects null not empty array, see http://docs.oracle.com/javase/6/docs/api/javax/net/ssl/X509KeyManager.html
131
        // API expects null not empty array, see http://docs.oracle.com/javase/6/docs/api/javax/net/ssl/X509KeyManager.html
133
        return null;
132
        throw new IllegalArgumentException("No certificate found for alias:'"+alias+"'");
134
    }
133
    }
135
134
136
    /**
135
    /**
Lines 138-143 Link Here
138
     * @return the next or only alias.
137
     * @return the next or only alias.
139
     */
138
     */
140
    public String getAlias() {
139
    public String getAlias() {
140
        if(!StringUtils.isEmpty(clientCertAliasVarName)) {
141
            // We return even if result is null
142
            String aliasName = JMeterContextService.getContext().getVariables().get(clientCertAliasVarName);
143
            if(StringUtils.isEmpty(aliasName)) {
144
                LOG.error("No var called '"+clientCertAliasVarName+"' found");
145
                throw new IllegalArgumentException("No var called '"+clientCertAliasVarName+"' found");
146
            }
147
            return aliasName;
148
        }
141
        int length = this.names.length;
149
        int length = this.names.length;
142
        if (length == 0) { // i.e. is == null
150
        if (length == 0) { // i.e. is == null
143
            return null;
151
            return null;
Lines 164-174 Link Here
164
     * Return the private Key for a specific alias
172
     * Return the private Key for a specific alias
165
     */
173
     */
166
    public PrivateKey getPrivateKey(String alias) {
174
    public PrivateKey getPrivateKey(String alias) {
167
        int entry = findAlias(alias);
175
        PrivateKey pk = this.privateKeyByAlias.get(alias);
168
        if (entry >=0) {
176
        if(pk != null) {
169
            return this.keys[entry];
177
            return pk;
170
        }
178
        }
171
        return null;
179
        throw new IllegalArgumentException("No PrivateKey found for alias:'"+alias+"'");
172
    }
180
    }
173
181
174
    /**
182
    /**
Lines 176-186 Link Here
176
     * @param type store type (e.g. JKS)
184
     * @param type store type (e.g. JKS)
177
     * @param startIndex first index (from 0)
185
     * @param startIndex first index (from 0)
178
     * @param endIndex last index (to count -1)
186
     * @param endIndex last index (to count -1)
187
     * @param clientCertAliasVarName 
179
     * @return the keystore
188
     * @return the keystore
180
     * @throws Exception
189
     * @throws Exception
181
     */
190
     */
182
    public static JmeterKeyStore getInstance(String type, int startIndex, int endIndex) throws Exception {
191
    public static JmeterKeyStore getInstance(String type, int startIndex, int endIndex, String clientCertAliasVarName) throws Exception {
183
        return new JmeterKeyStore(type, startIndex, endIndex);
192
        return new JmeterKeyStore(type, startIndex, endIndex, clientCertAliasVarName);
184
    }
193
    }
185
194
186
    /**
195
    /**
Lines 190-205 Link Here
190
     * @throws Exception
199
     * @throws Exception
191
     */
200
     */
192
    public static JmeterKeyStore getInstance(String type) throws Exception {
201
    public static JmeterKeyStore getInstance(String type) throws Exception {
193
        return new JmeterKeyStore(type, 0, 0);
202
        return getInstance(type, 0, 0, null);
194
    }
195
    
196
    private int findAlias(String alias) {
197
        for(int i = 0; i < names.length; i++) {
198
            if (alias.equals(names[i])){
199
                return i;
200
            }
201
        }
202
        return -1;
203
    }
203
    }
204
204
205
    /**
205
    /**
(-)src/core/org/apache/jmeter/util/JsseSSLManager.java (-3 / +5 lines)
Lines 389-398 Link Here
389
         */
389
         */
390
        @Override
390
        @Override
391
        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
391
        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
392
            log.debug("keyType: " + keyType[0]);
392
            if(log.isDebugEnabled()) {
393
                log.debug("keyType: " + keyType[0]);
394
            }
393
            String alias = this.store.getAlias();
395
            String alias = this.store.getAlias();
394
            if (alias == null || alias.length() == 0) {
396
            if(log.isDebugEnabled()) {
395
                log.debug("ClientAlias not found.");
397
                log.debug("Client alias:'"+alias+"'");
396
            }
398
            }
397
            return alias;
399
            return alias;
398
        }
400
        }
(-)src/core/org/apache/jmeter/util/SSLManager.java (-3 / +7 lines)
Lines 81-86 Link Here
81
81
82
    private int keystoreAliasEndIndex;
82
    private int keystoreAliasEndIndex;
83
83
84
    private String clientCertAliasVarName;
85
84
    /**
86
    /**
85
     * Resets the SSLManager so that we can create a new one with a new keystore
87
     * Resets the SSLManager so that we can create a new one with a new keystore
86
     */
88
     */
Lines 104-117 Link Here
104
     * not set, this method will prompt you to enter it. Unfortunately, there is
106
     * not set, this method will prompt you to enter it. Unfortunately, there is
105
     * no PasswordEntryField available from JOptionPane.
107
     * no PasswordEntryField available from JOptionPane.
106
     */
108
     */
107
    protected JmeterKeyStore getKeyStore() {
109
    protected synchronized JmeterKeyStore getKeyStore() {
108
        if (null == this.keyStore) {
110
        if (null == this.keyStore) {
109
            String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE,""); // empty if not provided
111
            String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE,""); // empty if not provided
110
            String fileType = System.getProperty(JAVAX_NET_SSL_KEY_STORE_TYPE, // use the system property to determine the type
112
            String fileType = System.getProperty(JAVAX_NET_SSL_KEY_STORE_TYPE, // use the system property to determine the type
111
                    fileName.toLowerCase(Locale.ENGLISH).endsWith(".p12") ? PKCS12 : "JKS"); // otherwise use the name
113
                    fileName.toLowerCase(Locale.ENGLISH).endsWith(".p12") ? PKCS12 : "JKS"); // otherwise use the name
112
            log.info("JmeterKeyStore Location: " + fileName + " type " + fileType);
114
            log.info("JmeterKeyStore Location: " + fileName + " type " + fileType);
113
            try {
115
            try {
114
                this.keyStore = JmeterKeyStore.getInstance(fileType, keystoreAliasStartIndex, keystoreAliasEndIndex);
116
                this.keyStore = JmeterKeyStore.getInstance(fileType, keystoreAliasStartIndex, keystoreAliasEndIndex, clientCertAliasVarName);
115
                log.info("KeyStore created OK");
117
                log.info("KeyStore created OK");
116
            } catch (Exception e) {
118
            } catch (Exception e) {
117
                this.keyStore = null;
119
                this.keyStore = null;
Lines 275-284 Link Here
275
     * @param preload 
277
     * @param preload 
276
     * @param startIndex 
278
     * @param startIndex 
277
     * @param endIndex 
279
     * @param endIndex 
280
     * @param clientCertAliasVarName 
278
     */
281
     */
279
    public void configureKeystore(boolean preload, int startIndex, int endIndex) {
282
    public void configureKeystore(boolean preload, int startIndex, int endIndex, String clientCertAliasVarName) {
280
        this.keystoreAliasStartIndex = startIndex;
283
        this.keystoreAliasStartIndex = startIndex;
281
        this.keystoreAliasEndIndex = endIndex;
284
        this.keystoreAliasEndIndex = endIndex;
285
        this.clientCertAliasVarName = clientCertAliasVarName;
282
        if(preload) {
286
        if(preload) {
283
            keyStore = getKeyStore();
287
            keyStore = getKeyStore();
284
        }
288
        }
(-)src/components/org/apache/jmeter/config/KeystoreConfig.java (-2 / +19 lines)
Lines 41-46 Link Here
41
    private String startIndex;
41
    private String startIndex;
42
    private String endIndex;
42
    private String endIndex;
43
    private String preload;
43
    private String preload;
44
    private String clientCertAliasVarName;
44
    
45
    
45
    public KeystoreConfig() {
46
    public KeystoreConfig() {
46
        super();
47
        super();
Lines 90-100 Link Here
90
            throw new JMeterStopTestException("Keystore Config error : Alias start index must be lower than Alias end index");
91
            throw new JMeterStopTestException("Keystore Config error : Alias start index must be lower than Alias end index");
91
        }
92
        }
92
        log.info("Configuring Keystore with (preload:"+preload+", startIndex:"+
93
        log.info("Configuring Keystore with (preload:"+preload+", startIndex:"+
93
                startIndexAsInt+", endIndex:"+endIndexAsInt+")");
94
                startIndexAsInt+", endIndex:"+endIndexAsInt+
95
                ", clientCertAliasVarName:" + clientCertAliasVarName +")");
94
96
95
        SSLManager.getInstance().configureKeystore(Boolean.parseBoolean(preload),
97
        SSLManager.getInstance().configureKeystore(Boolean.parseBoolean(preload),
96
                startIndexAsInt, 
98
                startIndexAsInt, 
97
                endIndexAsInt);
99
                endIndexAsInt,
100
                clientCertAliasVarName);
98
    }
101
    }
99
102
100
    /**
103
    /**
Lines 138-141 Link Here
138
    public void setPreload(String preload) {
141
    public void setPreload(String preload) {
139
        this.preload = preload;
142
        this.preload = preload;
140
    }
143
    }
144
145
    /**
146
     * @return the clientCertAliasVarName
147
     */
148
    public String getClientCertAliasVarName() {
149
        return clientCertAliasVarName;
150
    }
151
152
    /**
153
     * @param clientCertAliasVarName the clientCertAliasVarName to set
154
     */
155
    public void setClientCertAliasVarName(String clientCertAliasVarName) {
156
        this.clientCertAliasVarName = clientCertAliasVarName;
157
    }
141
}
158
}
(-)src/components/org/apache/jmeter/config/KeystoreConfigResources.properties (-2 / +4 lines)
Lines 18-25 Link Here
18
aliases.displayName=Aliases selection configuration
18
aliases.displayName=Aliases selection configuration
19
# fields
19
# fields
20
preload.displayName=Preload
20
preload.displayName=Preload
21
preload.shortDescription=Preload Keystore before test
21
preload.shortDescription=Preload Keystore before test. Setting is to true is usually the best option.
22
startIndex.displayName=Alias Start index (0-based)
22
startIndex.displayName=Alias Start index (0-based)
23
startIndex.shortDescription=First index of Alias in Keystore
23
startIndex.shortDescription=First index of Alias in Keystore
24
endIndex.displayName=Alias End index (0-based)
24
endIndex.displayName=Alias End index (0-based)
25
endIndex.shortDescription=Last index of Alias in Keystore
25
endIndex.shortDescription=Last index of Alias in Keystore. When using Variable name ensure it is large enough so that all keys are loaded at startup.
26
clientCertAliasVarName.displayName=Variable name holding certificate alias
27
clientCertAliasVarName.shortDescription=Variable name that will contain the alias to use for Cert authentication. Var content can come from CSV Data Set.
(-)src/components/org/apache/jmeter/config/KeystoreConfigResources_fr.properties (+2 lines)
Lines 23-25 Link Here
23
startIndex.shortDescription=Num\u00E9ro d'index du premier alias de cl\u00E9 dans le coffre de cl\u00E9s (JKS)
23
startIndex.shortDescription=Num\u00E9ro d'index du premier alias de cl\u00E9 dans le coffre de cl\u00E9s (JKS)
24
endIndex.displayName=Num\u00E9ro d'index derni\u00E8re cl\u00E9 (d\u00E9marre \u00E0 0)
24
endIndex.displayName=Num\u00E9ro d'index derni\u00E8re cl\u00E9 (d\u00E9marre \u00E0 0)
25
endIndex.shortDescription=Num\u00E9ro d'index du dernier alias de cl\u00E9 dans le coffre de cl\u00E9s (JKS)
25
endIndex.shortDescription=Num\u00E9ro d'index du dernier alias de cl\u00E9 dans le coffre de cl\u00E9s (JKS)
26
clientCertAliasVarName.displayName=Nom variable contenant l'alias
27
clientCertAliasVarName.shortDescription=Nom de la variable qui contiendra l'alias \u00E0 utiliser pour l'authentification par Certificat. La variable peut \u00E8tre aliment\u00E9e depuis un CSV Data Set.
(-)src/components/org/apache/jmeter/config/KeystoreConfigBeanInfo.java (-1 / +6 lines)
Lines 30-35 Link Here
30
    private static final String ALIASES_GROUP = "aliases";
30
    private static final String ALIASES_GROUP = "aliases";
31
    private static final String ALIAS_END_INDEX = "endIndex";
31
    private static final String ALIAS_END_INDEX = "endIndex";
32
    private static final String ALIAS_START_INDEX = "startIndex";
32
    private static final String ALIAS_START_INDEX = "startIndex";
33
    private static final String CLIENT_CERT_ALIAS_VAR_NAME = "clientCertAliasVarName";
33
    private static final String PRELOAD = "preload";
34
    private static final String PRELOAD = "preload";
34
35
35
    /**
36
    /**
Lines 39-45 Link Here
39
        super(KeystoreConfig.class);
40
        super(KeystoreConfig.class);
40
41
41
        createPropertyGroup(ALIASES_GROUP, new String[] { 
42
        createPropertyGroup(ALIASES_GROUP, new String[] { 
42
                PRELOAD, ALIAS_START_INDEX, ALIAS_END_INDEX });
43
                PRELOAD, CLIENT_CERT_ALIAS_VAR_NAME, ALIAS_START_INDEX, ALIAS_END_INDEX });
43
44
44
        PropertyDescriptor p = property(PRELOAD);
45
        PropertyDescriptor p = property(PRELOAD);
45
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
46
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
Lines 48-53 Link Here
48
        p.setValue(NOT_OTHER, Boolean.TRUE);
49
        p.setValue(NOT_OTHER, Boolean.TRUE);
49
        p.setValue(TAGS, new String[]{"True", "False"}); // $NON-NLS-1$ $NON-NLS-2$
50
        p.setValue(TAGS, new String[]{"True", "False"}); // $NON-NLS-1$ $NON-NLS-2$
50
51
52
        p = property(CLIENT_CERT_ALIAS_VAR_NAME);
53
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
54
        p.setValue(DEFAULT, ""); // $NON-NLS-1$
55
51
        p = property(ALIAS_START_INDEX);
56
        p = property(ALIAS_START_INDEX);
52
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
57
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
53
        p.setValue(DEFAULT, ""); // $NON-NLS-1$
58
        p.setValue(DEFAULT, ""); // $NON-NLS-1$

Return to bug 54977