removed tests for pki qbft and add deprecation notice (#6979)

* removed tests for pki qbft and add deprecation notice

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

* remove more PKI and CMS creation utils from tests

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/7059/head 24.4.0-RC3
Sally MacFarlane 7 months ago committed by GitHub
parent e4df70a350
commit 12723ace68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 8
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java
  3. 27
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java
  4. 4
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java
  5. 64
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  6. 196
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/pki/PkiKeystoreConfigurationFactory.java
  7. 1
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java
  8. 29
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/pki/ParameterizedPkiQbftTestBase.java
  9. 130
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/pki/PkiQbftAcceptanceTest.java
  10. 120
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/pki/PkiQbftAcceptanceTestParameterization.java
  11. 162
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/blockcreation/PkiQbftBlockCreatorTest.java
  12. 85
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/pki/DefaultKeyStoreWrapperProviderTest.java
  13. 75
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/pki/PkiBlockCreationConfigurationProviderTest.java
  14. 85
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/pki/PkiQbftBlockHashingTest.java
  15. 243
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/pki/PkiQbftExtraDataCodecTest.java
  16. 202
      pki/src/test/java/org/hyperledger/besu/pki/cms/CmsCreationAndValidationTest.java
  17. 243
      pki/src/test/java/org/hyperledger/besu/pki/cms/CmsTestKeystores.java
  18. 156
      pki/src/test/java/org/hyperledger/besu/pki/keystore/BaseKeyStoreFileWrapperTest.java
  19. 81
      pki/src/test/java/org/hyperledger/besu/pki/keystore/CryptoTestUtil.java
  20. 119
      pki/src/test/java/org/hyperledger/besu/pki/keystore/HardwareKeyStoreFileWrapperTest.java
  21. 82
      pki/src/test/java/org/hyperledger/besu/pki/keystore/HardwareKeyStoreWrapperTest.java
  22. 80
      pki/src/test/java/org/hyperledger/besu/pki/keystore/SoftwareKeyStoreFileWrapperTest.java
  23. 198
      pki/src/test/java/org/hyperledger/besu/pki/keystore/SoftwareKeyStoreWrapperTest.java
  24. BIN
      pki/src/test/resources/keystore/partner1client1/keys.p12
  25. BIN
      pki/src/test/resources/keystore/partner1client1/keystore.jks
  26. 6
      pki/src/test/resources/keystore/partner1client1/nss.cfg
  27. BIN
      pki/src/test/resources/keystore/partner1client1/truststore.jks

@ -9,6 +9,7 @@
### Upcoming Breaking Changes
- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version.
- PKI-backed QBFT will be removed in a future version of Besu. Other forms of QBFT will remain unchanged.
### Deprecations

@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.NodeConfiguration;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationProvider;
@ -132,7 +131,6 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
private final List<String> staticNodes;
private boolean isDnsEnabled = false;
private Optional<Integer> exitCode = Optional.empty();
private Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration = Optional.empty();
private final boolean isStrictTxReplayProtectionEnabled;
private final Map<String, String> environment;
@ -169,7 +167,6 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
final Optional<PrivacyParameters> privacyParameters,
final List<String> runCommand,
final Optional<KeyPair> keyPair,
final Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration,
final boolean isStrictTxReplayProtectionEnabled,
final Map<String, String> environment)
throws IOException {
@ -230,7 +227,6 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
this.staticNodes = staticNodes;
this.isDnsEnabled = isDnsEnabled;
privacyParameters.ifPresent(this::setPrivacyParameters);
this.pkiKeyStoreConfiguration = pkiKeyStoreConfiguration;
this.environment = environment;
LOG.info("Created BesuNode {}", this);
}
@ -763,10 +759,6 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
return runCommand;
}
public Optional<PkiKeyStoreConfiguration> getPkiKeyStoreConfiguration() {
return pkiKeyStoreConfiguration;
}
public boolean isStrictTxReplayProtectionEnabled() {
return isStrictTxReplayProtectionEnabled;
}

@ -377,33 +377,6 @@ public class ProcessBesuNodeRunner implements BesuNodeRunner {
permissioningConfiguration.getNodeSmartContractInterfaceVersion()));
});
node.getPkiKeyStoreConfiguration()
.ifPresent(
pkiConfig -> {
params.add("--Xpki-block-creation-enabled");
params.add("--Xpki-block-creation-keystore-certificate-alias");
params.add(pkiConfig.getCertificateAlias());
params.add("--Xpki-block-creation-keystore-type");
params.add(pkiConfig.getKeyStoreType());
params.add("--Xpki-block-creation-keystore-file");
params.add(pkiConfig.getKeyStorePath().toAbsolutePath().toString());
params.add("--Xpki-block-creation-keystore-password-file");
params.add(pkiConfig.getKeyStorePasswordPath().toAbsolutePath().toString());
params.add("--Xpki-block-creation-truststore-type");
params.add(pkiConfig.getTrustStoreType());
params.add("--Xpki-block-creation-truststore-file");
params.add(pkiConfig.getTrustStorePath().toAbsolutePath().toString());
params.add("--Xpki-block-creation-truststore-password-file");
params.add(pkiConfig.getTrustStorePasswordPath().toAbsolutePath().toString());
});
params.addAll(node.getExtraCLIOptions());
params.add("--key-value-storage");

@ -21,7 +21,6 @@ import org.hyperledger.besu.RunnerBuilder;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfigurationProvider;
import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.controller.BesuControllerBuilder;
import org.hyperledger.besu.crypto.KeyPairUtil;
@ -253,9 +252,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
.isRevertReasonEnabled(node.isRevertReasonEnabled())
.storageProvider(storageProvider)
.gasLimitCalculator(GasLimitCalculator.constant())
.pkiBlockCreationConfiguration(
node.getPkiKeyStoreConfiguration()
.map(pkiConfig -> new PkiBlockCreationConfigurationProvider().load(pkiConfig)))
.evmConfiguration(EvmConfiguration.DEFAULT)
.maxPeers(maxPeers)
.maxRemotelyInitiatedPeers(15)

