mirror of https://github.com/hyperledger/besu
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
parent
e4df70a350
commit
12723ace68
@ -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()); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
Binary file not shown.
Binary file not shown.
@ -1,6 +0,0 @@ |
||||
|
||||
name = NSScrypto-partner1client1 |
||||
nssSecmodDirectory = ./src/test/resources/keystore/partner1client1/nssdb |
||||
nssDbMode = readOnly |
||||
nssModule = keystore |
||||
|
Binary file not shown.
Loading…
Reference in new issue