902 lines
34 KiB
Java
902 lines
34 KiB
Java
/* KeyStoreBenchmark.java
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.FileNotFoundException;
|
|
import java.nio.file.Files;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.Provider;
|
|
import java.security.Security;
|
|
import java.security.SecureRandom;
|
|
import java.security.KeyStore;
|
|
import java.security.PrivateKey;
|
|
import java.security.KeyFactory;
|
|
import java.security.KeyStoreException;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.CertificateFactory;
|
|
import java.security.cert.CertificateException;
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
import java.security.spec.InvalidKeySpecException;
|
|
import java.util.Enumeration;
|
|
import javax.crypto.KeyGenerator;
|
|
import javax.crypto.SecretKey;
|
|
|
|
import com.wolfssl.provider.jce.WolfCryptProvider;
|
|
|
|
public class KeyStoreBenchmark {
|
|
|
|
/* Default test parameters */
|
|
private static final int WARMUP_ITERATIONS = 10;
|
|
private static final int BENCHMARK_ITERATIONS = 1000;
|
|
private static final int MIN_TEST_TIME_MS = 5000; /* 5 seconds minimum */
|
|
|
|
/* KeyStore configuration */
|
|
private static String storePass = "wolfsslbenchmarkpassword";
|
|
private static String storeType = "WKS";
|
|
private static String providerName = "wolfJCE";
|
|
private static boolean benchmarkAll = true;
|
|
private static String currentDisplayName = null;
|
|
private static String customIterationCount = "20000";
|
|
|
|
/* Test files and data */
|
|
private static String tmpKeyStoreFile = "benchmark_keystore_tmp";
|
|
private static String serverCertRsaDer = "../../certs/server-cert.der";
|
|
private static String serverRsaPkcs8Der = "../../certs/server-keyPkcs8.der";
|
|
private static String serverCertEccDer = "../../certs/server-ecc.der";
|
|
private static String serverEccPkcs8Der = "../../certs/ecc-keyPkcs8.der";
|
|
|
|
/* Benchmark results storage */
|
|
private static class BenchmarkResult {
|
|
String operation;
|
|
String provider;
|
|
String type;
|
|
double throughput;
|
|
String units;
|
|
|
|
BenchmarkResult(String operation, String provider, String type,
|
|
double throughput, String units) {
|
|
this.operation = operation;
|
|
this.provider = provider;
|
|
this.type = type;
|
|
this.throughput = throughput;
|
|
this.units = units;
|
|
}
|
|
}
|
|
|
|
private static java.util.List<BenchmarkResult> results =
|
|
new java.util.ArrayList<>();
|
|
|
|
/**
|
|
* Helper method for string repetition (Java 8 compatibility)
|
|
*/
|
|
private static String repeat(String str, int count) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = 0; i < count; i++) {
|
|
sb.append(str);
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Print usage and exit
|
|
*/
|
|
private static void printUsage() {
|
|
System.out.println("KeyStore Benchmark Tool");
|
|
System.out.println("Usage: java KeyStoreBenchmark [options]");
|
|
System.out.println("");
|
|
System.out.println("Options:");
|
|
System.out.println(" -provider <name> Use specified provider (default: benchmark all)");
|
|
System.out.println(" -type <type> Use specified KeyStore type (default: benchmark all)");
|
|
System.out.println(" -iterations <num> PBKDF2 iteration count for WKS low-iteration test (default: 20000)");
|
|
System.out.println(" -help Show this help message");
|
|
System.out.println("");
|
|
System.out.println("Examples:");
|
|
System.out.println(" java KeyStoreBenchmark (benchmark all types)");
|
|
System.out.println(" java KeyStoreBenchmark -provider SunJCE -type JKS");
|
|
System.out.println(" java KeyStoreBenchmark -iterations 10000 (use 10k for WKS low test)");
|
|
System.out.println(" java KeyStoreBenchmark -type PKCS12");
|
|
}
|
|
|
|
/**
|
|
* Parse command line arguments
|
|
*/
|
|
private static void parseArgs(String[] args) {
|
|
for (int i = 0; i < args.length; i++) {
|
|
if (args[i].equals("-provider") && i + 1 < args.length) {
|
|
providerName = args[++i];
|
|
benchmarkAll = false;
|
|
} else if (args[i].equals("-type") && i + 1 < args.length) {
|
|
storeType = args[++i];
|
|
benchmarkAll = false;
|
|
} else if (args[i].equals("-iterations") && i + 1 < args.length) {
|
|
try {
|
|
int iterCount = Integer.parseInt(args[++i]);
|
|
if (iterCount < 1000) {
|
|
System.err.println("Warning: Iteration count " + iterCount +
|
|
" is very low, minimum recommended is 1000");
|
|
}
|
|
customIterationCount = String.valueOf(iterCount);
|
|
} catch (NumberFormatException e) {
|
|
System.err.println("Invalid iteration count: " + args[i]);
|
|
System.exit(1);
|
|
}
|
|
} else if (args[i].equals("-help") || args[i].equals("--help")) {
|
|
printUsage();
|
|
System.exit(0);
|
|
} else {
|
|
System.err.println("Unknown argument: " + args[i]);
|
|
printUsage();
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup provider for testing
|
|
*/
|
|
private static void setupProvider() {
|
|
if (providerName.equals("wolfJCE")) {
|
|
Security.insertProviderAt(new WolfCryptProvider(), 1);
|
|
}
|
|
/* For other providers like SunJCE, JKS, etc., they should already
|
|
* be available in the JVM */
|
|
}
|
|
|
|
/**
|
|
* Create and return PrivateKey object from file path to DER-encoded
|
|
* private key file.
|
|
*/
|
|
private static PrivateKey DerFileToPrivateKey(String derFilePath,
|
|
String alg) throws Exception {
|
|
|
|
byte[] fileBytes = Files.readAllBytes(new File(derFilePath).toPath());
|
|
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(fileBytes);
|
|
KeyFactory kf = KeyFactory.getInstance(alg);
|
|
return kf.generatePrivate(spec);
|
|
}
|
|
|
|
/**
|
|
* Read in and convert certificate file to Certificate object.
|
|
*/
|
|
private static Certificate CertFileToCertificate(String certPath)
|
|
throws Exception {
|
|
|
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
|
return cf.generateCertificate(new FileInputStream(certPath));
|
|
}
|
|
|
|
/**
|
|
* Create a test KeyStore with various entries for benchmarking
|
|
*/
|
|
private static KeyStore createTestKeyStore() throws Exception {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
|
|
/* Add RSA certificate entry */
|
|
Certificate rsaCert = CertFileToCertificate(serverCertRsaDer);
|
|
store.setCertificateEntry("rsaCert", rsaCert);
|
|
|
|
/* Add RSA private key entry */
|
|
PrivateKey rsaKey = DerFileToPrivateKey(serverRsaPkcs8Der, "RSA");
|
|
store.setKeyEntry("rsaKey", rsaKey, storePass.toCharArray(),
|
|
new Certificate[] { rsaCert });
|
|
|
|
/* Add ECC certificate entry */
|
|
Certificate eccCert = CertFileToCertificate(serverCertEccDer);
|
|
store.setCertificateEntry("eccCert", eccCert);
|
|
|
|
/* Add ECC private key entry */
|
|
PrivateKey eccKey = DerFileToPrivateKey(serverEccPkcs8Der, "EC");
|
|
store.setKeyEntry("eccKey", eccKey, storePass.toCharArray(),
|
|
new Certificate[] { eccCert });
|
|
|
|
/* Add secret key entries (only for types that support them) */
|
|
if (storeType.equals("WKS") || storeType.equals("PKCS12")) {
|
|
KeyGenerator aesGen = KeyGenerator.getInstance("AES");
|
|
aesGen.init(256);
|
|
SecretKey aesKey = aesGen.generateKey();
|
|
store.setKeyEntry("aesKey", aesKey, storePass.toCharArray(), null);
|
|
|
|
/* Add multiple AES keys for bulk operations */
|
|
for (int i = 0; i < 10; i++) {
|
|
SecretKey key = aesGen.generateKey();
|
|
store.setKeyEntry("aesKey" + i, key, storePass.toCharArray(),
|
|
null);
|
|
}
|
|
}
|
|
|
|
return store;
|
|
}
|
|
|
|
/**
|
|
* Benchmark KeyStore creation (load with null)
|
|
*/
|
|
private static void benchmarkKeyStoreCreation() {
|
|
System.out.println("Benchmarking KeyStore creation...");
|
|
|
|
long startTime = System.nanoTime();
|
|
int operations = 0;
|
|
double elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
try {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
} catch (Exception e) {
|
|
System.err.println("Warmup failed: " + e.getMessage());
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
try {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
operations++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} catch (Exception e) {
|
|
System.err.println("Benchmark failed: " + e.getMessage());
|
|
return;
|
|
}
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double opsPerSec = operations / elapsedTime;
|
|
System.out.printf(" KeyStore creation: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", operations, elapsedTime, opsPerSec);
|
|
|
|
String displayName;
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("KeyStore Creation", displayName,
|
|
storeType, opsPerSec, "ops/sec"));
|
|
}
|
|
|
|
/**
|
|
* Benchmark KeyStore entry insertion
|
|
*/
|
|
private static void benchmarkEntryInsertion() {
|
|
System.out.println("Benchmarking KeyStore entry insertion...");
|
|
|
|
try {
|
|
/* Prepare test data */
|
|
Certificate rsaCert = CertFileToCertificate(serverCertRsaDer);
|
|
PrivateKey rsaKey = DerFileToPrivateKey(serverRsaPkcs8Der, "RSA");
|
|
|
|
KeyGenerator aesGen = null;
|
|
boolean supportsSecretKeys = storeType.equals("WKS") ||
|
|
storeType.equals("PKCS12");
|
|
if (supportsSecretKeys) {
|
|
aesGen = KeyGenerator.getInstance("AES");
|
|
aesGen.init(256);
|
|
}
|
|
|
|
/* Benchmark certificate insertion */
|
|
long startTime = System.nanoTime();
|
|
int certOps = 0;
|
|
double elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
store.setCertificateEntry("testCert", rsaCert);
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
store.setCertificateEntry("testCert" + certOps, rsaCert);
|
|
certOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double certOpsPerSec = certOps / elapsedTime;
|
|
System.out.printf(" Certificate insertion: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", certOps, elapsedTime,
|
|
certOpsPerSec);
|
|
|
|
String displayName;
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("Certificate Insertion",
|
|
displayName, storeType,
|
|
certOpsPerSec, "ops/sec"));
|
|
|
|
/* Benchmark private key insertion */
|
|
int keyOps = 0;
|
|
startTime = System.nanoTime();
|
|
elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
store.setKeyEntry("testKey", rsaKey, storePass.toCharArray(),
|
|
new Certificate[] { rsaCert });
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
store.setKeyEntry("testKey" + keyOps, rsaKey,
|
|
storePass.toCharArray(),
|
|
new Certificate[] { rsaCert });
|
|
keyOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double keyOpsPerSec = keyOps / elapsedTime;
|
|
System.out.printf(" Private key insertion: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", keyOps, elapsedTime,
|
|
keyOpsPerSec);
|
|
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("Private Key Insertion",
|
|
displayName, storeType,
|
|
keyOpsPerSec, "ops/sec"));
|
|
|
|
/* Benchmark secret key insertion (if supported) */
|
|
if (supportsSecretKeys) {
|
|
int secretOps = 0;
|
|
startTime = System.nanoTime();
|
|
elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
SecretKey aesKey = aesGen.generateKey();
|
|
store.setKeyEntry("testSecret", aesKey,
|
|
storePass.toCharArray(), null);
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
KeyStore store = KeyStore.getInstance(storeType);
|
|
store.load(null, storePass.toCharArray());
|
|
SecretKey aesKey = aesGen.generateKey();
|
|
store.setKeyEntry("testSecret" + secretOps, aesKey,
|
|
storePass.toCharArray(), null);
|
|
secretOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) /
|
|
1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double secretOpsPerSec = secretOps / elapsedTime;
|
|
System.out.printf(" Secret key insertion: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", secretOps, elapsedTime,
|
|
secretOpsPerSec);
|
|
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("Secret Key Insertion",
|
|
displayName, storeType,
|
|
secretOpsPerSec, "ops/sec"));
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Entry insertion benchmark failed: " +
|
|
e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Benchmark KeyStore entry retrieval
|
|
*/
|
|
private static void benchmarkEntryRetrieval() {
|
|
System.out.println("Benchmarking KeyStore entry retrieval...");
|
|
|
|
try {
|
|
/* Create test KeyStore */
|
|
KeyStore store = createTestKeyStore();
|
|
|
|
/* Benchmark certificate retrieval */
|
|
long startTime = System.nanoTime();
|
|
int certOps = 0;
|
|
double elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
store.getCertificate("rsaCert");
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
store.getCertificate("rsaCert");
|
|
certOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double certOpsPerSec = certOps / elapsedTime;
|
|
System.out.printf(" Certificate retrieval: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", certOps, elapsedTime,
|
|
certOpsPerSec);
|
|
|
|
String displayName;
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("Certificate Retrieval",
|
|
displayName, storeType,
|
|
certOpsPerSec, "ops/sec"));
|
|
|
|
/* Benchmark private key retrieval */
|
|
int keyOps = 0;
|
|
startTime = System.nanoTime();
|
|
elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
store.getKey("rsaKey", storePass.toCharArray());
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
store.getKey("rsaKey", storePass.toCharArray());
|
|
keyOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double keyOpsPerSec = keyOps / elapsedTime;
|
|
System.out.printf(" Private key retrieval: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", keyOps, elapsedTime,
|
|
keyOpsPerSec);
|
|
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("Private Key Retrieval",
|
|
displayName, storeType,
|
|
keyOpsPerSec, "ops/sec"));
|
|
|
|
/* Benchmark alias enumeration */
|
|
int enumOps = 0;
|
|
startTime = System.nanoTime();
|
|
elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
Enumeration<String> aliases = store.aliases();
|
|
while (aliases.hasMoreElements()) {
|
|
aliases.nextElement();
|
|
}
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
Enumeration<String> aliases = store.aliases();
|
|
while (aliases.hasMoreElements()) {
|
|
aliases.nextElement();
|
|
}
|
|
enumOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double enumOpsPerSec = enumOps / elapsedTime;
|
|
System.out.printf(" Alias enumeration: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", enumOps, elapsedTime,
|
|
enumOpsPerSec);
|
|
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("Alias Enumeration", displayName,
|
|
storeType, enumOpsPerSec, "ops/sec"));
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Entry retrieval benchmark failed: " +
|
|
e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Benchmark KeyStore store/load operations
|
|
*/
|
|
private static void benchmarkStoreLoad() {
|
|
System.out.println("Benchmarking KeyStore store/load operations...");
|
|
|
|
try {
|
|
/* Create test KeyStore */
|
|
KeyStore store = createTestKeyStore();
|
|
|
|
/* Benchmark store operation */
|
|
long startTime = System.nanoTime();
|
|
int storeOps = 0;
|
|
double elapsedTime = 0;
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
FileOutputStream fos = new FileOutputStream(tmpKeyStoreFile +
|
|
"_warmup");
|
|
store.store(fos, storePass.toCharArray());
|
|
fos.close();
|
|
new File(tmpKeyStoreFile + "_warmup").delete();
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
FileOutputStream fos = new FileOutputStream(tmpKeyStoreFile +
|
|
storeOps);
|
|
store.store(fos, storePass.toCharArray());
|
|
fos.close();
|
|
storeOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double storeOpsPerSec = storeOps / elapsedTime;
|
|
System.out.printf(" KeyStore store: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", storeOps, elapsedTime,
|
|
storeOpsPerSec);
|
|
|
|
String displayName;
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("KeyStore Store", displayName,
|
|
storeType, storeOpsPerSec, "ops/sec"));
|
|
|
|
/* Benchmark load operation */
|
|
int loadOps = 0;
|
|
startTime = System.nanoTime();
|
|
elapsedTime = 0;
|
|
|
|
/* Use the first stored file for loading */
|
|
String testFile = tmpKeyStoreFile + "0";
|
|
|
|
/* Warmup */
|
|
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
|
|
KeyStore loadStore = KeyStore.getInstance(storeType);
|
|
FileInputStream fis = new FileInputStream(testFile);
|
|
loadStore.load(fis, storePass.toCharArray());
|
|
fis.close();
|
|
}
|
|
|
|
/* Benchmark */
|
|
startTime = System.nanoTime();
|
|
do {
|
|
KeyStore loadStore = KeyStore.getInstance(storeType);
|
|
FileInputStream fis = new FileInputStream(testFile);
|
|
loadStore.load(fis, storePass.toCharArray());
|
|
fis.close();
|
|
loadOps++;
|
|
elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0;
|
|
} while (elapsedTime < (MIN_TEST_TIME_MS / 1000.0));
|
|
|
|
double loadOpsPerSec = loadOps / elapsedTime;
|
|
System.out.printf(" KeyStore load: %d ops in %.3f sec, " +
|
|
"%.2f ops/sec%n", loadOps, elapsedTime,
|
|
loadOpsPerSec);
|
|
|
|
if (currentDisplayName != null) {
|
|
displayName = currentDisplayName;
|
|
} else {
|
|
displayName = (providerName + " " + storeType);
|
|
}
|
|
results.add(new BenchmarkResult("KeyStore Load", displayName,
|
|
storeType, loadOpsPerSec, "ops/sec"));
|
|
|
|
/* Cleanup temp files */
|
|
for (int i = 0; i < storeOps; i++) {
|
|
new File(tmpKeyStoreFile + i).delete();
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Store/load benchmark failed: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Benchmark configuration for a specific provider/type combination
|
|
*/
|
|
private static class BenchmarkConfig {
|
|
String provider;
|
|
String type;
|
|
String iterationCount;
|
|
String displayName;
|
|
|
|
BenchmarkConfig(String provider, String type) {
|
|
|
|
this.provider = provider;
|
|
this.type = type;
|
|
this.iterationCount = null;
|
|
this.displayName = provider + " " + type;
|
|
}
|
|
|
|
BenchmarkConfig(String provider, String type, String iterationCount,
|
|
String displayName) {
|
|
|
|
this.provider = provider;
|
|
this.type = type;
|
|
this.iterationCount = iterationCount;
|
|
this.displayName = displayName;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run benchmarks for a specific provider/type configuration
|
|
*/
|
|
private static void runBenchmarkForConfig(BenchmarkConfig config) {
|
|
String originalProvider = providerName;
|
|
String originalType = storeType;
|
|
|
|
providerName = config.provider;
|
|
storeType = config.type;
|
|
currentDisplayName = config.displayName;
|
|
|
|
System.out.println("\n" + repeat("=", 60));
|
|
System.out.println("BENCHMARKING: " + config.displayName);
|
|
if (config.iterationCount != null) {
|
|
System.out.println("PBKDF2 Iteration Count: " +
|
|
config.iterationCount);
|
|
}
|
|
System.out.println(repeat("=", 60));
|
|
|
|
try {
|
|
setupProvider();
|
|
|
|
/* Set PBKDF2 iteration count if specified */
|
|
if (config.iterationCount != null && config.provider.equals("wolfJCE")) {
|
|
Security.setProperty(
|
|
"wolfjce.wks.iterationCount", config.iterationCount);
|
|
System.out.println("Set wolfjce.wks.iterationCount to " +
|
|
Security.getProperty("wolfjce.wks.iterationCount"));
|
|
}
|
|
|
|
/* Verify KeyStore type is available */
|
|
KeyStore.getInstance(storeType);
|
|
|
|
/* Run benchmarks */
|
|
benchmarkKeyStoreCreation();
|
|
benchmarkEntryInsertion();
|
|
benchmarkEntryRetrieval();
|
|
benchmarkStoreLoad();
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Benchmark failed for " + config.displayName +
|
|
": " + e.getMessage());
|
|
}
|
|
|
|
/* Restore original values */
|
|
providerName = originalProvider;
|
|
storeType = originalType;
|
|
}
|
|
|
|
/**
|
|
* Run benchmarks for all supported KeyStore types
|
|
*/
|
|
private static void runAllBenchmarks() {
|
|
java.util.List<BenchmarkConfig> configs =
|
|
new java.util.ArrayList<>();
|
|
|
|
/* Add configurations to test */
|
|
configs.add(
|
|
new BenchmarkConfig("wolfJCE", "WKS", null, "wolfJCE WKS (210k)"));
|
|
|
|
/* Create display name for custom iteration count */
|
|
String iterDisplayName;
|
|
int iterCount = Integer.parseInt(customIterationCount);
|
|
if (iterCount >= 1000) {
|
|
iterDisplayName = String.format("wolfJCE WKS (%dk)",
|
|
iterCount / 1000);
|
|
} else {
|
|
iterDisplayName = String.format("wolfJCE WKS (%d)", iterCount);
|
|
}
|
|
|
|
configs.add(new BenchmarkConfig("wolfJCE", "WKS", customIterationCount,
|
|
iterDisplayName));
|
|
configs.add(new BenchmarkConfig("SunJCE", "JKS"));
|
|
configs.add(new BenchmarkConfig("SunJCE", "PKCS12"));
|
|
|
|
for (BenchmarkConfig config : configs) {
|
|
runBenchmarkForConfig(config);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print comparison table of all benchmark results
|
|
*/
|
|
private static void printComparisonTable() {
|
|
System.out.println("\n" + repeat("=", 105));
|
|
System.out.println("KEYSTORE PERFORMANCE COMPARISON TABLE");
|
|
System.out.println(repeat("=", 105));
|
|
|
|
/* Group results by operation */
|
|
java.util.Map<String, java.util.Map<String, BenchmarkResult>>
|
|
operationMap = new java.util.HashMap<>();
|
|
|
|
for (BenchmarkResult result : results) {
|
|
String key = result.provider;
|
|
operationMap.computeIfAbsent(result.operation,
|
|
k -> new java.util.HashMap<>()).put(key, result);
|
|
}
|
|
|
|
/* Print header */
|
|
System.out.printf("%-25s %15s %15s %15s %15s%n",
|
|
"Operation", "WKS (210k)", "WKS (20k)", "JKS",
|
|
"PKCS12");
|
|
System.out.println(repeat("-", 105));
|
|
|
|
/* Print each operation's results across all providers/types */
|
|
String[] operations = {
|
|
"KeyStore Creation",
|
|
"Certificate Insertion",
|
|
"Private Key Insertion",
|
|
"Secret Key Insertion",
|
|
"Certificate Retrieval",
|
|
"Private Key Retrieval",
|
|
"Alias Enumeration",
|
|
"KeyStore Store",
|
|
"KeyStore Load"
|
|
};
|
|
|
|
for (String operation : operations) {
|
|
java.util.Map<String, BenchmarkResult> opResults =
|
|
operationMap.get(operation);
|
|
if (opResults != null) {
|
|
System.out.printf("%-25s", operation);
|
|
|
|
String[] providerKeys = {
|
|
"wolfJCE WKS (210k)", "wolfJCE WKS (20k)", "SunJCE JKS",
|
|
"SunJCE PKCS12"};
|
|
for (String provider : providerKeys) {
|
|
BenchmarkResult result = opResults.get(provider);
|
|
if (result != null) {
|
|
if (result.throughput >= 1000000) {
|
|
System.out.printf(" %14.2fM",
|
|
result.throughput / 1000000);
|
|
} else if (result.throughput >= 1000) {
|
|
System.out.printf(" %14.2fK",
|
|
result.throughput / 1000);
|
|
} else {
|
|
System.out.printf(" %15.2f", result.throughput);
|
|
}
|
|
} else {
|
|
System.out.printf(" %15s", "N/A");
|
|
}
|
|
}
|
|
System.out.println();
|
|
}
|
|
}
|
|
|
|
System.out.println(repeat("-", 105));
|
|
System.out.println("Units: ops/sec (K=thousands, M=millions)");
|
|
System.out.println(
|
|
"N/A = Not Available/Supported for this KeyStore type");
|
|
System.out.println(repeat("=", 105));
|
|
}
|
|
|
|
/**
|
|
* Print benchmark results summary
|
|
*/
|
|
private static void printResults() {
|
|
System.out.println("\n" + repeat("=", 70));
|
|
System.out.println("BENCHMARK RESULTS SUMMARY");
|
|
System.out.println(repeat("=", 70));
|
|
System.out.printf("Provider: %s, KeyStore Type: %s%n", providerName,
|
|
storeType);
|
|
System.out.println(repeat("-", 70));
|
|
System.out.printf("%-25s %15s %15s%n", "Operation", "Throughput",
|
|
"Units");
|
|
System.out.println(repeat("-", 70));
|
|
|
|
for (BenchmarkResult result : results) {
|
|
System.out.printf("%-25s %15.2f %15s%n", result.operation,
|
|
result.throughput, result.units);
|
|
}
|
|
System.out.println(repeat("=", 70));
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
try {
|
|
/* Parse command line arguments */
|
|
parseArgs(args);
|
|
|
|
System.out.println("KeyStore Benchmark Tool");
|
|
System.out.println("Minimum test time per operation: " +
|
|
MIN_TEST_TIME_MS + "ms");
|
|
|
|
if (benchmarkAll) {
|
|
System.out.println("Benchmarking all KeyStore types:");
|
|
System.out.println(
|
|
" - WKS with 210k PBKDF2 iterations (default)");
|
|
System.out.println(" - WKS with 20k PBKDF2 iterations");
|
|
System.out.println(" - JKS and PKCS12 from SunJCE");
|
|
System.out.println(repeat("=", 60));
|
|
|
|
/* Run benchmarks for all types */
|
|
runAllBenchmarks();
|
|
|
|
/* Print comparison table */
|
|
printComparisonTable();
|
|
|
|
} else {
|
|
System.out.println("Provider: " + providerName);
|
|
System.out.println("KeyStore Type: " + storeType);
|
|
System.out.println(repeat("=", 51));
|
|
|
|
/* Setup provider */
|
|
setupProvider();
|
|
|
|
/* Verify KeyStore type is available */
|
|
try {
|
|
KeyStore.getInstance(storeType);
|
|
} catch (KeyStoreException e) {
|
|
System.err.println("KeyStore type '" + storeType +
|
|
"' not available with provider '" +
|
|
providerName + "'");
|
|
System.exit(1);
|
|
}
|
|
|
|
/* Run benchmarks */
|
|
benchmarkKeyStoreCreation();
|
|
benchmarkEntryInsertion();
|
|
benchmarkEntryRetrieval();
|
|
benchmarkStoreLoad();
|
|
|
|
/* Print results */
|
|
printResults();
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Benchmark failed: " + e.getMessage());
|
|
e.printStackTrace();
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|