@ -39,7 +39,6 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.Node;
import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.pki.PkiKeystoreConfigurationFactory;
import java.io.File;
import java.io.IOException;
@ -57,8 +56,6 @@ import io.vertx.core.Vertx;
public class BesuNodeFactory {
private final NodeConfigurationFactory node = new NodeConfigurationFactory();
private final PkiKeystoreConfigurationFactory pkiKeystoreConfigurationFactory =
new PkiKeystoreConfigurationFactory();
public BesuNode create(final BesuNodeConfiguration config) throws IOException {
return new BesuNode(
@ -94,7 +91,6 @@ public class BesuNodeFactory {
config.getPrivacyParameters(),
config.getRunCommand(),
config.getKeyPair(),
config.getPkiKeyStoreConfiguration(),
config.isStrictTxReplayProtectionEnabled(),
config.getEnvironment());
}
@ -517,31 +513,6 @@ public class BesuNodeFactory {
.build());
}
public BesuNode createPkiQbftJKSNode(final String name) throws IOException {
return createPkiQbftNode(KeyStoreWrapper.KEYSTORE_TYPE_JKS, name);
}
public BesuNode createPkiQbftPKCS11Node(final String name) throws IOException {
return createPkiQbftNode(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, name);
}
public BesuNode createPkiQbftPKCS12Node(final String name) throws IOException {
return createPkiQbftNode(KeyStoreWrapper.KEYSTORE_TYPE_PKCS12, name);
}
public BesuNode createPkiQbftNode(final String type, final String name) throws IOException {
return create(
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithQbftEnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(GenesisConfigurationFactory::createQbftGenesisConfig)
.pkiBlockCreationEnabled(pkiKeystoreConfigurationFactory.createPkiConfig(type, name))
.build());
}
public BesuNode createCustomGenesisNode(
final String name, final String genesisPath, final boolean canBeBootnode) throws IOException {
return createCustomGenesisNode(name, genesisPath, canBeBootnode, false);
@ -699,41 +670,6 @@ public class BesuNodeFactory {
.build());
}
public BesuNode createPkiQbftJKSNodeWithValidators(final String name, final String... validators)
throws IOException {
return createPkiQbftNodeWithValidators(KeyStoreWrapper.KEYSTORE_TYPE_JKS, name, validators);
}
public BesuNode createPkiQbftPKCS11NodeWithValidators(
final String name, final String... validators) throws IOException {
return createPkiQbftNodeWithValidators(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, name, validators);
}
public BesuNode createPkiQbftPKCS12NodeWithValidators(
final String name, final String... validators) throws IOException {
return createPkiQbftNodeWithValidators(KeyStoreWrapper.KEYSTORE_TYPE_PKCS12, name, validators);
}
public BesuNode createPkiQbftNodeWithValidators(
final String type, final String name, final String... validators) throws IOException {
return create(
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithQbftEnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.pkiBlockCreationEnabled(pkiKeystoreConfigurationFactory.createPkiConfig(type, name))
.genesisConfigProvider(
nodes ->
node.createGenesisConfigForValidators(
asList(validators),
nodes,
GenesisConfigurationFactory::createQbftGenesisConfig))
.build());
}
public BesuNode createNodeWithStaticNodes(final String name, final List<Node> staticNodes)
throws IOException {

@ -1,196 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.node.configuration.pki;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.createKeyPair;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.createSelfSignedCertificate;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.issueCertificate;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import org.hyperledger.besu.pki.util.TestCertificateUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.UUID;
public class PkiKeystoreConfigurationFactory {
/*
PKCS11 config files
*/
final String NSSCONFIG_PATH_STRING = "/pki-certs/%s/nss.cfg";
final String NSSPIN_PATH_STRING = "/pki-certs/%s/nsspin.txt";
final String TRUSTSTORE_PATH_STRING = "/pki-certs/%s/truststore.p12";
final String CRL_PATH_STRING = "/pki-certs/crl/crl.pem";
/*
Software keystore config
*/
public static final String KEYSTORE_DEFAULT_PASSWORD = "password";
private KeyPair caKeyPair;
private X509Certificate caCertificate;
private Path trustStoreFile;
private Path passwordFile;
public PkiKeyStoreConfiguration createPkiConfig(final String type, final String name) {
if (KeyStoreWrapper.KEYSTORE_TYPE_PKCS11.equals(type)) {
return createPKCS11PkiConfig(name);
} else {
return createSoftwareKeyStorePkiConfig(type, name);
}
}
private PkiKeyStoreConfiguration createPKCS11PkiConfig(final String name) {
final PkiKeyStoreConfiguration.Builder pkiKeyStoreConfigBuilder =
new PkiKeyStoreConfiguration.Builder();
try {
pkiKeyStoreConfigBuilder
.withKeyStoreType(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11)
.withKeyStorePath(
PKCS11Utils.initNSSConfigFile(
readResourceAsPath(String.format(NSSCONFIG_PATH_STRING, name))))
.withKeyStorePasswordPath(readResourceAsPath(String.format(NSSPIN_PATH_STRING, name)))
.withTrustStoreType(KeyStoreWrapper.KEYSTORE_TYPE_PKCS12)
.withTrustStorePath(readResourceAsPath(String.format(TRUSTSTORE_PATH_STRING, name)))
.withTrustStorePasswordPath(readResourceAsPath(String.format(NSSPIN_PATH_STRING, name)))
.withCrlFilePath(readResourceAsPath(CRL_PATH_STRING))
.withCertificateAlias(name);
} catch (Exception e) {
throw new RuntimeException(e);
}
return pkiKeyStoreConfigBuilder.build();
}
private PkiKeyStoreConfiguration createSoftwareKeyStorePkiConfig(
final String type, final String name) {
PkiKeyStoreConfiguration.Builder pkiKeyStoreConfigBuilder =
new PkiKeyStoreConfiguration.Builder();
pkiKeyStoreConfigBuilder.withTrustStoreType(type);
pkiKeyStoreConfigBuilder.withTrustStorePath(createTrustStore(type));
pkiKeyStoreConfigBuilder.withTrustStorePasswordPath(passwordFile);
pkiKeyStoreConfigBuilder.withKeyStoreType(type);
pkiKeyStoreConfigBuilder.withKeyStorePath(createKeyStore(type, name));
pkiKeyStoreConfigBuilder.withKeyStorePasswordPath(passwordFile);
pkiKeyStoreConfigBuilder.withCertificateAlias(name);
return pkiKeyStoreConfigBuilder.build();
}
private Path createTrustStore(final String type) {
// Only create the truststore if this is the first time this method is being called
if (caKeyPair == null) {
try {
caKeyPair = createKeyPair(TestCertificateUtils.Algorithm.RSA);
caCertificate = createSelfSignedCertificate("ca", notBefore(), notAfter(), caKeyPair);
final KeyStore truststore = KeyStore.getInstance(type);
truststore.load(null, null);
truststore.setCertificateEntry("ca", caCertificate);
final String uniqueId = UUID.randomUUID().toString();
trustStoreFile = writeKeyStoreFile(truststore, "truststore", uniqueId);
passwordFile = writePasswordFile(KEYSTORE_DEFAULT_PASSWORD, "password", uniqueId);
} catch (final Exception e) {
throw new RuntimeException("Error creating truststore for Acceptance Test", e);
}
}
return trustStoreFile;
}
private Path createKeyStore(final String type, final String alias) {
if (caKeyPair == null) {
createTrustStore(type);
}
final KeyPair kp = createKeyPair(TestCertificateUtils.Algorithm.RSA);
final X509Certificate certificate =
issueCertificate(caCertificate, caKeyPair, "validator", notBefore(), notAfter(), kp, false);
try {
final KeyStore keyStore = KeyStore.getInstance(type);
keyStore.load(null, null);
keyStore.setKeyEntry(
alias,
kp.getPrivate(),
KEYSTORE_DEFAULT_PASSWORD.toCharArray(),
new Certificate[] {certificate, caCertificate});
final String id = UUID.randomUUID().toString();
return writeKeyStoreFile(keyStore, "keystore", id);
} catch (final Exception e) {
throw new RuntimeException("Error creating keystore for Acceptance Test", e);
}
}
private Path writeKeyStoreFile(
final KeyStore keyStore, final String prefix, final String suffix) {
try {
final Path file = Files.createTempFile(prefix, suffix != null ? suffix : "");
file.toFile().deleteOnExit();
final FileOutputStream keyStoreFOS = new FileOutputStream(file.toFile());
keyStore.store(keyStoreFOS, KEYSTORE_DEFAULT_PASSWORD.toCharArray());
return file;
} catch (final Exception e) {
throw new RuntimeException("Error creating keystore file", e);
}
}
private Path writePasswordFile(final String password, final String prefix, final String suffix) {
try {
final Path file = Files.createTempFile(prefix, suffix);
file.toFile().deleteOnExit();
Files.write(file, password.getBytes(StandardCharsets.UTF_8));
return file;
} catch (final IOException e) {
throw new RuntimeException("Error creating password file", e);
}
}
private Instant notBefore() {
return Instant.now().minus(1, ChronoUnit.DAYS);
}
private Instant notAfter() {
return Instant.now().plus(10, ChronoUnit.DAYS);
}
private Path readResourceAsPath(final String path) throws Exception {
return Path.of(Objects.requireNonNull(this.getClass().getResource(path)).toURI());
}
}

@ -130,7 +130,6 @@ public class PrivacyNode implements AutoCloseable {
besuConfig.getPrivacyParameters(),
List.of(),
Optional.empty(),
Optional.empty(),
besuConfig.isStrictTxReplayProtectionEnabled(),
besuConfig.getEnvironment());
}

@ -1,29 +0,0 @@
/*
* Copyright 2020 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.bft.pki;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBaseJunit5;
import java.util.stream.Stream;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.provider.Arguments;
@Disabled("This is not a test class, it offers PKI QBFT parameterization only.")
public abstract class ParameterizedPkiQbftTestBase extends AcceptanceTestBaseJunit5 {
public static Stream<Arguments> factoryFunctions() {
return PkiQbftAcceptanceTestParameterization.getFactories();
}
}

@ -1,130 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.bft.pki;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class PkiQbftAcceptanceTest extends ParameterizedPkiQbftTestBase {
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("factoryFunctions")
public void shouldMineOnSingleNode(
final String testName, final PkiQbftAcceptanceTestParameterization nodeFactory)
throws Exception {
final BesuNode minerNode = nodeFactory.createNode(besu, "miner1");
cluster.start(minerNode);
cluster.verify(blockchain.reachesHeight(minerNode, 1));
final Account sender = accounts.createAccount("account1");
final Account receiver = accounts.createAccount("account2");
minerNode.execute(accountTransactions.createTransfer(sender, 50));
cluster.verify(sender.balanceEquals(50));
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 1));
cluster.verify(receiver.balanceEquals(1));
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 2));
cluster.verify(receiver.balanceEquals(3));
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("factoryFunctions")
public void shouldMineOnMultipleNodes(
final String testName, final PkiQbftAcceptanceTestParameterization nodeFactory)
throws Exception {
final BesuNode minerNode1 = nodeFactory.createNode(besu, "miner1");
final BesuNode minerNode2 = nodeFactory.createNode(besu, "miner2");
final BesuNode minerNode3 = nodeFactory.createNode(besu, "miner3");
final BesuNode minerNode4 = nodeFactory.createNode(besu, "miner4");
cluster.start(minerNode1, minerNode2, minerNode3, minerNode4);
cluster.verify(blockchain.reachesHeight(minerNode1, 1, 85));
final Account sender = accounts.createAccount("account1");
final Account receiver = accounts.createAccount("account2");
minerNode1.execute(accountTransactions.createTransfer(sender, 50));
cluster.verify(sender.balanceEquals(50));
minerNode2.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 1));
cluster.verify(receiver.balanceEquals(1));
minerNode3.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 2));
cluster.verify(receiver.balanceEquals(3));
minerNode4.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 3));
cluster.verify(receiver.balanceEquals(6));
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("factoryFunctions")
public void shouldMineWithIgnoringANodeInCRL(
final String testName, final PkiQbftAcceptanceTestParameterization nodeFactory)
throws Exception {
final BesuNode minerNode1 = nodeFactory.createNode(besu, "miner1");
final BesuNode minerNode2 = nodeFactory.createNode(besu, "miner2");
final BesuNode minerNode3 = nodeFactory.createNode(besu, "miner3");
final BesuNode minerNode4 = nodeFactory.createNode(besu, "miner4");
final BesuNode minerNode5 = nodeFactory.createNode(besu, "miner5");
final BesuNode minerNode6 = nodeFactory.createNode(besu, "miner6");
try {
cluster.start(minerNode1, minerNode2, minerNode3, minerNode4);
cluster.startNode(minerNode5);
cluster.startNode(minerNode6);
cluster.verify(blockchain.reachesHeight(minerNode1, 1, 85));
final Account sender = accounts.createAccount("account1");
final Account receiver = accounts.createAccount("account2");
minerNode1.execute(accountTransactions.createTransfer(sender, 50));
cluster.verify(sender.balanceEquals(50));
minerNode2.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 1));
cluster.verify(receiver.balanceEquals(1));
minerNode3.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 2));
cluster.verify(receiver.balanceEquals(3));
minerNode4.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 3));
cluster.verify(receiver.balanceEquals(6));
if (minerNode1.getTLSConfiguration().isEmpty()) {
minerNode1.verify(net.awaitPeerCount(5));
minerNode5.verify(net.awaitPeerCount(5));
minerNode6.verify(net.awaitPeerCount(5));
} else {
minerNode1.verify(net.awaitPeerCount(3));
minerNode5.verify(net.awaitPeerCount(0));
minerNode6.verify(net.awaitPeerCount(0));
}
} finally {
cluster.stopNode(minerNode5);
cluster.stopNode(minerNode6);
minerNode5.close();
minerNode6.close();
}
}
}

@ -1,120 +0,0 @@
/*
* Copyright 2020 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.bft.pki;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;
public class PkiQbftAcceptanceTestParameterization {
public static Stream<Arguments> getFactories() {
List<Arguments> args = new ArrayList<>();
/*
BLOCK CREATION
*/
args.add(
Arguments.of(
"qbft-pki-jks",
new PkiQbftAcceptanceTestParameterization(
BesuNodeFactory::createPkiQbftJKSNode,
BesuNodeFactory::createPkiQbftJKSNodeWithValidators)));
args.add(
Arguments.of(
"qbft-pki-pkcs12",
new PkiQbftAcceptanceTestParameterization(
BesuNodeFactory::createPkiQbftPKCS12Node,
BesuNodeFactory::createPkiQbftPKCS12NodeWithValidators)));
if (Boolean.getBoolean("acctests.runBesuAsProcess")) {
args.add(
Arguments.of(
"qbft-pki-pkcs11",
new PkiQbftAcceptanceTestParameterization(
BesuNodeFactory::createPkiQbftPKCS11Node,
BesuNodeFactory::createPkiQbftPKCS11NodeWithValidators)));
}
/*
TLS
*/
args.add(
Arguments.of(
"qbft-tls-jks",
new PkiQbftAcceptanceTestParameterization(
BesuNodeFactory::createQbftNodeWithTLSJKS,
BesuNodeFactory::createQbftTLSJKSNodeWithValidators)));
args.add(
Arguments.of(
"qbft-tls-pkcs12",
new PkiQbftAcceptanceTestParameterization(
BesuNodeFactory::createQbftNodeWithTLSPKCS12,
BesuNodeFactory::createQbftTLSPKCS12NodeWithValidators)));
if (Boolean.getBoolean("acctests.runBesuAsProcess")) {
args.add(
Arguments.of(
"qbft-tls-pkcs11",
new PkiQbftAcceptanceTestParameterization(
BesuNodeFactory::createQbftNodeWithTLSPKCS11,
BesuNodeFactory::createQbftTLSPKCS11NodeWithValidators)));
}
return args.stream();
}
@FunctionalInterface
public interface NodeCreator {
BesuNode create(BesuNodeFactory factory, String name) throws Exception;
}
@FunctionalInterface
public interface NodeWithValidatorsCreator {
BesuNode create(BesuNodeFactory factory, String name, String[] validators) throws Exception;
}
private final NodeCreator creatorFn;
private final NodeWithValidatorsCreator createorWithValidatorFn;
public PkiQbftAcceptanceTestParameterization(
final NodeCreator creatorFn, final NodeWithValidatorsCreator createorWithValidatorFn) {
this.creatorFn = creatorFn;
this.createorWithValidatorFn = createorWithValidatorFn;
}
public BesuNode createNode(BesuNodeFactory factory, String name) throws Exception {
return creatorFn.create(factory, name);
}
public BesuNode createNodeWithValidators(
BesuNodeFactory factory, String name, String[] validators) throws Exception {
return createorWithValidatorFn.create(factory, name, validators);
}
}

