Author: fschumacher
Date: Sun Mar 24 13:13:40 2019
New Revision: 1856146
URL: http://svn.apache.org/viewvc?rev=1856146&view=rev
Log:
Enable PKCS11 keystores for usage with KeyStore Manager
Based on a patch by Clifford Harms (clifford.harms at gmail.com).
Bugzilla Id: 62863
Modified:
jmeter/trunk/src/core/org/apache/jmeter/util/SSLManager.java
jmeter/trunk/src/core/org/apache/jmeter/util/keystore/JmeterKeyStore.java
jmeter/trunk/xdocs/changes.xml
jmeter/trunk/xdocs/usermanual/component_reference.xml
Modified: jmeter/trunk/src/core/org/apache/jmeter/util/SSLManager.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/util/SSLManager.java?rev=1856146&r1=1856145&r2=1856146&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/util/SSLManager.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/util/SSLManager.java Sun Mar 24 13:13:40 2019
@@ -26,10 +26,13 @@ import java.net.HttpURLConnection;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
+import java.util.Arrays;
import java.util.Locale;
import javax.swing.JOptionPane;
+import javax.swing.JPasswordField;
+import org.apache.commons.lang3.Validate;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.keystore.JmeterKeyStore;
import org.slf4j.Logger;
@@ -123,10 +126,16 @@ public abstract class SSLManager {
throw new IllegalArgumentException("Could not create keystore: "+e.getMessage(),
e);
}
- try {
- File initStore = new File(fileName);
+ try {
- if (fileName.length() >0 && initStore.exists()) {
+ // The string 'NONE' is used for the keystore location when using PKCS11
+ // https://docs.oracle.com/javase/8/docs/technotes/guides/security/p11guide.html#JSSE
+ if ("NONE".equalsIgnoreCase(fileName)) {
+ this.keyStore.load(null, Validate.notNull(getPassword(), "Password should
not be null"));
+ log.info("Total of {} aliases loaded OK from PKCS11", Integer.valueOf(keyStore.getAliasCount()));
+ } else {
+ File initStore = new File(fileName);
+ if (fileName.length() > 0 && initStore.exists()) {
try (InputStream fis = new FileInputStream(initStore);
InputStream fileInputStream = new BufferedInputStream(fis)) {
this.keyStore.load(fileInputStream, getPassword());
@@ -136,18 +145,19 @@ public abstract class SSLManager {
Integer.valueOf(keyStore.getAliasCount()));
}
}
- } else {
+ } else {
log.warn("Keystore file not found, loading empty keystore");
this.defaultpw = ""; // Ensure not null
this.keyStore.load(null, "");
- }
- } catch (Exception e) {
- log.error("Problem loading keystore: {}", e.getMessage(), e);
- }
-
- if (log.isDebugEnabled()) {
- log.debug("JmeterKeyStore type: {}", this.keyStore.getClass());
- }
+ }
+ }
+ } catch (Exception e) {
+ log.error("Problem loading keystore: {}", e.getMessage(), e);
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("JmeterKeyStore type: {}", this.keyStore.getClass());
+ }
}
return this.keyStore;
@@ -156,26 +166,31 @@ public abstract class SSLManager {
/*
* The password can be defined as a property; this dialogue is provided to allow it
* to be entered at run-time.
- *
- * However, this does not gain much, as the dialogue does not (yet) support hidden input
...
- *
- */
+ */
private String getPassword() {
String password = this.defaultpw;
if (null == password) {
final GuiPackage guiInstance = GuiPackage.getInstance();
if (guiInstance != null) {
synchronized (this) { // TODO is sync really needed?
- this.defaultpw = JOptionPane.showInputDialog(
- guiInstance.getMainFrame(),
- JMeterUtils.getResString("ssl_pass_prompt"), // $NON-NLS-1$
- JMeterUtils.getResString("ssl_pass_title"), // $NON-NLS-1$
- JOptionPane.QUESTION_MESSAGE);
- System.setProperty(KEY_STORE_PASSWORD, this.defaultpw);
- password = this.defaultpw;
- }
+ JPasswordField pwf = new JPasswordField(64);
+ pwf.setEchoChar('*');
+ int choice = JOptionPane.showConfirmDialog(
+ guiInstance.getMainFrame(),
+ pwf,
+ JMeterUtils.getResString("ssl_pass_prompt"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.PLAIN_MESSAGE);
+ if (choice == JOptionPane.OK_OPTION) {
+ char[] pwchars = pwf.getPassword();
+ this.defaultpw = new String(pwchars);
+ Arrays.fill(pwchars, '*');
+ }
+ System.setProperty(KEY_STORE_PASSWORD, this.defaultpw);
+ password = this.defaultpw;
+ }
} else {
- log.warn("No password provided, and no GUI present so cannot prompt");
+ log.warn("No password provided, and no GUI present so cannot prompt");
}
}
return password;
Modified: jmeter/trunk/src/core/org/apache/jmeter/util/keystore/JmeterKeyStore.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/util/keystore/JmeterKeyStore.java?rev=1856146&r1=1856145&r2=1856146&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/util/keystore/JmeterKeyStore.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/util/keystore/JmeterKeyStore.java Sun Mar 24 13:13:40
2019
@@ -113,57 +113,73 @@ public final class JmeterKeyStore {
*/
public void load(InputStream is, String pword)
throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException,
UnrecoverableKeyException {
- char[] pw = pword==null ? null : pword.toCharArray();
+ char[] pw = toCharArrayOrNull(pword);
store.load(is, pw);
-
+
List<String> aliasesList = new ArrayList<>();
this.privateKeyByAlias = new HashMap<>();
this.certsByAlias = new HashMap<>();
- if (null != is){ // No point checking an empty keystore
- PrivateKey privateKey = null;
- int index = 0;
- Enumeration<String> aliases = store.aliases();
- while (aliases.hasMoreElements()) {
- String alias = aliases.nextElement();
- if (store.isKeyEntry(alias)) {
- if (index >= startIndex && (endIndex== -1 || index <= endIndex))
{
- privateKey = (PrivateKey) store.getKey(alias, pw);
- if (null == privateKey) {
- throw new IOException("No key found for alias: " + alias); //
Should not happen
- }
- Certificate[] chain = store.getCertificateChain(alias);
- if (null == chain) {
- throw new IOException("No certificate chain found for alias:
" + alias);
- }
- aliasesList.add(alias);
- X509Certificate[] x509certs = new X509Certificate[chain.length];
- for (int i = 0; i < x509certs.length; i++) {
- x509certs[i] = (X509Certificate)chain[i];
- }
-
- privateKeyByAlias.put(alias, privateKey);
- certsByAlias.put(alias, x509certs);
- }
- index++;
+ PrivateKey privateKey = null;
+ int index = 0;
+ Enumeration<String> aliases = store.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ if (store.isKeyEntry(alias)) {
+ if (isIndexInConfiguredRange(index)) {
+ privateKey = validateNotNull(
+ (PrivateKey) store.getKey(alias, pw),
+ "No key found for alias: " + alias);
+ Certificate[] chain = validateNotNull(
+ store.getCertificateChain(alias),
+ "No certificate chain found for alias" + alias);
+ aliasesList.add(alias);
+ privateKeyByAlias.put(alias, privateKey);
+ certsByAlias.put(alias, toX509Certificates(chain));
}
+ index++;
+ }
+ }
+
+ if (is != null) { // only check for keys, if we were given a file as inputstream
+ validateNotNull(privateKey, "No key(s) found");
+ if (endIndex != -1 && index <= endIndex - startIndex && log.isWarnEnabled())
{
+ log.warn("Did not find as much aliases as configured in indexes Start={},
end={}, found={}", startIndex,
+ endIndex, certsByAlias.size());
}
-
- if (null == privateKey) {
- throw new IOException("No key(s) found");
- }
- if (endIndex != -1 && index <= endIndex-startIndex && log.isWarnEnabled())
{
- log.warn("Did not find as much aliases as configured in indexes Start={},
end={}, found={}",
- startIndex, endIndex, certsByAlias.size());
- }
- }
-
+ }
+
/*
- * Note: if is == null, the arrays will be empty
+ * Note: if is == null and no pkcs11 store is configured, the arrays will be empty
*/
this.names = aliasesList.toArray(new String[aliasesList.size()]);
}
+ private <T> T validateNotNull(T object, String message) throws IOException {
+ if (null == object) {
+ throw new IOException(message);
+ }
+ return object;
+ }
+
+ private X509Certificate[] toX509Certificates(Certificate[] chain) {
+ X509Certificate[] x509certs = new X509Certificate[chain.length];
+ for (int i = 0; i < x509certs.length; i++) {
+ x509certs[i] = (X509Certificate) chain[i];
+ }
+ return x509certs;
+ }
+
+ private boolean isIndexInConfiguredRange(int index) {
+ return index >= startIndex && (endIndex == -1 || index <= endIndex);
+ }
+
+ private char[] toCharArrayOrNull(String pword) {
+ if (pword == null) {
+ return null; // NOSONAR the api used requires null for "no password used"
+ }
+ return pword.toCharArray();
+ }
/**
* Get the ordered certificate chain for a specific alias.
Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1856146&r1=1856145&r2=1856146&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Sun Mar 24 13:13:40 2019
@@ -97,6 +97,7 @@ to view the last major behaviors with th
<h3>Timers, Assertions, Config, Pre- & Post-Processors</h3>
<ul>
+ <li><bug>62863</bug>Enable PKCS11 keystores for usage with KeyStore Manager.
Based on patch by Clifford Harms (clifford.harms at gmail.com).</li>
</ul>
<h3>Functions</h3>
@@ -169,6 +170,7 @@ to view the last major behaviors with th
<p>We thank all contributors mentioned in bug and improvement sections above:
</p>
<ul>
+ <li>Clifford Harms (clifford.harms at gmail.com)</li>
</ul>
<p>We also thank bug reporters who helped us improve JMeter.</p>
<ul>
Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1856146&r1=1856145&r2=1856146&view=diff
==============================================================================
--- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jmeter/trunk/xdocs/usermanual/component_reference.xml Sun Mar 24 13:13:40 2019
@@ -4080,6 +4080,8 @@ This component is typically used in HTTP
</ul>
</li>
</ol>
+<p>To use PKCS11 as the source for the store, you need to set <code>javax.net.ssl.keyStoreType</code>
to <code>PKCS11</code>
+and <code>javax.net.ssl.keyStore</code> to <code>NONE</code>.</p>
</description>
<properties>
|