Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for PKCS11 keystore on PKI Block Creation #2865

Merged
merged 6 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Additions and Improvements
- Add CLI autocomplete scripts. [#2854](https://github.com/hyperledger/besu/pull/2854)
- Added support for PKCS11 keystore on PKI Block Creation. [#2865](https://github.com/hyperledger/besu/pull/2865)

### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,17 @@
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationProvider;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.pki.PKCS11Utils;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import com.google.common.base.Charsets;

public class BesuNodeConfigurationBuilder {

private String name;
Expand Down Expand Up @@ -273,14 +270,14 @@ private static Path toPath(final String path) throws Exception {
public BesuNodeConfigurationBuilder p2pTLSEnabled(final String name, final String type) {
final TLSConfiguration.Builder builder = TLSConfiguration.Builder.tlsConfiguration();
try {
final String nsspin = "/p2p-tls/%s/nsspin.txt";
final String truststore = "/p2p-tls/%s/truststore.jks";
final String crl = "/p2p-tls/%s/crl.pem";
final String nsspin = "/pki-certs/%s/nsspin.txt";
final String truststore = "/pki-certs/%s/truststore.jks";
final String crl = "/pki-certs/%s/crl.pem";
switch (type) {
case KeyStoreWrapper.KEYSTORE_TYPE_JKS:
builder
.withKeyStoreType(type)
.withKeyStorePath(toPath(String.format("/p2p-tls/%s/keystore.jks", name)))
.withKeyStorePath(toPath(String.format("/pki-certs/%s/keystore.jks", name)))
.withKeyStorePasswordSupplier(
new FileBasedPasswordProvider(toPath(String.format(nsspin, name))))
.withKeyStorePasswordPath(toPath(String.format(nsspin, name)))
Expand All @@ -294,7 +291,7 @@ public BesuNodeConfigurationBuilder p2pTLSEnabled(final String name, final Strin
case KeyStoreWrapper.KEYSTORE_TYPE_PKCS12:
builder
.withKeyStoreType(type)
.withKeyStorePath(toPath(String.format("/p2p-tls/%s/keys.p12", name)))
.withKeyStorePath(toPath(String.format("/pki-certs/%s/keys.p12", name)))
.withKeyStorePasswordSupplier(
new FileBasedPasswordProvider(toPath(String.format(nsspin, name))))
.withKeyStorePasswordPath(toPath(String.format(nsspin, name)))
Expand All @@ -309,7 +306,8 @@ public BesuNodeConfigurationBuilder p2pTLSEnabled(final String name, final Strin
builder
.withKeyStoreType(type)
.withKeyStorePath(
initNSSConfigFile(toPath(String.format("/p2p-tls/%s/nss.cfg", name))))
PKCS11Utils.initNSSConfigFile(
toPath(String.format("/pki-certs/%s/nss.cfg", name))))
.withKeyStorePasswordSupplier(
new FileBasedPasswordProvider(toPath(String.format(nsspin, name))))
.withKeyStorePasswordPath(toPath(String.format(nsspin, name)))
Expand All @@ -323,34 +321,6 @@ public BesuNodeConfigurationBuilder p2pTLSEnabled(final String name, final Strin
return this;
}

private Path initNSSConfigFile(final Path srcFilePath) {
Path ret = null;
try {
final String content = Files.readString(srcFilePath);
final String updated =
content.replaceAll(
"(nssSecmodDirectory\\W*)(\\.\\/.*)",
"$1".concat(srcFilePath.toAbsolutePath().toString().replace("nss.cfg", "nssdb")));
final Path targetFilePath = createTemporaryFile("nsscfg");
Files.write(targetFilePath, updated.getBytes(Charsets.UTF_8));
ret = targetFilePath;
} catch (IOException e) {
throw new RuntimeException("Error populating nss config file", e);
}
return ret;
}

private Path createTemporaryFile(final String suffix) {
final File tempFile;
try {
tempFile = File.createTempFile("temp", suffix);
tempFile.deleteOnExit();
} catch (IOException e) {
throw new RuntimeException("Error creating temporary file", e);
}
return tempFile.toPath();
}

public BesuNodeConfigurationBuilder pkiBlockCreationEnabled(
final PkiKeyStoreConfiguration pkiKeyStoreConfiguration) {
this.pkiKeyStoreConfiguration = Optional.of(pkiKeyStoreConfiguration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,19 @@ public BesuNode createQbftNode(final String name) throws IOException {
.build());
}

public BesuNode createPkiQbftNode(final String name) throws IOException {
public BesuNode createPkiQbftJKSNode(final String name) throws IOException {
return createPkiQbftNode(KeyStoreWrapper.KEYSTORE_TYPE_JKS, name);
}

public BesuNode createPkiQbftPKCS11Node(final String name) throws IOException {
return createPkiQbftNode(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, name);
}

public BesuNode createPkiQbftPKCS12Node(final String name) throws IOException {
return createPkiQbftNode(KeyStoreWrapper.KEYSTORE_TYPE_PKCS12, name);
}

public BesuNode createPkiQbftNode(final String type, final String name) throws IOException {
return create(
new BesuNodeConfigurationBuilder()
.name(name)
Expand All @@ -406,7 +418,7 @@ public BesuNode createPkiQbftNode(final String name) throws IOException {
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(genesis::createQbftGenesisConfig)
.pkiBlockCreationEnabled(pkiKeystoreConfigurationFactory.createPkiConfig())
.pkiBlockCreationEnabled(pkiKeystoreConfigurationFactory.createPkiConfig(type, name))
.build());
}

Expand Down Expand Up @@ -522,8 +534,23 @@ public BesuNode createQbftNodeWithValidators(final String name, final String...
.build());
}

public BesuNode createPkiQbftNodeWithValidators(final String name, final String... validators)
public BesuNode createPkiQbftJKSNodeWithValidators(final String name, final String... validators)
throws IOException {
return createPkiQbftNodeWithValidators(KeyStoreWrapper.KEYSTORE_TYPE_JKS, name, validators);
}

public BesuNode createPkiQbftPKCS11NodeWithValidators(
final String name, final String... validators) throws IOException {
return createPkiQbftNodeWithValidators(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, name, validators);
}

public BesuNode createPkiQbftPKCS12NodeWithValidators(
final String name, final String... validators) throws IOException {
return createPkiQbftNodeWithValidators(KeyStoreWrapper.KEYSTORE_TYPE_PKCS12, name, validators);
}

public BesuNode createPkiQbftNodeWithValidators(
final String type, final String name, final String... validators) throws IOException {

return create(
new BesuNodeConfigurationBuilder()
Expand All @@ -532,7 +559,7 @@ public BesuNode createPkiQbftNodeWithValidators(final String name, final String.
.jsonRpcConfiguration(node.createJsonRpcWithQbftEnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.pkiBlockCreationEnabled(pkiKeystoreConfigurationFactory.createPkiConfig())
.pkiBlockCreationEnabled(pkiKeystoreConfigurationFactory.createPkiConfig(type, name))
.genesisConfigProvider(
nodes ->
node.createGenesisConfigForValidators(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.tests.acceptance.dsl.node.configuration.pki;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import com.google.common.base.Charsets;

public class PKCS11Utils {

public static Path initNSSConfigFile(final Path srcFilePath) {
Path ret = null;
try {
final String content = Files.readString(srcFilePath);
final String updated =
content.replaceAll(
"(nssSecmodDirectory\\W*)(\\.\\/.*)",
"$1".concat(srcFilePath.toAbsolutePath().toString().replace("nss.cfg", "nssdb")));
final Path targetFilePath = createTemporaryFile("nsscfg");
Files.write(targetFilePath, updated.getBytes(Charsets.UTF_8));
ret = targetFilePath;
} catch (IOException e) {
throw new RuntimeException("Error populating nss config file", e);
}
return ret;
}

private static Path createTemporaryFile(final String suffix) {
final File tempFile;
try {
tempFile = File.createTempFile("temp", suffix);
tempFile.deleteOnExit();
} catch (IOException e) {
throw new RuntimeException("Error creating temporary file", e);
}
return tempFile.toPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static org.hyperledger.besu.pki.util.TestCertificateUtils.issueCertificate;

import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;

import java.io.FileOutputStream;
import java.io.IOException;
Expand All @@ -35,44 +36,87 @@
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 {

public static final String KEYSTORE_DEFAULT_TYPE = "PKCS12";
/*
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.jks";
final String CRL_PATH_STRING = "/pki-certs/%s/crl.pem";

/*
Software keystore config
*/
public static final String KEYSTORE_DEFAULT_PASSWORD = "password";
public static final String KEYSTORE_DEFAULT_CERT_ALIAS = "validator";

private KeyPair caKeyPair;
private X509Certificate caCertificate;
private Path trustStoreFile;
private Path passwordFile;

public PkiKeyStoreConfiguration createPkiConfig() {
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_JKS)
.withTrustStorePath(readResourceAsPath(String.format(TRUSTSTORE_PATH_STRING, name)))
.withTrustStorePasswordPath(readResourceAsPath(String.format(NSSPIN_PATH_STRING, name)))
.withCrlFilePath(readResourceAsPath(String.format(CRL_PATH_STRING, name)))
.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(KEYSTORE_DEFAULT_TYPE);
pkiKeyStoreConfigBuilder.withTrustStorePath(createTrustStore());
pkiKeyStoreConfigBuilder.withTrustStoreType(type);
pkiKeyStoreConfigBuilder.withTrustStorePath(createTrustStore(type));
pkiKeyStoreConfigBuilder.withTrustStorePasswordPath(passwordFile);

pkiKeyStoreConfigBuilder.withKeyStoreType(KEYSTORE_DEFAULT_TYPE);
pkiKeyStoreConfigBuilder.withKeyStorePath(createKeyStore());
pkiKeyStoreConfigBuilder.withKeyStoreType(type);
pkiKeyStoreConfigBuilder.withKeyStorePath(createKeyStore(type, name));
pkiKeyStoreConfigBuilder.withKeyStorePasswordPath(passwordFile);

pkiKeyStoreConfigBuilder.withCertificateAlias(KEYSTORE_DEFAULT_CERT_ALIAS);
pkiKeyStoreConfigBuilder.withCertificateAlias(name);

return pkiKeyStoreConfigBuilder.build();
}

private Path createTrustStore() {
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();
caCertificate = createSelfSignedCertificate("ca", notBefore(), notAfter(), caKeyPair);

final KeyStore truststore = KeyStore.getInstance(KEYSTORE_DEFAULT_TYPE);
final KeyStore truststore = KeyStore.getInstance(type);
truststore.load(null, null);
truststore.setCertificateEntry("ca", caCertificate);

Expand All @@ -87,20 +131,20 @@ private Path createTrustStore() {
return trustStoreFile;
}

private Path createKeyStore() {
private Path createKeyStore(final String type, final String alias) {
if (caKeyPair == null) {
createTrustStore();
createTrustStore(type);
}

final KeyPair kp = createKeyPair();
final X509Certificate certificate =
issueCertificate(caCertificate, caKeyPair, "validator", notBefore(), notAfter(), kp, false);

try {
final KeyStore keyStore = KeyStore.getInstance(KEYSTORE_DEFAULT_TYPE);
final KeyStore keyStore = KeyStore.getInstance(type);
keyStore.load(null, null);
keyStore.setKeyEntry(
"validator",
alias,
kp.getPrivate(),
KEYSTORE_DEFAULT_PASSWORD.toCharArray(),
new Certificate[] {certificate, caCertificate});
Expand Down Expand Up @@ -144,4 +188,8 @@ private Instant notBefore() {
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());
}
}
Loading