@ -1,162 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.qbft.blockcreation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.consensus.common.bft.BftExtraDataFixture.createExtraData;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.consensus.common.bft.BftBlockHeaderFunctions;
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
import org.hyperledger.besu.consensus.qbft.QbftExtraDataCodec;
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftBlockHeaderFunctions;
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftExtraData;
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftExtraDataCodec;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreationTiming;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator.BlockCreationResult;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
import org.hyperledger.besu.pki.cms.CmsCreator;
import java.util.Collections;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PkiQbftBlockCreatorTest {
private final PkiQbftExtraDataCodec extraDataCodec = new PkiQbftExtraDataCodec();
private BlockCreator blockCreator;
private CmsCreator cmsCreator;
private PkiQbftBlockCreator pkiQbftBlockCreator;
private BlockHeaderTestFixture blockHeaderBuilder;
private BlockHeader blockHeader;
private BftProtocolSchedule protocolSchedule;
private ProtocolSpec protocolSpec;
@BeforeEach
public void before() {
blockCreator = mock(BlockCreator.class);
cmsCreator = mock(CmsCreator.class);
blockHeader = mock(BlockHeader.class);
protocolSchedule = mock(BftProtocolSchedule.class);
protocolSpec = mock(ProtocolSpec.class);
pkiQbftBlockCreator =
new PkiQbftBlockCreator(
blockCreator, cmsCreator, extraDataCodec, blockHeader, protocolSchedule);
blockHeaderBuilder = new BlockHeaderTestFixture();
when(protocolSchedule.getByBlockNumberOrTimestamp(anyLong(), anyLong()))
.thenReturn(protocolSpec);
}
@Test
public void createProposalBehaviourWithNonPkiCodecFails() {
assertThatThrownBy(
() ->
new PkiQbftBlockCreator(
blockCreator,
cmsCreator,
new QbftExtraDataCodec(),
blockHeader,
protocolSchedule))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("PkiQbftBlockCreator must use PkiQbftExtraDataCodec");
}
@Test
public void cmsInProposedBlockHasValueCreatedByCmsCreator() {
createBlockBeingProposed();
when(protocolSpec.getWithdrawalsValidator())
.thenReturn(new WithdrawalsValidator.AllowedWithdrawals());
final Bytes cms = Bytes.random(32);
when(cmsCreator.create(any(Bytes.class))).thenReturn(cms);
final Block proposedBlock = pkiQbftBlockCreator.createBlock(1L).getBlock();
final PkiQbftExtraData proposedBlockExtraData =
(PkiQbftExtraData) extraDataCodec.decodeRaw(proposedBlock.getHeader().getExtraData());
assertThat(proposedBlockExtraData).isInstanceOf(PkiQbftExtraData.class);
assertThat(proposedBlockExtraData.getCms()).isEqualTo(cms);
}
@Test
public void cmsIsCreatedWithCorrectHashingFunction() {
when(protocolSpec.getWithdrawalsValidator())
.thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals());
final Block block = createBlockBeingProposed();
final Hash expectedHashForCmsCreation =
PkiQbftBlockHeaderFunctions.forCmsSignature(extraDataCodec).hash(block.getHeader());
when(cmsCreator.create(any(Bytes.class))).thenReturn(Bytes.random(32));
pkiQbftBlockCreator.createBlock(1L);
verify(cmsCreator).create(eq(expectedHashForCmsCreation));
}
@Test
public void proposedBlockHashUsesCommittedSealHeaderFunction() {
createBlockBeingProposed();
when(cmsCreator.create(any(Bytes.class))).thenReturn(Bytes.random(32));
final Block blockWithCms = pkiQbftBlockCreator.createBlock(1L).getBlock();
final Hash expectedBlockHash =
BftBlockHeaderFunctions.forCommittedSeal(extraDataCodec).hash(blockWithCms.getHeader());
assertThat(blockWithCms.getHash()).isEqualTo(expectedBlockHash);
}
private Block createBlockBeingProposed() {
final BftExtraData originalExtraData =
createExtraData(blockHeaderBuilder.buildHeader(), extraDataCodec);
final BlockHeader blockHeaderWithExtraData =
blockHeaderBuilder.extraData(extraDataCodec.encode(originalExtraData)).buildHeader();
final Block block =
new Block(
blockHeaderWithExtraData,
new BlockBody(Collections.emptyList(), Collections.emptyList()));
when(blockCreator.createBlock(eq(1L)))
.thenReturn(
new BlockCreationResult(
block, new TransactionSelectionResults(), new BlockCreationTiming()));
when(blockCreator.createEmptyWithdrawalsBlock(anyLong()))
.thenReturn(
new BlockCreationResult(
block, new TransactionSelectionResults(), new BlockCreationTiming()));
return block;
}
}

