[BESU-80] TLS to orion (#324)

* enable SSL on connection to enclave

Signed-off-by: Sally MacFarlane <sally.macfarlane@consensys.net>
pull/364/head
Sally MacFarlane 5 years ago committed by GitHub
parent f12c92ec58
commit 9dfed5ab74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 47
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  2. 31
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  3. 6
      besu/src/test/resources/everything_config.toml
  4. 10
      enclave/build.gradle
  5. 53
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsCertificateDefinition.java
  6. 145
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledEnclaveTest.java
  7. 101
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledHttpServerFactory.java
  8. 95
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsHelpers.java
  9. BIN
      enclave/src/integration-test/resources/tls/cert1.pfx
  10. BIN
      enclave/src/integration-test/resources/tls/cert2.pfx
  11. 73
      enclave/src/main/java/org/hyperledger/besu/enclave/EnclaveFactory.java
  12. 30
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java

@ -524,6 +524,31 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
"Require authentication for the JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})") "Require authentication for the JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})")
private final Boolean isRpcWsAuthenticationEnabled = false; private final Boolean isRpcWsAuthenticationEnabled = false;
@Option(
names = {"--privacy-tls-enabled"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "Enable TLS for connecting to privacy enclave (default: ${DEFAULT-VALUE})")
private final Boolean isPrivacyTlsEnabled = false;
@Option(
names = "--privacy-tls-keystore-file",
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"Path to a PKCS#12 formatted keystore; used to enable TLS on inbound connections.")
private final Path privacyKeyStoreFile = null;
@Option(
names = "--privacy-tls-keystore-password-file",
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "Path to a file containing the password used to decrypt the keystore.")
private final Path privacyKeyStorePasswordFile = null;
@Option(
names = "--privacy-tls-known-enclave-file",
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "Path to a file containing the fingerprints of the authorized privacy enclave.")
private final Path privacyTlsKnownEnclaveFile = null;
@Option( @Option(
names = {"--metrics-enabled"}, names = {"--metrics-enabled"},
description = "Set to start the metrics exporter (default: ${DEFAULT-VALUE})") description = "Set to start the metrics exporter (default: ${DEFAULT-VALUE})")
@ -1265,6 +1290,18 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
asList("--rpc-http-tls-known-clients-file", "--rpc-http-tls-ca-clients-enabled")); asList("--rpc-http-tls-known-clients-file", "--rpc-http-tls-ca-clients-enabled"));
} }
private void checkPrivacyTlsOptionsDependencies() {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--privacy-tls-enabled",
!isPrivacyTlsEnabled,
asList(
"--privacy-tls-keystore-file",
"--privacy-tls-keystore-password-file",
"--privacy-tls-known-enclave-file"));
}
private Optional<TlsConfiguration> rpcHttpTlsConfiguration() { private Optional<TlsConfiguration> rpcHttpTlsConfiguration() {
if (!isRpcTlsConfigurationRequired()) { if (!isRpcTlsConfigurationRequired()) {
return Optional.empty(); return Optional.empty();
@ -1492,7 +1529,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
"--privacy-url", "--privacy-url",
"--privacy-public-key-file", "--privacy-public-key-file",
"--privacy-precompiled-address", "--privacy-precompiled-address",
"--privacy-multi-tenancy-enabled")); "--privacy-multi-tenancy-enabled",
"--privacy-tls-enabled"));
checkPrivacyTlsOptionsDependencies();
final PrivacyParameters.Builder privacyParametersBuilder = new PrivacyParameters.Builder(); final PrivacyParameters.Builder privacyParametersBuilder = new PrivacyParameters.Builder();
if (isPrivacyEnabled) { if (isPrivacyEnabled) {
@ -1538,6 +1578,11 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
privacyParametersBuilder.setPrivateKeyPath(privacyMarkerTransactionSigningKeyPath); privacyParametersBuilder.setPrivateKeyPath(privacyMarkerTransactionSigningKeyPath);
privacyParametersBuilder.setStorageProvider( privacyParametersBuilder.setStorageProvider(
privacyKeyStorageProvider(keyValueStorageName + "-privacy")); privacyKeyStorageProvider(keyValueStorageName + "-privacy"));
if (isPrivacyTlsEnabled) {
privacyParametersBuilder.setPrivacyKeyStoreFile(privacyKeyStoreFile);
privacyParametersBuilder.setPrivacyKeyStorePasswordFile(privacyKeyStorePasswordFile);
privacyParametersBuilder.setPrivacyTlsKnownEnclaveFile(privacyTlsKnownEnclaveFile);
}
privacyParametersBuilder.setEnclaveFactory(new EnclaveFactory(vertx)); privacyParametersBuilder.setEnclaveFactory(new EnclaveFactory(vertx));
} else { } else {
if (anyPrivacyApiEnabled()) { if (anyPrivacyApiEnabled()) {

@ -1532,6 +1532,37 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty();
} }
@Test
public void privacyTlsOptionsRequiresTlsToBeEnabled() {
when(storageService.getByName("rocksdb-privacy"))
.thenReturn(Optional.of(rocksDBSPrivacyStorageFactory));
final URL configFile = this.getClass().getResource("/orion_publickey.pub");
parseCommand(
"--privacy-enabled",
"--privacy-url",
ENCLAVE_URI,
"--privacy-public-key-file",
configFile.getPath(),
"--privacy-tls-keystore-file",
"/Users/me/key");
verifyOptionsConstraintLoggerCall("--privacy-tls-enabled", "--privacy-tls-keystore-file");
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void privacyTlsOptionsRequiresPrivacyToBeEnabled() {
parseCommand("--privacy-tls-enabled", "--privacy-tls-keystore-file", "/Users/me/key");
verifyOptionsConstraintLoggerCall("--privacy-enabled", "--privacy-tls-enabled");
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test @Test
public void fastSyncOptionsRequiresFastSyncModeToBeSet() { public void fastSyncOptionsRequiresFastSyncModeToBeSet() {
parseCommand("--fast-sync-min-peers", "5"); parseCommand("--fast-sync-min-peers", "5");

@ -58,6 +58,12 @@ rpc-http-tls-client-auth-enabled=false
rpc-http-tls-known-clients-file="rpc_tls_clients.txt" rpc-http-tls-known-clients-file="rpc_tls_clients.txt"
rpc-http-tls-ca-clients-enabled=false rpc-http-tls-ca-clients-enabled=false
# PRIVACY TLS
privacy-tls-enabled=false
privacy-tls-keystore-file="none.pfx"
privacy-tls-keystore-password-file="none.passwd"
privacy-tls-known-enclave-file="privacy_tls_enclave.txt"
# GRAPHQL HTTP # GRAPHQL HTTP
graphql-http-enabled=false graphql-http-enabled=false
graphql-http-host="6.7.8.9" graphql-http-host="6.7.8.9"

@ -1,8 +1,15 @@
dependencies { dependencies {
api project(':util')
api project(':crypto')
implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'io.vertx:vertx-core' implementation 'io.vertx:vertx-core'
implementation 'org.apache.tuweni:tuweni-net'
implementation 'org.apache.logging.log4j:log4j-api' implementation 'org.apache.logging.log4j:log4j-api'
runtimeOnly('org.bouncycastle:bcpkix-jdk15on')
// test dependencies. // test dependencies.
testImplementation project(':testutil') testImplementation project(':testutil')
@ -10,6 +17,9 @@ dependencies {
// integration test dependencies. // integration test dependencies.
integrationTestImplementation project(':testutil') integrationTestImplementation project(':testutil')
integrationTestImplementation 'org.bouncycastle:bcpkix-jdk15on'
integrationTestImplementation 'junit:junit' integrationTestImplementation 'junit:junit'
integrationTestImplementation 'net.consensys:orion' integrationTestImplementation 'net.consensys:orion'

@ -0,0 +1,53 @@
/*
* 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.enclave;
import java.io.File;
import java.net.URL;
import java.nio.file.Path;
import com.google.common.io.Resources;
public class TlsCertificateDefinition {
private final File pkcs12File;
private final String password;
public static TlsCertificateDefinition loadFromResource(
final String resourcePath, final String password) {
try {
final URL sslCertificate = Resources.getResource(resourcePath);
final Path keystorePath = Path.of(sslCertificate.getPath());
return new TlsCertificateDefinition(keystorePath.toFile(), password);
} catch (final Exception e) {
throw new RuntimeException("Failed to load TLS certificates", e);
}
}
public TlsCertificateDefinition(final File pkcs12File, final String password) {
this.pkcs12File = pkcs12File;
this.password = password;
}
public File getPkcs12File() {
return pkcs12File;
}
public String getPassword() {
return password;
}
}

@ -0,0 +1,145 @@
/*
* 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.enclave;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.hyperledger.besu.enclave.TlsHelpers.populateFingerprintFile;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Optional;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TlsEnabledEnclaveTest {
private TlsEnabledHttpServerFactory serverFactory;
private Vertx vertx;
final TlsCertificateDefinition httpServerCert =
TlsCertificateDefinition.loadFromResource("tls/cert1.pfx", "password");
final TlsCertificateDefinition besuCert =
TlsCertificateDefinition.loadFromResource("tls/cert2.pfx", "password2");
public void shutdown() {
vertx.close();
}
@Before
public void setup() {
serverFactory = new TlsEnabledHttpServerFactory();
this.vertx = Vertx.vertx();
}
@After
public void cleanup() {
serverFactory.shutdown();
this.shutdown();
}
private Enclave createEnclave(
final int httpServerPort, final Path workDir, final boolean tlsEnabled) throws IOException {
final Path serverFingerprintFile = workDir.resolve("server_known_clients");
final Path besuCertPasswordFile = workDir.resolve("password_file");
try {
populateFingerprintFile(serverFingerprintFile, httpServerCert, Optional.of(httpServerPort));
Files.write(besuCertPasswordFile, besuCert.getPassword().getBytes(Charset.defaultCharset()));
final EnclaveFactory factory = new EnclaveFactory(vertx);
if (tlsEnabled) {
final URI httpServerUri = new URI("https://localhost:" + httpServerPort);
return factory.createVertxEnclave(
httpServerUri,
besuCert.getPkcs12File().toPath(),
besuCertPasswordFile,
serverFingerprintFile);
} else {
return factory.createVertxEnclave(new URI("http://localhost:" + httpServerPort));
}
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
fail("unable to populate fingerprint file");
return null;
} catch (URISyntaxException e) {
fail("unable to create URI");
return null;
}
}
@Test
public void nonTlsEnclaveCannotConnectToTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, true);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, false);
assertThat(enclave.upCheck()).isEqualTo(false);
}
@Test
public void nonTlsEnclaveCanConnectToNonTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, false);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, false);
assertThat(enclave.upCheck()).isEqualTo(true);
}
@Test
public void tlsEnclaveCannotConnectToNonTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up!".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, false);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, true);
assertThat(enclave.upCheck()).isEqualTo(false);
}
@Test
public void tlsEnclaveCanConnectToTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, true);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, true);
assertThat(enclave.upCheck()).isEqualTo(true);
}
}

@ -0,0 +1,101 @@
/*
* 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.enclave;
import static org.hyperledger.besu.enclave.TlsHelpers.populateFingerprintFile;
import java.io.IOException;
import java.nio.file.Path;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import com.google.common.collect.Lists;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.Vertx;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.net.PfxOptions;
import io.vertx.ext.web.Router;
import org.apache.tuweni.net.tls.VertxTrustOptions;
public class TlsEnabledHttpServerFactory {
private final Vertx vertx;
private final List<HttpServer> serversCreated = Lists.newArrayList();
public TlsEnabledHttpServerFactory() {
this.vertx = Vertx.vertx();
}
public void shutdown() {
serversCreated.forEach(HttpServer::close);
vertx.close();
}
public HttpServer create(
final TlsCertificateDefinition serverCert,
final TlsCertificateDefinition acceptedClientCerts,
final Path workDir,
final boolean tlsEnabled) {
try {
final Path serverFingerprintFile = workDir.resolve("server_known_clients");
populateFingerprintFile(serverFingerprintFile, acceptedClientCerts, Optional.empty());
final HttpServerOptions web3HttpServerOptions = new HttpServerOptions();
web3HttpServerOptions.setPort(0);
if (tlsEnabled) {
web3HttpServerOptions.setSsl(true);
web3HttpServerOptions.setClientAuth(ClientAuth.REQUIRED);
web3HttpServerOptions.setTrustOptions(
VertxTrustOptions.whitelistClients(serverFingerprintFile));
web3HttpServerOptions.setPfxKeyCertOptions(
new PfxOptions()
.setPath(serverCert.getPkcs12File().toString())
.setPassword(serverCert.getPassword()));
}
final Router router = Router.router(vertx);
router
.route(HttpMethod.GET, "/upcheck")
.produces(HttpHeaderValues.APPLICATION_JSON.toString())
.handler(context -> context.response().end("I'm up!"));
final HttpServer mockOrionHttpServer = vertx.createHttpServer(web3HttpServerOptions);
final CompletableFuture<Boolean> serverConfigured = new CompletableFuture<>();
mockOrionHttpServer.requestHandler(router).listen(result -> serverConfigured.complete(true));
serverConfigured.get();
serversCreated.add(mockOrionHttpServer);
return mockOrionHttpServer;
} catch (KeyStoreException
| NoSuchAlgorithmException
| CertificateException
| IOException
| ExecutionException
| InterruptedException e) {
throw new RuntimeException("Failed to construct a TLS Enabled Server", e);
}
}
}

@ -0,0 +1,95 @@
/*
* 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.enclave;
import org.hyperledger.besu.crypto.MessageDigestFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import com.google.common.collect.Lists;
public class TlsHelpers {
private static KeyStore loadP12KeyStore(final File pkcsFile, final String password)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
final KeyStore store = KeyStore.getInstance("pkcs12");
try (final InputStream keystoreStream = new FileInputStream(pkcsFile)) {
store.load(keystoreStream, password.toCharArray());
} catch (IOException e) {
throw new RuntimeException("Unable to load keystore.", e);
}
return store;
}
public static void populateFingerprintFile(
final Path knownClientsPath,
final TlsCertificateDefinition certDef,
final Optional<Integer> serverPortToAppendToHostname)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
final List<X509Certificate> certs = getCertsFromPkcs12(certDef);
final StringBuilder fingerprintsToAdd = new StringBuilder();
final String portFragment = serverPortToAppendToHostname.map(port -> ":" + port).orElse("");
for (final X509Certificate cert : certs) {
final String fingerprint = generateFingerprint(cert);
fingerprintsToAdd.append(String.format("localhost%s %s%n", portFragment, fingerprint));
fingerprintsToAdd.append(String.format("127.0.0.1%s %s%n", portFragment, fingerprint));
}
Files.writeString(knownClientsPath, fingerprintsToAdd.toString());
}
public static List<X509Certificate> getCertsFromPkcs12(final TlsCertificateDefinition certDef)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
final List<X509Certificate> results = Lists.newArrayList();
final KeyStore p12 = loadP12KeyStore(certDef.getPkcs12File(), certDef.getPassword());
final Enumeration<String> aliases = p12.aliases();
while (aliases.hasMoreElements()) {
results.add((X509Certificate) p12.getCertificate(aliases.nextElement()));
}
return results;
}
private static String generateFingerprint(final X509Certificate cert)
throws NoSuchAlgorithmException, CertificateEncodingException {
final MessageDigest md = MessageDigestFactory.create("SHA-256");
md.update(cert.getEncoded());
final byte[] digest = md.digest();
final StringJoiner joiner = new StringJoiner(":");
for (final byte b : digest) {
joiner.add(String.format("%02X", b));
}
return joiner.toString().toLowerCase();
}
}

@ -14,21 +14,42 @@
*/ */
package org.hyperledger.besu.enclave; package org.hyperledger.besu.enclave;
import org.hyperledger.besu.util.InvalidConfigurationException;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import com.google.common.base.Charsets;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.net.PfxOptions;
import org.apache.tuweni.net.tls.VertxTrustOptions;
public class EnclaveFactory { public class EnclaveFactory {
private final Vertx vertx; private final Vertx vertx;
private static final int CONNECT_TIMEOUT = 1000; private static final int CONNECT_TIMEOUT = 1000;
private static final boolean TRUST_CA = false;
public EnclaveFactory(final Vertx vertx) { public EnclaveFactory(final Vertx vertx) {
this.vertx = vertx; this.vertx = vertx;
} }
public Enclave createVertxEnclave(final URI enclaveUri) { public Enclave createVertxEnclave(final URI enclaveUri) {
final HttpClientOptions clientOptions = createNonTlsClientOptions(enclaveUri);
final RequestTransmitter vertxTransmitter =
new VertxRequestTransmitter(vertx.createHttpClient(clientOptions));
return new Enclave(vertxTransmitter);
}
private HttpClientOptions createNonTlsClientOptions(final URI enclaveUri) {
if (enclaveUri.getPort() == -1) { if (enclaveUri.getPort() == -1) {
throw new EnclaveIOException("Illegal URI - no port specified"); throw new EnclaveIOException("Illegal URI - no port specified");
} }
@ -37,10 +58,62 @@ public class EnclaveFactory {
clientOptions.setDefaultHost(enclaveUri.getHost()); clientOptions.setDefaultHost(enclaveUri.getHost());
clientOptions.setDefaultPort(enclaveUri.getPort()); clientOptions.setDefaultPort(enclaveUri.getPort());
clientOptions.setConnectTimeout(CONNECT_TIMEOUT); clientOptions.setConnectTimeout(CONNECT_TIMEOUT);
return clientOptions;
}
private HttpClientOptions createTlsClientOptions(
final URI enclaveUri,
final Path privacyKeyStoreFile,
final Path privacyKeyStorePasswordFile,
final Path privacyWhitelistFile) {
final HttpClientOptions clientOptions = createNonTlsClientOptions(enclaveUri);
try {
if (privacyKeyStoreFile != null && privacyKeyStorePasswordFile != null) {
clientOptions.setSsl(true);
clientOptions.setPfxKeyCertOptions(
convertFrom(privacyKeyStoreFile, privacyKeyStorePasswordFile));
}
clientOptions.setTrustOptions(
VertxTrustOptions.whitelistServers(privacyWhitelistFile, TRUST_CA));
} catch (final NoSuchFileException e) {
throw new InvalidConfigurationException(
"Requested file " + e.getMessage() + " does not exist at specified location.");
} catch (final AccessDeniedException e) {
throw new InvalidConfigurationException(
"Current user does not have permissions to access " + e.getMessage());
} catch (final IllegalArgumentException e) {
throw new InvalidConfigurationException("Illegally formatted client fingerprint file.");
} catch (final IOException e) {
throw new InvalidConfigurationException("Failed to load TLS files " + e.getMessage());
}
return clientOptions;
}
public Enclave createVertxEnclave(
final URI enclaveUri,
final Path privacyKeyStoreFile,
final Path privacyKeyStorePasswordFile,
final Path privacyWhitelistFile) {
final HttpClientOptions clientOptions =
createTlsClientOptions(
enclaveUri, privacyKeyStoreFile, privacyKeyStorePasswordFile, privacyWhitelistFile);
final RequestTransmitter vertxTransmitter = final RequestTransmitter vertxTransmitter =
new VertxRequestTransmitter(vertx.createHttpClient(clientOptions)); new VertxRequestTransmitter(vertx.createHttpClient(clientOptions));
return new Enclave(vertxTransmitter); return new Enclave(vertxTransmitter);
} }
private static PfxOptions convertFrom(final Path keystoreFile, final Path keystorePasswordFile)
throws IOException {
final String password = readSecretFromFile(keystorePasswordFile);
return new PfxOptions().setPassword(password).setPath(keystoreFile.toString());
}
private static String readSecretFromFile(final Path path) throws IOException {
final byte[] fileContent = Files.readAllBytes(path);
return new String(fileContent, Charsets.UTF_8);
}
} }

@ -165,6 +165,9 @@ public class PrivacyParameters {
private PrivacyStorageProvider storageProvider; private PrivacyStorageProvider storageProvider;
private EnclaveFactory enclaveFactory; private EnclaveFactory enclaveFactory;
private boolean multiTenancyEnabled; private boolean multiTenancyEnabled;
private Path privacyKeyStoreFile;
private Path privacyKeyStorePasswordFile;
private Path privacyTlsKnownEnclaveFile;
public Builder setPrivacyAddress(final Integer privacyAddress) { public Builder setPrivacyAddress(final Integer privacyAddress) {
this.privacyAddress = privacyAddress; this.privacyAddress = privacyAddress;
@ -201,6 +204,21 @@ public class PrivacyParameters {
return this; return this;
} }
public Builder setPrivacyKeyStoreFile(final Path privacyKeyStoreFile) {
this.privacyKeyStoreFile = privacyKeyStoreFile;
return this;
}
public Builder setPrivacyKeyStorePasswordFile(final Path privacyKeyStorePasswordFile) {
this.privacyKeyStorePasswordFile = privacyKeyStorePasswordFile;
return this;
}
public Builder setPrivacyTlsKnownEnclaveFile(final Path privacyTlsKnownEnclaveFile) {
this.privacyTlsKnownEnclaveFile = privacyTlsKnownEnclaveFile;
return this;
}
public PrivacyParameters build() { public PrivacyParameters build() {
final PrivacyParameters config = new PrivacyParameters(); final PrivacyParameters config = new PrivacyParameters();
if (enabled) { if (enabled) {
@ -218,7 +236,17 @@ public class PrivacyParameters {
config.setEnclavePublicKeyFile(enclavePublicKeyFile); config.setEnclavePublicKeyFile(enclavePublicKeyFile);
config.setPrivateStorageProvider(storageProvider); config.setPrivateStorageProvider(storageProvider);
config.setPrivateStateStorage(privateStateStorage); config.setPrivateStateStorage(privateStateStorage);
config.setEnclave(enclaveFactory.createVertxEnclave(enclaveUrl)); // pass TLS options to enclave factory if they are set
if (privacyKeyStoreFile != null) {
config.setEnclave(
enclaveFactory.createVertxEnclave(
enclaveUrl,
privacyKeyStoreFile,
privacyKeyStorePasswordFile,
privacyTlsKnownEnclaveFile));
} else {
config.setEnclave(enclaveFactory.createVertxEnclave(enclaveUrl));
}
if (privateKeyPath != null) { if (privateKeyPath != null) {
config.setSigningKeyPair(KeyPairUtil.load(privateKeyPath.toFile())); config.setSigningKeyPair(KeyPairUtil.load(privateKeyPath.toFile()));

Loading…
Cancel
Save