Bug 52033 - Allowing multiple certificates (JKS)

git-svn-id: https://svn.apache.org/repos/asf/jakarta/jmeter/trunk@1187840 13f79535-47bb-0310-9956-ffa450edef68

Former-commit-id: 4df8794c51
This commit is contained in:
Sebastian Bazley 2011-10-23 01:50:55 +00:00
parent f5042794dc
commit c83fa55265
6 changed files with 132 additions and 44 deletions

View File

@ -78,6 +78,11 @@ xml.parser=org.apache.xerces.parsers.SAXParser
# set the value to 'false' to reset the SSL context each iteration
#https.use.cached.ssl.context=true
# Start and end index to be used with keystores with many entries
# The default is to use entry 0, i.e. the first
#https.keyStoreStartIndex=0
#https.keyStoreEndIndex=0
#---------------------------------------------------------------------------
# Look and Feel configuration
#---------------------------------------------------------------------------

View File

@ -70,6 +70,9 @@ public class JsseSSLManager extends SSLManager {
private static final int cps;
//@GuardedBy("this")
private static int last_user;
static {
log.info("Using default SSL protocol: "+DEFAULT_SSL_PROTOCOL);
log.info("SSL session context: "+(SHARED_SESSION_CONTEXT ? "shared" : "per-thread"));
@ -314,8 +317,12 @@ public class JsseSSLManager extends SSLManager {
*/
public String[] getClientAliases(String keyType, Principal[] issuers) {
log.debug("WrappedX509Manager: getClientAliases: ");
log.debug(this.store.getAlias());
return new String[] { this.store.getAlias() };
int count = this.store.getAliasCount();
String[] aliases = new String[count];
for(int i = 0; i < aliases.length; i++) {
aliases[i] = this.store.getAlias(i);
}
return aliases;
}
/**
@ -343,7 +350,7 @@ public class JsseSSLManager extends SSLManager {
*/
public X509Certificate[] getCertificateChain(String alias) {
log.debug("WrappedX509Manager: getCertificateChain(" + alias + ")");
return this.store.getCertificateChain();
return this.store.getCertificateChain(alias);
}
/**
@ -354,8 +361,9 @@ public class JsseSSLManager extends SSLManager {
* @return The PrivateKey value
*/
public PrivateKey getPrivateKey(String alias) {
log.debug("WrappedX509Manager: getPrivateKey: " + this.store.getPrivateKey());
return this.store.getPrivateKey();
PrivateKey privateKey = this.store.getPrivateKey(alias);
log.debug("WrappedX509Manager: getPrivateKey: " + privateKey);
return privateKey;
}
/**
@ -372,14 +380,28 @@ public class JsseSSLManager extends SSLManager {
* @see javax.net.ssl.X509KeyManager#chooseClientAlias(String[], Principal[], Socket)
*/
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
String alias = this.store.getAlias();
log.debug("ClientAlias: " + alias);
log.debug("keyType: " + keyType[0]);
int aliasCount = this.store.getAliasCount();
String alias = this.store.getAlias(getNextIndex(aliasCount));
if (alias == null || alias.length() == 0) {
log.debug("ClientAlias not found.");
}
return alias;
}
private int getNextIndex(int aliasCount) {
if (aliasCount == 1) {
return 0;
}
synchronized(this) {
last_user ++;
if (last_user >= aliasCount) {
last_user = 0;
}
return last_user;
}
}
/**
* Choose the server alias for the SSLServerSockets. This are not used
* in JMeter.

View File

@ -64,7 +64,7 @@ public abstract class SSLManager {
private static final Provider sslProvider = null;
/** Cache the KeyStore instance */
private JmeterKeyStore keyStore;
private volatile JmeterKeyStore keyStore;
/** Cache the TrustStore instance - null if no truststore name was provided */
private KeyStore trustStore = null;
@ -126,7 +126,9 @@ public abstract class SSLManager {
if (initStore.exists()) {
fileInputStream = new FileInputStream(initStore);
this.keyStore.load(fileInputStream, getPassword());
log.info("Keystore loaded OK from file, found alias: "+keyStore.getAlias());
if (log.isInfoEnabled()) {
log.info("Total of " + keyStore.getAliasCount() + " aliases loaded OK from keystore");
}
} else {
log.warn("Keystore file not found, loading empty keystore");
this.defaultpw = ""; // Ensure not null

View File

@ -23,21 +23,35 @@ import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import org.apache.jmeter.util.JMeterUtils;
/**
* Use this Keystore to wrap the normal KeyStore implementation.
*
*/
public class DefaultKeyStore extends JmeterKeyStore {
private X509Certificate[] certChain;
private X509Certificate[][] certChains;
private PrivateKey key;
private PrivateKey[] keys;
private String alias;
private String[] names;
private final KeyStore store;
private static final String KEY_STORE_START_INDEX = "https.keyStoreStartIndex"; // $NON-NLS-1$
private static final String KEY_STORE_END_INDEX = "https.keyStoreEndIndex"; // $NON-NLS-1$
private static final int startIndex;
private static final int endIndex;
static {
startIndex = JMeterUtils.getPropDefault(KEY_STORE_START_INDEX, 0);
endIndex = JMeterUtils.getPropDefault(KEY_STORE_END_INDEX, 0);
}
public DefaultKeyStore(String type) throws Exception {
this.store = KeyStore.getInstance(type);
}
@ -46,54 +60,96 @@ public class DefaultKeyStore extends JmeterKeyStore {
@Override
public void load(InputStream is, String pword) throws Exception {
store.load(is, pword.toCharArray());
PrivateKey _key = null;
X509Certificate[] _certChain = null;
ArrayList<String> v_names = new ArrayList<String>();
ArrayList<PrivateKey> v_keys = new ArrayList<PrivateKey>();
ArrayList<X509Certificate[]> v_certChains = new ArrayList<X509Certificate[]>();
if (null != is){ // No point checking an empty keystore
PrivateKey _key = null;
int index = 0;
Enumeration<String> aliases = store.aliases();
while (aliases.hasMoreElements()) {
this.alias = aliases.nextElement();
String alias = aliases.nextElement();
if (store.isKeyEntry(alias)) {
_key = (PrivateKey) store.getKey(alias, pword.toCharArray());
Certificate[] chain = store.getCertificateChain(alias);
_certChain = new X509Certificate[chain.length];
for (int i = 0; i < chain.length; i++) {
_certChain[i] = (X509Certificate) chain[i];
if ((index >= startIndex && index <= endIndex)) {
_key = (PrivateKey) store.getKey(alias, pword.toCharArray());
if (null == _key) {
throw new Exception("No key found for alias: " + alias); // Should not happen
}
Certificate[] chain = store.getCertificateChain(alias);
if (null == chain) {
throw new Exception("No certificate chain found for alias: " + alias);
}
v_names.add(alias);
v_keys.add(_key);
v_certChains.add((X509Certificate[]) chain);
}
break;
}
index++;
}
if (null == _key) {
throw new Exception("No key found");
}
if (null == _certChain) {
throw new Exception("No certificate chain found");
throw new Exception("No key(s) found");
}
}
this.key = _key;
this.certChain = _certChain;
/*
* Note: if is == null, the arrays will be empty
*/
int v_size = v_names.size();
this.names = new String[v_size];
this.names = v_names.toArray(names);
this.keys = new PrivateKey[v_size];
this.keys = v_keys.toArray(keys);
this.certChains = new X509Certificate[v_size][];
this.certChains = v_certChains.toArray(certChains);
}
/** {@inheritDoc} */
@Override
public final X509Certificate[] getCertificateChain() {
return this.certChain;
public final X509Certificate[] getCertificateChain(String alias) {
int entry = findAlias(alias);
if (entry >=0) {
return this.certChains[entry];
}
return null;
}
/** {@inheritDoc} */
@Override
public final PrivateKey getPrivateKey() {
return this.key;
public final PrivateKey getPrivateKey(String alias) {
int entry = findAlias(alias);
if (entry >=0) {
return this.keys[entry];
}
return null;
}
/** {@inheritDoc} */
@Override
public final String getAlias() {
return this.alias;
public final String getAlias(int index) {
int length = this.names.length;
if (length == 0 && index == 0) { // i.e. is == null
return null;
}
if (index >= length || index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
return this.names[index];
}
@Override
public int getAliasCount() {
return this.names.length;
}
private int findAlias(String alias) {
for(int i = 0; i < names.length; i++) {
if (alias.equals(names[i])){
return i;
}
}
return -1;
}
}

View File

@ -34,16 +34,18 @@ public abstract class JmeterKeyStore {
public abstract void load(InputStream is, String password) throws Exception;
/**
* Get the ordered certificate chain.
* Get the ordered certificate chain for a specific alias.
*/
public abstract X509Certificate[] getCertificateChain();
public abstract X509Certificate[] getCertificateChain(String alias);
public abstract String getAlias();
public abstract int getAliasCount();
public abstract String getAlias(int index);
/**
* Return the private Key
* Return the private Key for a specific alias
*/
public abstract PrivateKey getPrivateKey();
public abstract PrivateKey getPrivateKey(String alias);
public static final JmeterKeyStore getInstance(String type) throws Exception {
// JAVA 1.4 now handles all keystore types, so just use default

View File

@ -137,6 +137,7 @@ Mirror server now uses default port 8081, was 8080 before 2.5.1.
<h3>HTTP Samplers</h3>
<ul>
<li>Bug 51981 - Better support for file: protocol in HTTP sampler</li>
<li>Bug 52033 - Allowing multiple certificates (JKS)</li>
</ul>
<h3>Other samplers</h3>