@ -1,85 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.qbft.pki;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import org.hyperledger.besu.consensus.qbft.pki.DefaultKeyStoreWrapperProvider.HardwareKeyStoreWrapperProvider;
import org.hyperledger.besu.consensus.qbft.pki.DefaultKeyStoreWrapperProvider.SoftwareKeyStoreWrapperProvider;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class DefaultKeyStoreWrapperProviderTest {
@Mock private HardwareKeyStoreWrapperProvider hardwareKeyStoreWrapperProvider;
@Mock private SoftwareKeyStoreWrapperProvider softwareKeyStoreWrapperProvider;
@InjectMocks private DefaultKeyStoreWrapperProvider keyStoreWrapperProvider;
private final Path keystorePath = Path.of("/keystore");
private final String keystorePassword = "pwd";
private final Path crlPath = Path.of("/crl");
@Test
public void configWithTypePKCS11UsesHardwareKeyStoreProvider() {
keyStoreWrapperProvider.apply(
KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, keystorePath, keystorePassword, crlPath);
verify(hardwareKeyStoreWrapperProvider)
.get(eq(keystorePassword), eq(keystorePath), eq(crlPath));
verifyNoInteractions(softwareKeyStoreWrapperProvider);
}
@Test
public void configWithTypePKCS12UsesSoftwareKeyStoreProvider() {
keyStoreWrapperProvider.apply(
KeyStoreWrapper.KEYSTORE_TYPE_PKCS12, keystorePath, keystorePassword, crlPath);
verify(softwareKeyStoreWrapperProvider)
.get(
eq(KeyStoreWrapper.KEYSTORE_TYPE_PKCS12),
eq(keystorePath),
eq(keystorePassword),
eq(crlPath));
verifyNoInteractions(hardwareKeyStoreWrapperProvider);
}
@Test
public void configWithTypeJKSUsesSoftwareKeyStoreProvider() {
keyStoreWrapperProvider.apply(
KeyStoreWrapper.KEYSTORE_TYPE_JKS, keystorePath, keystorePassword, crlPath);
verify(softwareKeyStoreWrapperProvider)
.get(
eq(KeyStoreWrapper.KEYSTORE_TYPE_JKS),
eq(keystorePath),
eq(keystorePassword),
eq(crlPath));
verifyNoInteractions(hardwareKeyStoreWrapperProvider);
}
}

@ -1,75 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.qbft.pki;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class PkiBlockCreationConfigurationProviderTest {
@Mock KeyStoreWrapperProvider keyStoreWrapperProvider;
@Mock KeyStoreWrapper keyStoreWrapper;
@Mock KeyStoreWrapper trustStoreWrapper;
@Test
public void pkiBlockCreationConfigurationIsLoadedCorrectly() {
when(keyStoreWrapperProvider.apply(any(), eq(Path.of("/tmp/keystore")), eq("pwd"), isNull()))
.thenReturn(keyStoreWrapper);
when(keyStoreWrapperProvider.apply(
any(), eq(Path.of("/tmp/truststore")), eq("pwd"), eq(Path.of("/tmp/crl"))))
.thenReturn(trustStoreWrapper);
final PkiKeyStoreConfiguration pkiKeyStoreConfiguration =
spy(
new PkiKeyStoreConfiguration.Builder()
.withKeyStorePath(Path.of("/tmp/keystore"))
.withKeyStorePasswordPath(Path.of("/tmp/password"))
.withTrustStorePath(Path.of("/tmp/truststore"))
.withTrustStorePasswordPath(Path.of("/tmp/password"))
.withCertificateAlias("anAlias")
.withCrlFilePath(Path.of("/tmp/crl"))
.build());
doReturn("pwd").when(pkiKeyStoreConfiguration).getKeyStorePassword();
doReturn("pwd").when(pkiKeyStoreConfiguration).getTrustStorePassword();
final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider =
new PkiBlockCreationConfigurationProvider(keyStoreWrapperProvider);
final PkiBlockCreationConfiguration pkiBlockCreationConfiguration =
pkiBlockCreationConfigProvider.load(pkiKeyStoreConfiguration);
assertThat(pkiBlockCreationConfiguration).isNotNull();
assertThat(pkiBlockCreationConfiguration.getKeyStore()).isNotNull();
assertThat(pkiBlockCreationConfiguration.getTrustStore()).isNotNull();
assertThat(pkiBlockCreationConfiguration.getCertificateAlias()).isEqualTo("anAlias");
}
}

@ -1,85 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.qbft.pki;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.consensus.common.bft.BftBlockHashing;
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
import org.hyperledger.besu.consensus.common.bft.BftExtraDataFixture;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PkiQbftBlockHashingTest {
private PkiQbftExtraDataCodec pkiExtraDataCodec = new PkiQbftExtraDataCodec();
private PkiQbftBlockHashing pkiQbftBlockHashing;
@BeforeEach
public void before() {
pkiExtraDataCodec = spy(new PkiQbftExtraDataCodec());
pkiQbftBlockHashing = new PkiQbftBlockHashing(pkiExtraDataCodec);
}
@Test
public void blockHashingUsesCorrectEncodingWithoutCmsMethodInCodec() {
final PkiQbftExtraData pkiQbftExtraData = createPkiQbftExtraData();
final BlockHeader headerWithExtraData =
new BlockHeaderTestFixture()
.number(1L)
.extraData(pkiExtraDataCodec.encode(pkiQbftExtraData))
.buildHeader();
// Expected hash using the extraData encoded by the encodeWithoutCms method of the codec
final Hash expectedHash =
Hash.hash(
BftBlockHashing.serializeHeader(
headerWithExtraData,
() -> pkiExtraDataCodec.encodeWithoutCms(pkiQbftExtraData),
pkiExtraDataCodec));
final Hash hash =
pkiQbftBlockHashing.calculateHashOfBftBlockForCmsSignature(headerWithExtraData);
assertThat(hash).isEqualTo(expectedHash);
/*
Verify that the encodeWithoutCms method was called twice, once when calculating the
expected hash and a second time as part of the hash calculation on
calculateHashOfBftBlockForCmsSignature
*/
verify(pkiExtraDataCodec, times(2)).encodeWithoutCms(any(PkiQbftExtraData.class));
}
private PkiQbftExtraData createPkiQbftExtraData() {
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader();
final BftExtraData extraData =
BftExtraDataFixture.createExtraData(blockHeader, pkiExtraDataCodec);
return new PkiQbftExtraData(extraData, Bytes.random(32));
}
}

@ -1,243 +0,0 @@
/*
* Copyright 2020 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.qbft.pki;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.consensus.qbft.QbftExtraDataCodecTestUtils.createNonEmptyVanityData;
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
import org.hyperledger.besu.consensus.common.bft.Vote;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
public class PkiQbftExtraDataCodecTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
// Arbitrary bytes representing a non-empty CMS
private final Bytes cms = Bytes.fromHexString("0x01");
private final String RAW_EXCLUDE_COMMIT_SEALS_AND_ROUND_NUMBER_ENCODED_STRING =
"0xf867a00102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20ea940000000000000000"
+ "000000000000000000000001940000000000000000000000000000000000000002d7940000000000000000"
+ "00000000000000000000000181ff80c001";
private final String RAW_EXCLUDE_COMMIT_SEALS_ENCODED_STRING =
"0xf86aa00102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20ea940000000000000000"
+ "000000000000000000000001940000000000000000000000000000000000000002d7940000000000000000"
+ "00000000000000000000000181ff83fedcbac001";
private final String RAW_ALL_ENCODED_STRING =
"0xf8f1a00102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20ea940000000000000000"
+ "000000000000000000000001940000000000000000000000000000000000000002d7940000000000000000"
+ "00000000000000000000000181ff83fedcbaf886b841000000000000000000000000000000000000000000"
+ "0000000000000000000001000000000000000000000000000000000000000000000000000000000000000a"
+ "00b841000000000000000000000000000000000000000000000000000000000000000a0000000000000000"
+ "0000000000000000000000000000000000000000000000010001";
private final String RAW_QBFT_EXTRA_DATA =
"0xf8f0a00102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20ea940000000000000000"
+ "000000000000000000000001940000000000000000000000000000000000000002d7940000000000000000"
+ "00000000000000000000000181ff83fedcbaf886b841000000000000000000000000000000000000000000"
+ "0000000000000000000001000000000000000000000000000000000000000000000000000000000000000a"
+ "00b841000000000000000000000000000000000000000000000000000000000000000a0000000000000000"
+ "00000000000000000000000000000000000000000000000100";
private final PkiQbftExtraDataCodec bftExtraDataCodec = new PkiQbftExtraDataCodec();
@Test
public void fullyPopulatedDataProducesCorrectlyFormedExtraDataObject() {
final List<Address> validators =
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
final int round = 0x00FEDCBA;
final List<SECPSignature> committerSeals =
Arrays.asList(
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.TEN, (byte) 0),
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.TEN, BigInteger.ONE, (byte) 0));
// Create randomised vanity data.
final byte[] vanity_bytes = createNonEmptyVanityData();
new Random().nextBytes(vanity_bytes);
final Bytes vanity_data = Bytes.wrap(vanity_bytes);
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList(); // start extra data list
// vanity data
encoder.writeBytes(vanity_data);
// validators
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
// votes
encoder.startList();
encoder.writeBytes(Address.fromHexString("1"));
encoder.writeByte(Vote.ADD_BYTE_VALUE);
encoder.endList();
// rounds
encoder.writeIntScalar(round);
// committer seals
encoder.writeList(committerSeals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
// cms
encoder.writeBytes(cms);
encoder.endList(); // end extra data list
final Bytes bufferToInject = encoder.encoded();
final PkiQbftExtraData extraData =
(PkiQbftExtraData) bftExtraDataCodec.decodeRaw(bufferToInject);
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
assertThat(extraData.getRound()).isEqualTo(round);
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
assertThat(extraData.getValidators()).isEqualTo(validators);
assertThat(extraData.getCms()).isEqualTo(cms);
}
@Test
public void decodingQbftExtraDataDelegatesToQbftCodec() {
final List<Address> validators =
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
final int round = 0x00FEDCBA;
final List<SECPSignature> committerSeals =
Arrays.asList(
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.TEN, (byte) 0),
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.TEN, BigInteger.ONE, (byte) 0));
// Create randomised vanity data.
final byte[] vanity_bytes = createNonEmptyVanityData();
new Random().nextBytes(vanity_bytes);
final Bytes vanity_data = Bytes.wrap(vanity_bytes);
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList(); // start extra data list
// vanity data
encoder.writeBytes(vanity_data);
// validators
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
// votes
encoder.startList();
encoder.writeBytes(Address.fromHexString("1"));
encoder.writeByte(Vote.ADD_BYTE_VALUE);
encoder.endList();
// rounds
encoder.writeIntScalar(round);
// committer seals
encoder.writeList(committerSeals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
// Not including the CMS in the list (to generate a non-pki QBFT extra data)
encoder.endList(); // end extra data list
final Bytes bufferToInject = encoder.encoded();
final PkiQbftExtraData extraData =
(PkiQbftExtraData) bftExtraDataCodec.decodeRaw(bufferToInject);
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
assertThat(extraData.getRound()).isEqualTo(round);
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
assertThat(extraData.getValidators()).isEqualTo(validators);
assertThat(extraData.getCms()).isEqualTo(Bytes.EMPTY);
}
/*
When encoding for blockchain, we ignore commit seals and round number, but we include the CMS
*/
@Test
public void encodingForBlockchainShouldIncludeCms() {
final Bytes expectedRawDecoding =
Bytes.fromHexString(RAW_EXCLUDE_COMMIT_SEALS_AND_ROUND_NUMBER_ENCODED_STRING);
final Bytes encoded =
bftExtraDataCodec.encodeWithoutCommitSealsAndRoundNumber(getDecodedExtraData(cms));
assertThat(encoded).isEqualTo(expectedRawDecoding);
}
@Test
public void encodingWithoutCommitSealsShouldIncludeCms() {
final Bytes expectedRawDecoding = Bytes.fromHexString(RAW_EXCLUDE_COMMIT_SEALS_ENCODED_STRING);
final Bytes encoded = bftExtraDataCodec.encodeWithoutCommitSeals(getDecodedExtraData(cms));
assertThat(encoded).isEqualTo(expectedRawDecoding);
}
@Test
public void encodingWithAllShouldIncludeCms() {
final Bytes expectedRawDecoding = Bytes.fromHexString(RAW_ALL_ENCODED_STRING);
final Bytes encoded = bftExtraDataCodec.encode(getDecodedExtraData(cms));
assertThat(encoded).isEqualTo(expectedRawDecoding);
}
/*
When encoding for proposal, we include commit seals and round number, but we ignore the CMS
*/
@Test
public void encodingForCreatingCmsProposal() {
final Bytes expectedRawDecoding = Bytes.fromHexString(RAW_QBFT_EXTRA_DATA);
final Bytes encoded = bftExtraDataCodec.encodeWithoutCms(getDecodedExtraData(cms));
assertThat(encoded).isEqualTo(expectedRawDecoding);
}
/*
When encoding non-pki extra data, we delegate to the regular QBFT encoder
*/
@Test
public void encodingQbftExtraData() {
final Bytes expectedRawDecoding = Bytes.fromHexString(RAW_QBFT_EXTRA_DATA);
final PkiQbftExtraData pkiBftExtraData = getDecodedExtraData(cms);
final BftExtraData bftExtraData =
new BftExtraData(
pkiBftExtraData.getVanityData(),
pkiBftExtraData.getSeals(),
pkiBftExtraData.getVote(),
pkiBftExtraData.getRound(),
pkiBftExtraData.getValidators());
final Bytes encoded = bftExtraDataCodec.encode(bftExtraData);
assertThat(encoded).isEqualTo(expectedRawDecoding);
}
private static PkiQbftExtraData getDecodedExtraData(final Bytes cms) {
final List<Address> validators =
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
final int round = 0x00FEDCBA;
final List<SECPSignature> committerSeals =
Arrays.asList(
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.TEN, (byte) 0),
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.TEN, BigInteger.ONE, (byte) 0));
// Create a byte buffer with no data.
final byte[] vanity_bytes = createNonEmptyVanityData();
final Bytes vanity_data = Bytes.wrap(vanity_bytes);
return new PkiQbftExtraData(vanity_data, committerSeals, vote, round, validators, cms);
}
}

@ -1,202 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.cms;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.Algorithm.EC;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.Algorithm.RSA;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.hyperledger.besu.pki.util.TestCertificateUtils.Algorithm;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import org.apache.tuweni.bytes.Bytes;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
public class CmsCreationAndValidationTest {
private static final CmsTestKeystores rsaTestKeystores = new CmsTestKeystores(RSA);
private static final CmsTestKeystores ecTestKeystores = new CmsTestKeystores(EC);
private CmsTestKeystores getCmsTestKeystores(final Algorithm algorithm) {
return switch (algorithm) {
case RSA -> rsaTestKeystores;
case EC -> ecTestKeystores;
};
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithEmptyCmsMessage(final Algorithm algorithm) {
final Bytes data = Bytes.random(32);
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(Bytes.EMPTY, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithTrustedSelfSignedCertificate(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "trusted_selfsigned");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
assertTrue(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithUntrustedSelfSignedCertificate(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "untrusted_selfsigned");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithTrustedChain(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "trusted");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
assertTrue(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithUntrustedChain(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "untrusted");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithExpiredCertificate(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "expired");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithRevokedCertificate(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "revoked");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithoutCRLConfigDisablesCRLCheck(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "revoked");
final Bytes data = Bytes.random(32);
final Bytes cms = cmsCreator.create(data);
CmsValidator cmsValidator = getCmsTestKeystores(algorithm).getCmsValidatorWithoutCrl();
// Because we don't have a CRL CertStore, revocation is not checked
assertTrue(cmsValidator.validate(cms, data));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithWrongSignedData(final Algorithm algorithm) {
final CmsCreator cmsCreator =
new CmsCreator(getCmsTestKeystores(algorithm).getKeystoreWrapper(), "trusted");
final Bytes otherData = Bytes.random(32);
final Bytes cms = cmsCreator.create(otherData);
final Bytes expectedData = Bytes.random(32);
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(cms, expectedData));
}
@ParameterizedTest
@EnumSource(value = Algorithm.class)
public void cmsValidationWithInvalidSignature(final Algorithm algorithm) throws Exception {
// Create a CMS message signed with a certificate, but create SignerInfo using another
// certificate to trigger the signature verification to fail.
final PrivateKey privateKey =
getCmsTestKeystores(algorithm).getKeystoreWrapper().getPrivateKey("trusted");
final PublicKey publicKey =
getCmsTestKeystores(algorithm).getKeystoreWrapper().getPublicKey("trusted");
final X509Certificate signerCertificate =
(X509Certificate)
getCmsTestKeystores(algorithm).getKeystoreWrapper().getCertificate("trusted");
final X509Certificate otherCertificate =
(X509Certificate)
getCmsTestKeystores(algorithm)
.getKeystoreWrapper()
.getCertificate("trusted_selfsigned");
final ContentSigner contentSigner =
new JcaContentSignerBuilder(CmsCreator.getPreferredSignatureAlgorithm(publicKey))
.build(privateKey);
final CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator();
cmsGenerator.addCertificate(new JcaX509CertificateHolder(signerCertificate));
cmsGenerator.addCertificate(new JcaX509CertificateHolder(otherCertificate));
final DigestCalculatorProvider digestCalculatorProvider =
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
cmsGenerator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider)
.build(contentSigner, otherCertificate));
final Bytes expectedData = Bytes.random(32);
final CMSTypedData cmsData = new CMSProcessableByteArray(expectedData.toArray());
final CMSSignedData cmsSignedData = cmsGenerator.generate(cmsData, true);
final Bytes cmsBytes = Bytes.wrap(cmsSignedData.getEncoded());
assertFalse(getCmsTestKeystores(algorithm).getCmsValidator().validate(cmsBytes, expectedData));
}
}

@ -1,243 +0,0 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.cms;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.createCRL;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.createKeyPair;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.createSelfSignedCertificate;
import static org.hyperledger.besu.pki.util.TestCertificateUtils.issueCertificate;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import org.hyperledger.besu.pki.keystore.SoftwareKeyStoreWrapper;
import org.hyperledger.besu.pki.util.TestCertificateUtils.Algorithm;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class CmsTestKeystores {
private KeyStore keystore;
private KeyStore truststore;
private List<X509CRL> CRLs;
private KeyStoreWrapper keystoreWrapper;
private KeyStoreWrapper truststoreWrapper;
private KeyStoreWrapper truststoreWrapperWithoutCrl;
private CmsValidator cmsValidator;
private CmsValidator cmsValidatorWithoutCrl;
public CmsTestKeystores(final Algorithm algorithm) {
try {
init(algorithm);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void init(final Algorithm algorithm) throws Exception {
final Instant notBefore = Instant.now().minus(1, ChronoUnit.DAYS);
final Instant notAfter = Instant.now().plus(1, ChronoUnit.DAYS);
/*
Create self-signed certificate
*/
final KeyPair selfsignedKeyPair = createKeyPair(algorithm);
final X509Certificate selfsignedCertificate =
createSelfSignedCertificate("selfsigned", notBefore, notAfter, selfsignedKeyPair);
/*
Create trusted chain (ca -> interca -> partneraca -> partneravalidator)
*/
final KeyPair caKeyPair = createKeyPair(algorithm);
final X509Certificate caCertificate =
createSelfSignedCertificate("ca", notBefore, notAfter, caKeyPair);
final KeyPair interCAKeyPair = createKeyPair(algorithm);
final X509Certificate interCACertificate =
issueCertificate(
caCertificate, caKeyPair, "interca", notBefore, notAfter, interCAKeyPair, true);
final KeyPair partnerACAPair = createKeyPair(algorithm);
final X509Certificate partnerACACertificate =
issueCertificate(
interCACertificate,
interCAKeyPair,
"partneraca",
notBefore,
notAfter,
partnerACAPair,
true);
final KeyPair parterAValidatorKeyPair = createKeyPair(algorithm);
final X509Certificate partnerAValidatorCertificate =
issueCertificate(
partnerACACertificate,
partnerACAPair,
"partneravalidator",
notBefore,
notAfter,
parterAValidatorKeyPair,
false);
/*
Create expired certificate
*/
final KeyPair expiredKeyPair = createKeyPair(algorithm);
final X509Certificate expiredCertificate =
issueCertificate(
caCertificate,
caKeyPair,
"expired",
notBefore,
notBefore.plus(1, ChronoUnit.SECONDS),
expiredKeyPair,
true);
/*
Create revoked and revoked certificates
*/
final KeyPair revokedKeyPair = createKeyPair(algorithm);
final X509Certificate revokedCertificate =
issueCertificate(
caCertificate, caKeyPair, "revoked", notBefore, notAfter, revokedKeyPair, true);
/*
Create untrusted chain (untrusted_selfsigned -> unstrusted_partner)
*/
final KeyPair untrustedSelfSignedKeyPair = createKeyPair(algorithm);
final X509Certificate untrustedSelfsignedCertificate =
createSelfSignedCertificate(
"untrusted_selfsigned", notBefore, notAfter, untrustedSelfSignedKeyPair);
final KeyPair untrustedIntermediateKeyPair = createKeyPair(algorithm);
final X509Certificate untrustedIntermediateCertificate =
issueCertificate(
untrustedSelfsignedCertificate,
untrustedSelfSignedKeyPair,
"unstrusted_partner",
notBefore,
notAfter,
untrustedIntermediateKeyPair,
true);
/*
Create truststore wrapper with 3 trusted certificates: 'ca', 'interca' and 'selfsigned'
*/
truststore = KeyStore.getInstance("PKCS12");
truststore.load(null, null);
truststore.setCertificateEntry("ca", caCertificate);
truststore.setCertificateEntry("interca", interCACertificate);
truststore.setCertificateEntry("selfsigned", selfsignedCertificate);
/*
Create keystore with certificates used in tests
*/
keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, null);
keystore.setKeyEntry(
"trusted_selfsigned",
selfsignedKeyPair.getPrivate(),
"".toCharArray(),
new Certificate[] {selfsignedCertificate});
keystore.setKeyEntry(
"untrusted_selfsigned",
untrustedSelfSignedKeyPair.getPrivate(),
"".toCharArray(),
new Certificate[] {untrustedSelfsignedCertificate});
keystore.setKeyEntry(
"expired",
expiredKeyPair.getPrivate(),
"".toCharArray(),
new Certificate[] {expiredCertificate});
keystore.setKeyEntry(
"revoked",
revokedKeyPair.getPrivate(),
"".toCharArray(),
new Certificate[] {revokedCertificate});
keystore.setKeyEntry(
"trusted",
parterAValidatorKeyPair.getPrivate(),
"".toCharArray(),
new Certificate[] {partnerAValidatorCertificate, partnerACACertificate});
keystore.setKeyEntry(
"untrusted",
untrustedIntermediateKeyPair.getPrivate(),
"".toCharArray(),
new Certificate[] {untrustedIntermediateCertificate, untrustedSelfsignedCertificate});
/*
Create CRLs for all CA certificates (mostly empty, only ca has one revoked certificate)
*/
final X509CRL caCRL = createCRL(caCertificate, caKeyPair, Set.of(revokedCertificate));
final X509CRL intercaCRL =
createCRL(interCACertificate, interCAKeyPair, Collections.emptyList());
final X509CRL partnerACACRL =
createCRL(partnerACACertificate, partnerACAPair, Collections.emptyList());
final X509CRL selfsignedCRL =
createCRL(selfsignedCertificate, selfsignedKeyPair, Collections.emptyList());
CRLs = List.of(caCRL, intercaCRL, partnerACACRL, selfsignedCRL);
keystoreWrapper = new SoftwareKeyStoreWrapper(null, keystore, "");
truststoreWrapper = new SoftwareKeyStoreWrapper(CRLs, truststore, "");
truststoreWrapperWithoutCrl = new SoftwareKeyStoreWrapper(null, truststore, "");
cmsValidator = new CmsValidator(truststoreWrapper);
cmsValidatorWithoutCrl = new CmsValidator(truststoreWrapperWithoutCrl);
}
public KeyStore getKeystore() {
return keystore;
}
public KeyStore getTruststore() {
return truststore;
}
public List<X509CRL> getCRLs() {
return CRLs;
}
public KeyStoreWrapper getKeystoreWrapper() {
return keystoreWrapper;
}
public KeyStoreWrapper getTruststoreWrapper() {
return truststoreWrapper;
}
public CmsValidator getCmsValidator() {
return cmsValidator;
}
public KeyStoreWrapper getTruststoreWrapperWithoutCrl() {
return truststoreWrapperWithoutCrl;
}
public CmsValidator getCmsValidatorWithoutCrl() {
return cmsValidatorWithoutCrl;
}
}

@ -1,156 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.keystore;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.nio.file.Path;
import java.security.cert.Certificate;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public abstract class BaseKeyStoreFileWrapperTest {
protected static final String KEYSTORE_VALID_KEY_ALIAS = "partner1client1";
protected static final String KEYSTORE_INVALID_KEY_ALIAS = "partner1clientinvalid";
protected static final String TRUSTSTORE_VALID_CERTIFICATE_ALIAS = "interca";
protected static final String TRUSTSTORE_INVALID_CERTIFICATE_ALIAS = "interca-invalid";
protected static Path toPath(final String path) throws Exception {
return null == path
? null
: Path.of(BaseKeyStoreFileWrapperTest.class.getResource(path).toURI());
}
@ParameterizedTest
@MethodSource("data")
public void getPublicKey_WithValidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNotNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getPublicKey(KEYSTORE_VALID_KEY_ALIAS));
}
@ParameterizedTest
@MethodSource("data")
public void getPublicKey_WithInvalidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getPublicKey(KEYSTORE_INVALID_KEY_ALIAS));
}
@ParameterizedTest
@MethodSource("data")
public void getPrivateKey_WithValidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNotNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getPrivateKey(KEYSTORE_VALID_KEY_ALIAS),
"Private key is not null");
}
@ParameterizedTest
@MethodSource("data")
public void getPrivateKey_WithInvalidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getPrivateKey(KEYSTORE_INVALID_KEY_ALIAS),
"Private key is null");
}
@ParameterizedTest
@MethodSource("data")
public void getCertificate_WithValidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNotNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getCertificate(KEYSTORE_VALID_KEY_ALIAS),
"Certificate is not null");
}
@ParameterizedTest
@MethodSource("data")
public void getCertificate_WithInvalidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getCertificate(KEYSTORE_INVALID_KEY_ALIAS),
"Certificate is null");
}
@ParameterizedTest
@MethodSource("data")
public void getCertificateChain_WithValidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNotNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getCertificateChain(KEYSTORE_VALID_KEY_ALIAS),
"Certificate chain is not null");
}
@ParameterizedTest
@MethodSource("data")
public void getCertificateChain_WithInvalidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getCertificateChain(
KEYSTORE_INVALID_KEY_ALIAS),
"Certificate is null");
}
@ParameterizedTest
@MethodSource("data")
public void getCertificate_FromTruststore_WithValidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
final Certificate certificate =
keyStoreWrapperTestParameter.keyStoreWrapper.getCertificate(
TRUSTSTORE_VALID_CERTIFICATE_ALIAS);
if (keyStoreWrapperTestParameter.keystoreWrapperConfiguredWithTruststore) {
assertNotNull(certificate, "Certificate is not null");
} else {
assertNull(certificate, "Certificate is null");
}
}
@ParameterizedTest
@MethodSource("data")
public void getCertificate_FromTruststore_WithInvalidAlias_ReturnsExpectedValue(
final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNull(
keyStoreWrapperTestParameter.keyStoreWrapper.getPrivateKey(
TRUSTSTORE_INVALID_CERTIFICATE_ALIAS),
"Certificate is null");
}
@ParameterizedTest
@MethodSource("data")
public void getCRLS_Check(final KeyStoreWrapperTestParameter keyStoreWrapperTestParameter) {
assertNotNull(keyStoreWrapperTestParameter.keyStoreWrapper.getCRLs(), "CRLs is not null");
assertEquals(
keyStoreWrapperTestParameter.keyStoreWrapper.getCRLs().size(), 2, "CRLs size matches");
}
public static class KeyStoreWrapperTestParameter {
public String keyStoreWrapperDescription;
public boolean keystoreWrapperConfiguredWithTruststore;
public KeyStoreWrapper keyStoreWrapper;
public KeyStoreWrapperTestParameter(
final String keyStoreWrapperDescription,
final boolean keystoreWrapperConfiguredWithTruststore,
final KeyStoreWrapper keyStoreWrapper) {
this.keyStoreWrapperDescription = keyStoreWrapperDescription;
this.keystoreWrapperConfiguredWithTruststore = keystoreWrapperConfiguredWithTruststore;
this.keyStoreWrapper = keyStoreWrapper;
}
}
}

@ -1,81 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.keystore;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CryptoTestUtil {
private static final Logger LOG = LoggerFactory.getLogger(CryptoTestUtil.class);
private CryptoTestUtil() {}
public static boolean isNSSLibInstalled() {
try {
final String nssLibPath = getNSSLibPath();
return nssLibPath != null && !nssLibPath.trim().isEmpty();
} catch (final Exception e) {
LOG.info("NSS library does not seem to be installed!", e);
}
return false;
}
public static String getNSSLibPath() throws IOException, InterruptedException {
String nssLibPath = "";
final String centOS_nssPathCmd =
"whereis libnssdbm3 | grep -o \"\\/.*libnssdbm3\\.[0-9a-z]* \" | sed 's/\\/libnssdbm3.*//g'";
final String debian_nssPathCmd =
"whereis libnss3 | grep -o \".*libnss3.[0-9a-z]\" | sed 's/lib.* \\(\\/.*\\)\\/lib.*/\\1/'";
final String macOS_nssPathCmd = "dirname `which certutil` | sed 's/bin/lib/g'";
nssLibPath = executeSystemCmd(centOS_nssPathCmd).orElse(nssLibPath);
LOG.info("centOS_nssPathCmd: {}", nssLibPath);
if ("".equals(nssLibPath)) {
nssLibPath = executeSystemCmd(debian_nssPathCmd).orElse(nssLibPath);
LOG.info("debian_nssPathCmd: {}", nssLibPath);
}
if ("".equals(nssLibPath)) {
nssLibPath = executeSystemCmd(macOS_nssPathCmd).orElse(nssLibPath);
LOG.info("macOS_nssPathCmd: {}", nssLibPath);
}
LOG.info("Detected NSS library path: {}", nssLibPath);
return nssLibPath;
}
public static Optional<String> executeSystemCmd(final String cmd)
throws IOException, InterruptedException {
final Process p = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", cmd});
try {
if (p.waitFor() == 0) {
final java.util.Scanner s =
new java.util.Scanner(p.getInputStream(), StandardCharsets.UTF_8.name())
.useDelimiter("\\A");
if (s.hasNext()) {
return Optional.of(s.next().replace("\r", "").replace("\n", ""));
}
}
} finally {
if (p != null) {
p.destroy();
}
}
return Optional.empty();
}
}

@ -1,119 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.keystore;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.hyperledger.besu.pki.PkiException;
import java.nio.file.Path;
import java.security.Provider;
import java.security.Security;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.OS;
public class HardwareKeyStoreFileWrapperTest extends BaseKeyStoreFileWrapperTest {
private static final String config = "/keystore/partner1client1/nss.cfg";
private static final String crl = "/keystore/partner1client1/crl.pem";
private static final String configName = "NSScrypto-partner1client1";
private static final String validKeystorePassword = "test123";
private static KeyStoreWrapperTestParameter keyStoreWrapperTestParameter;
@BeforeAll
public static void setup() {
keyStoreWrapperTestParameter =
new KeyStoreWrapperTestParameter(
"HardwareKeyStoreWrapper[PKCS11 keystore/truststore]",
true,
CryptoTestUtil.isNSSLibInstalled() ? getHardwareKeyStoreWrapper(configName) : null);
}
public static Collection<KeyStoreWrapperTestParameter> data() {
return List.of(keyStoreWrapperTestParameter);
}
private static KeyStoreWrapper getHardwareKeyStoreWrapper(final String cfgName) {
try {
final Path path = toPath(config);
final Path crlPath = toPath(crl);
final Optional<Provider> existingProvider =
Stream.of(Security.getProviders())
.filter(p -> p.getName().equals("SunPKCS11" + cfgName))
.findAny();
return existingProvider
.map(provider -> new HardwareKeyStoreWrapper(validKeystorePassword, provider, crlPath))
.orElseGet(() -> new HardwareKeyStoreWrapper(validKeystorePassword, path, crlPath));
} catch (final Exception e) {
// nss3 is difficult to setup on mac, don't let it break unit tests for dev machines.
Assumptions.assumeFalse(
OS.MAC.isCurrentOs(),
"Failed to initialize hardware keystore: " + e.getLocalizedMessage());
// Not a mac, probably a production build. Full failure.
throw new PkiException("Failed to initialize hardware keystore", e);
}
}
@BeforeEach
public void beforeMethod() {
assumeTrue(
CryptoTestUtil.isNSSLibInstalled(),
"Test ignored due to NSS library not being installed/detected.");
}
@Test
public void getPkcs11Provider() throws Exception {
final HardwareKeyStoreWrapper sut =
(HardwareKeyStoreWrapper) getHardwareKeyStoreWrapper(configName);
assertThrows(
IllegalArgumentException.class, () -> sut.getPkcs11ProviderForConfig("no-library"));
}
@Test
public void init_keystorePassword_config() throws Exception {
new HardwareKeyStoreWrapper(validKeystorePassword, toPath(config), toPath(crl));
}
@Test
public void init_keystorePassword_config_invalid() throws Exception {
final String config = "invalid";
assertThrows(
NullPointerException.class,
() -> new HardwareKeyStoreWrapper(validKeystorePassword, toPath(config), toPath(crl)));
}
@Test
public void init_keystorePassword_config_missing_pw() throws Exception {
assertThrows(
PkiException.class, () -> new HardwareKeyStoreWrapper(null, toPath(config), toPath(crl)));
}
@Test
public void init_keystorePassword_provider_missing_pw() throws Exception {
final Provider p = null;
assertThrows(
PkiException.class,
() -> new HardwareKeyStoreWrapper(validKeystorePassword, p, toPath(crl)));
}
}

@ -1,82 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.keystore;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.when;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class HardwareKeyStoreWrapperTest {
private static final String KEY_ALIAS = "keyalias";
private static final String CERTIFICATE_ALIAS = "certalias";
private static final char[] PASSWORD = "password".toCharArray();
@Mock private KeyStore keyStore;
@Mock private PrivateKey privateKey;
@Mock private PublicKey publicKey;
@Mock private Certificate certificate;
private HardwareKeyStoreWrapper keyStoreWrapper;
@BeforeEach
public void before() {
keyStoreWrapper = new HardwareKeyStoreWrapper(null, keyStore, new String(PASSWORD));
}
@Test
public void getPrivateKey() throws Exception {
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey);
assertNotNull(keyStoreWrapper.getPrivateKey(KEY_ALIAS));
}
@Test
public void getPublicKey() throws Exception {
// Get public key from certificate
when(keyStore.getCertificate(KEY_ALIAS)).thenReturn(certificate);
when(certificate.getPublicKey()).thenReturn(publicKey);
assertNotNull(keyStoreWrapper.getPublicKey(KEY_ALIAS));
}
@Test
public void getCertificate() throws Exception {
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate);
assertNotNull(keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS));
}
@Test
public void getCertificateChain() throws Exception {
when(keyStore.getCertificateChain(CERTIFICATE_ALIAS))
.thenReturn(new Certificate[] {certificate});
assertEquals(keyStoreWrapper.getCertificateChain(CERTIFICATE_ALIAS).length, 1);
}
}

@ -1,80 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.keystore;
import org.hyperledger.besu.pki.PkiException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
public class SoftwareKeyStoreFileWrapperTest extends BaseKeyStoreFileWrapperTest {
private static final String p12KeyStore = "/keystore/partner1client1/keys.p12";
private static final String jksKeyStore = "/keystore/partner1client1/keystore.jks";
private static final String trustStore = "/keystore/partner1client1/truststore.jks";
private static final String crl = "/keystore/partner1client1/crl.pem";
private static final String validKeystorePassword = "test123";
public static Collection<KeyStoreWrapperTestParameter> data() {
return Arrays.asList(
new KeyStoreWrapperTestParameter(
"SoftwareKeyStoreWrapper[PKCS12 keystore only]",
false,
getPKCS12SoftwareKeyStoreWrapper()),
new KeyStoreWrapperTestParameter(
"SoftwareKeyStoreWrapper[JKS keystore only]",
false,
getJKSSoftwareKeyStoreWrapper(false)),
new KeyStoreWrapperTestParameter(
"SoftwareKeyStoreWrapper[JKS keystore/truststore]",
true,
getJKSSoftwareKeyStoreWrapper(true)));
}
private static KeyStoreWrapper getPKCS12SoftwareKeyStoreWrapper() {
try {
return new SoftwareKeyStoreWrapper(
KeyStoreWrapper.KEYSTORE_TYPE_PKCS12,
toPath(p12KeyStore),
validKeystorePassword,
toPath(crl));
} catch (final Exception e) {
throw new PkiException("Failed to initialize software keystore", e);
}
}
private static KeyStoreWrapper getJKSSoftwareKeyStoreWrapper(final boolean setupTruststore) {
try {
final Path keystoreLocation = toPath(jksKeyStore);
if (setupTruststore) {
final Path truststoreLocation = toPath(trustStore);
// password shouldn't be needed for retrieving certificate from truststore
return new SoftwareKeyStoreWrapper(
KeyStoreWrapper.KEYSTORE_TYPE_JKS,
keystoreLocation,
validKeystorePassword,
KeyStoreWrapper.KEYSTORE_TYPE_JKS,
truststoreLocation,
null,
toPath(crl));
}
return new SoftwareKeyStoreWrapper(
KeyStoreWrapper.KEYSTORE_TYPE_JKS, keystoreLocation, validKeystorePassword, toPath(crl));
} catch (final Exception e) {
throw new PkiException("Failed to initialize software keystore", e);
}
}
}

@ -1,198 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.pki.keystore;
import static org.hyperledger.besu.pki.keystore.KeyStoreWrapper.KEYSTORE_TYPE_PKCS12;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class SoftwareKeyStoreWrapperTest {
private static final String KEY_ALIAS = "keyalias";
private static final String CERTIFICATE_ALIAS = "certalias";
private static final char[] PASSWORD = "password".toCharArray();
private SoftwareKeyStoreWrapper keyStoreWrapper;
@Mock private KeyStore keyStore;
@Mock private KeyStore trustStore;
@Mock private PrivateKey privateKey;
@Mock private PublicKey publicKey;
@Mock private Certificate certificate;
@BeforeEach
public void before() {
keyStoreWrapper = new SoftwareKeyStoreWrapper(keyStore, new String(PASSWORD), null, "");
}
@Test
public void getPrivateKey() throws Exception {
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true);
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey);
assertNotNull(keyStoreWrapper.getPrivateKey(KEY_ALIAS));
}
@Test
public void getPrivateKeyCaching() throws Exception {
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true);
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey);
keyStoreWrapper.getPrivateKey(KEY_ALIAS);
keyStoreWrapper.getPrivateKey(KEY_ALIAS);
verify(keyStore, times(1)).getKey(eq(KEY_ALIAS), eq(PASSWORD));
}
@Test
public void getPrivateKeyFallbackToTrustStore() throws Exception {
keyStoreWrapper =
new SoftwareKeyStoreWrapper(
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD));
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false);
when(trustStore.containsAlias(KEY_ALIAS)).thenReturn(true);
when(trustStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey);
assertNotNull(keyStoreWrapper.getPrivateKey(KEY_ALIAS));
verify(trustStore).getKey(eq(KEY_ALIAS), eq(PASSWORD));
}
@Test
public void getPublicKey() throws Exception {
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true);
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(publicKey);
assertNotNull(keyStoreWrapper.getPublicKey(KEY_ALIAS));
}
@Test
public void getPublicKeyCaching() throws Exception {
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true);
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(publicKey);
keyStoreWrapper.getPublicKey(KEY_ALIAS);
keyStoreWrapper.getPublicKey(KEY_ALIAS);
verify(keyStore, times(1)).getKey(eq(KEY_ALIAS), eq(PASSWORD));
}
@Test
public void getPublicKeyFallbackToTrustStore() throws Exception {
keyStoreWrapper =
new SoftwareKeyStoreWrapper(
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD));
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false);
when(trustStore.containsAlias(KEY_ALIAS)).thenReturn(true);
when(trustStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(publicKey);
assertNotNull(keyStoreWrapper.getPublicKey(KEY_ALIAS));
verify(trustStore).getKey(eq(KEY_ALIAS), eq(PASSWORD));
}
@Test
public void getCertificate() throws Exception {
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate);
assertNotNull(keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS));
}
@Test
public void getCertificateCaching() throws Exception {
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate);
keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS);
keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS);
verify(keyStore, times(1)).getCertificate(eq(CERTIFICATE_ALIAS));
}
@Test
public void getCertificateFallbackToTrustStore() throws Exception {
keyStoreWrapper =
new SoftwareKeyStoreWrapper(
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD));
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(null);
when(trustStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate);
assertNotNull(keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS));
verify(trustStore).getCertificate(eq(CERTIFICATE_ALIAS));
}
@Test
public void getCertificateChain() throws Exception {
when(keyStore.getCertificateChain(CERTIFICATE_ALIAS))
.thenReturn(new Certificate[] {certificate});
assertEquals(keyStoreWrapper.getCertificateChain(CERTIFICATE_ALIAS).length, 1);
}
@Test
public void getCertificateChainFallbackToTrustStore() throws Exception {
keyStoreWrapper =
new SoftwareKeyStoreWrapper(
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD));
when(keyStore.getCertificateChain(CERTIFICATE_ALIAS)).thenReturn(null);
when(trustStore.getCertificateChain(CERTIFICATE_ALIAS))
.thenReturn(new Certificate[] {certificate});
assertEquals(keyStoreWrapper.getCertificateChain(CERTIFICATE_ALIAS).length, 1);
verify(trustStore).getCertificateChain(eq(CERTIFICATE_ALIAS));
}
@Test
public void loadKeyStoreFromFile() {
SoftwareKeyStoreWrapper loadedKeyStore =
new SoftwareKeyStoreWrapper(
KEYSTORE_TYPE_PKCS12,
Path.of("src/test/resources/keystore/keystore"),
"validator",
KEYSTORE_TYPE_PKCS12,
Path.of("src/test/resources/keystore/keystore"),
"validator",
null);
assertNotNull(loadedKeyStore.getPublicKey("validator"));
assertNotNull(loadedKeyStore.getPrivateKey("validator"));
assertNotNull(loadedKeyStore.getCertificate("validator"));
// CA -> INTERCA -> PARTNERACA -> VALIDATOR
assertEquals(loadedKeyStore.getCertificateChain("validator").length, 4);
}
}

@ -1,6 +0,0 @@
name = NSScrypto-partner1client1
nssSecmodDirectory = ./src/test/resources/keystore/partner1client1/nssdb
nssDbMode = readOnly
nssModule = keystore
Loading…
Cancel
Save