You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ad...@apache.org on 2021/07/08 14:28:51 UTC
[jackrabbit-oak] branch 1.22 updated: OAK-9451 - Cold Standby SSL
certificates should be configurable Backport from trunk (Oak-9451 Oak-9473)
This is an automated email from the ASF dual-hosted git repository.
adulceanu pushed a commit to branch 1.22
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/1.22 by this push:
new e7dc8b2 OAK-9451 - Cold Standby SSL certificates should be configurable Backport from trunk (Oak-9451 Oak-9473)
e7dc8b2 is described below
commit e7dc8b2122ae0a0fd7fa29c87ac1156c6b9d6154
Author: Andrei Dulceanu <ad...@apache.org>
AuthorDate: Fri Jun 4 15:45:52 2021 +0000
OAK-9451 - Cold Standby SSL certificates should be configurable
Backport from trunk (Oak-9451 Oak-9473)
---
.../jackrabbit/oak/fixture/SegmentTarFixture.java | 12 +-
.../oak/segment/standby/client/StandbyClient.java | 109 ++-
.../segment/standby/client/StandbyClientSync.java | 121 ++-
.../segment/standby/netty/SSLSubjectMatcher.java | 59 ++
.../oak/segment/standby/server/StandbyServer.java | 55 +-
.../segment/standby/server/StandbyServerSync.java | 61 +-
.../segment/standby/store/StandbyStoreService.java | 86 ++-
.../oak/segment/standby/BrokenNetworkIT.java | 45 +-
.../oak/segment/standby/DataStoreTestBase.java | 71 +-
.../oak/segment/standby/FailoverIPRangeIT.java | 10 +-
.../standby/FailoverMultipleClientsTestIT.java | 20 +-
.../oak/segment/standby/FailoverSslTestIT.java | 30 +-
.../jackrabbit/oak/segment/standby/MBeanIT.java | 32 +-
.../oak/segment/standby/RecoverTestIT.java | 10 +-
.../segment/standby/StandbySegmentBlobTestIT.java | 10 +-
.../oak/segment/standby/StandbyTestIT.java | 812 ++++++++++++++++++++-
.../jackrabbit/oak/segment/standby/TestBase.java | 288 ++++++++
.../oak/segment/standby/server/SlowServerIT.java | 10 +-
18 files changed, 1770 insertions(+), 71 deletions(-)
diff --git a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java
index 0b8ad8b..a61db8e 100644
--- a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java
+++ b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/fixture/SegmentTarFixture.java
@@ -324,8 +324,16 @@ public class SegmentTarFixture extends OakFixture {
.withBlobChunkSize(1 * MB)
.withSecureConnection(secure)
.build();
- clientSyncs[i] = new StandbyClientSync("127.0.0.1", port, stores[n + i], secure, DEFAULT_TIMEOUT, false, new File(StandardSystemProperty.JAVA_IO_TMPDIR.value()));
-
+ clientSyncs[i] = StandbyClientSync.builder()
+ .withHost("127.0.0.1")
+ .withPort(port)
+ .withFileStore(stores[n + 1])
+ .withSecureConnection(secure)
+ .withReadTimeoutMs(DEFAULT_TIMEOUT)
+ .withAutoClean(false)
+ .withSpoolFolder(new File(StandardSystemProperty.JAVA_IO_TMPDIR.value()))
+ .build();
+
if (!oneShotRun) {
serverSyncs[i].start();
clientSyncs[i].start();
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java
index 9073cd6..3e92a73 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java
@@ -23,8 +23,6 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
-import javax.net.ssl.SSLException;
-
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
@@ -54,12 +52,93 @@ import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequest;
import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequestEncoder;
import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponse;
import org.apache.jackrabbit.oak.segment.standby.codec.ResponseDecoder;
+import org.apache.jackrabbit.oak.segment.standby.netty.SSLSubjectMatcher;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class StandbyClient implements AutoCloseable {
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+
+ private String host;
+ private int port;
+ private NioEventLoopGroup group;
+ private String clientId;
+ private boolean secure;
+ private int readTimeoutMs;
+ private File spoolFolder;
+ private String sslKeyFile;
+ private String sslKeyPassword;
+ private String sslChainFile;
+ public String sslSubjectPattern;
+
+ private Builder() {}
+
+ public Builder withHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ public Builder withPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public Builder withGroup(NioEventLoopGroup group) {
+ this.group = group;
+ return this;
+ }
+
+ public Builder withClientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ public Builder withSecure(boolean secure) {
+ this.secure = secure;
+ return this;
+ }
+
+ public Builder withReadTimeoutMs(int readTimeoutMs) {
+ this.readTimeoutMs = readTimeoutMs;
+ return this;
+ }
+
+ public Builder withSpoolFolder(File spoolFolder) {
+ this.spoolFolder = spoolFolder;
+ return this;
+ }
+
+ public Builder withSSLKeyFile(String sslKeyFile) {
+ this.sslKeyFile = sslKeyFile;
+ return this;
+ }
+
+ public Builder withSSLKeyPassword(String sslKeyPassword) {
+ this.sslKeyPassword = sslKeyPassword;
+ return this;
+ }
+
+ public Builder withSSLChainFile(String sslChainFile) {
+ this.sslChainFile = sslChainFile;
+ return this;
+ }
+
+ public Builder withSSLSubjectPattern(String sslServerSubjectPattern) {
+ this.sslSubjectPattern = sslServerSubjectPattern;
+ return this;
+ }
+
+ public StandbyClient build() throws InterruptedException {
+ return new StandbyClient(this);
+ }
+ }
+
private static final Logger log = LoggerFactory.getLogger(StandbyClient.class);
private final BlockingQueue<GetHeadResponse> headQueue = new LinkedBlockingDeque<>();
@@ -76,12 +155,12 @@ class StandbyClient implements AutoCloseable {
private Channel channel;
- StandbyClient(String host, int port, NioEventLoopGroup group, String clientId, boolean secure, int readTimeoutMs, File spoolFolder) throws InterruptedException {
- this.clientId = clientId;
- this.readTimeoutMs = readTimeoutMs;
+ StandbyClient(Builder builder) throws InterruptedException {
+ this.clientId = builder.clientId;
+ this.readTimeoutMs = builder.readTimeoutMs;
Bootstrap b = new Bootstrap()
- .group(group)
+ .group(builder.group)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, readTimeoutMs)
.option(ChannelOption.TCP_NODELAY, true)
@@ -93,8 +172,18 @@ class StandbyClient implements AutoCloseable {
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
- if (secure) {
- p.addLast(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build().newHandler(ch.alloc()));
+ if (builder.secure) {
+ SslContext sslContext;
+ if (builder.sslKeyFile != null && !"".equals(builder.sslKeyFile)) {
+ sslContext = SslContextBuilder.forClient().keyManager(new File(builder.sslChainFile), new File(builder.sslKeyFile), builder.sslKeyPassword).build();
+ } else {
+ sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
+ }
+ p.addLast("ssl", sslContext.newHandler(ch.alloc()));
+
+ if (builder.sslSubjectPattern != null) {
+ p.addLast(new SSLSubjectMatcher(builder.sslSubjectPattern));
+ }
}
p.addLast(new ReadTimeoutHandler(readTimeoutMs, TimeUnit.MILLISECONDS));
@@ -106,7 +195,7 @@ class StandbyClient implements AutoCloseable {
// The frame length limits the chunk size to max. 2.2GB
p.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4));
- p.addLast(new ResponseDecoder(spoolFolder));
+ p.addLast(new ResponseDecoder(builder.spoolFolder));
// Encoders
@@ -130,7 +219,7 @@ class StandbyClient implements AutoCloseable {
});
- channel = b.connect(host, port).sync().channel();
+ channel = b.connect(builder.host, builder.port).sync().channel();
}
@Override
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java
index a5b0901..ebca61d 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientSync.java
@@ -45,6 +45,86 @@ import org.slf4j.LoggerFactory;
public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnable, Closeable {
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private String host;
+ private int port;
+ private FileStore fileStore;
+ private boolean secure;
+ private int readTimeoutMs;
+ private boolean autoClean;
+ private File spoolFolder;
+ private String sslKeyFile;
+ private String sslKeyPassword;
+ private String sslChainFile;
+ private String sslSubjectPattern;
+
+ private Builder() {}
+
+ public Builder withHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ public Builder withPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public Builder withFileStore(FileStore fileStore) {
+ this.fileStore = fileStore;
+ return this;
+ }
+
+ public Builder withSecureConnection(boolean secure) {
+ this.secure = secure;
+ return this;
+ }
+
+ public Builder withReadTimeoutMs(int readTimeoutMs) {
+ this.readTimeoutMs = readTimeoutMs;
+ return this;
+ }
+
+ public Builder withAutoClean(boolean autoClean) {
+ this.autoClean = autoClean;
+ return this;
+ }
+
+ public Builder withSpoolFolder(File spoolFolder) {
+ this.spoolFolder = spoolFolder;
+ return this;
+ }
+
+ public Builder withSSLKeyFile(String sslKeyFile) {
+ this.sslKeyFile = sslKeyFile;
+ return this;
+ }
+
+ public Builder withSSLKeyPassword(String sslKeyPassword) {
+ this.sslKeyPassword = sslKeyPassword;
+ return this;
+ }
+
+ public Builder withSSLChainFile(String sslChainFile) {
+ this.sslChainFile = sslChainFile;
+ return this;
+ }
+
+ public Builder withSSLSubjectPattern(String sslSubjectPattern) {
+ this.sslSubjectPattern = sslSubjectPattern;
+ return this;
+ }
+
+ public StandbyClientSync build() {
+ return new StandbyClientSync(this);
+ }
+ }
+
public static final String CLIENT_ID_PROPERTY_NAME = "standbyID";
private static final Logger log = LoggerFactory.getLogger(StandbyClientSync.class);
@@ -73,6 +153,14 @@ public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnab
private final AtomicBoolean active = new AtomicBoolean(false);
+ private final String sslKeyFile;
+
+ private final String sslKeyPassword;
+
+ private final String sslChainFile;
+
+ private final String sslSubjectPattern;
+
private int failedRequests;
private long lastSuccessfulRequest;
@@ -95,22 +183,26 @@ public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnab
return s;
}
- public StandbyClientSync(String host, int port, FileStore store, boolean secure, int readTimeoutMs, boolean autoClean, File spoolFolder) {
+ private StandbyClientSync(Builder builder) {
this.state = STATUS_INITIALIZING;
this.lastSuccessfulRequest = -1;
this.syncStartTimestamp = -1;
this.syncEndTimestamp = -1;
this.failedRequests = 0;
- this.host = host;
- this.port = port;
- this.secure = secure;
- this.readTimeoutMs = readTimeoutMs;
- this.autoClean = autoClean;
- this.fileStore = store;
+ this.host = builder.host;
+ this.port = builder.port;
+ this.secure = builder.secure;
+ this.readTimeoutMs = builder.readTimeoutMs;
+ this.autoClean = builder.autoClean;
+ this.fileStore = builder.fileStore;
this.observer = new CommunicationObserver(clientId());
this.group = new NioEventLoopGroup(0, new NamedThreadFactory("standby"));
this.execution = new StandbyClientSyncExecution(fileStore, () -> running);
- this.spoolFolder = spoolFolder;
+ this.spoolFolder = builder.spoolFolder;
+ this.sslKeyFile = builder.sslKeyFile;
+ this.sslKeyPassword = builder.sslKeyPassword;
+ this.sslChainFile = builder.sslChainFile;
+ this.sslSubjectPattern = builder.sslSubjectPattern;
try {
ManagementFactory.getPlatformMBeanServer().registerMBean(new StandardMBean(this, ClientStandbyStatusMBean.class), new ObjectName(this.getMBeanName()));
} catch (Exception e) {
@@ -161,7 +253,18 @@ public final class StandbyClientSync implements ClientStandbyStatusMBean, Runnab
GCGeneration genBefore = headGeneration(fileStore);
- try (StandbyClient client = new StandbyClient(host, port, group, observer.getID(), secure, readTimeoutMs, spoolFolder)) {
+ try (StandbyClient client = StandbyClient.builder()
+ .withHost(host)
+ .withPort(port)
+ .withGroup(group)
+ .withClientId(observer.getID())
+ .withSecure(secure)
+ .withReadTimeoutMs(readTimeoutMs)
+ .withSpoolFolder(spoolFolder)
+ .withSSLKeyFile(sslKeyFile)
+ .withSSLKeyPassword(sslKeyPassword)
+ .withSSLChainFile(sslChainFile)
+ .withSSLSubjectPattern(sslSubjectPattern).build()) {
execution.execute(client);
}
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/netty/SSLSubjectMatcher.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/netty/SSLSubjectMatcher.java
new file mode 100644
index 0000000..cf67973
--- /dev/null
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/netty/SSLSubjectMatcher.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.jackrabbit.oak.segment.standby.netty;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
+
+import javax.net.ssl.SSLSession;
+import javax.security.cert.X509Certificate;
+import java.security.Principal;
+import java.util.regex.Pattern;
+
+public class SSLSubjectMatcher extends ChannelInboundHandlerAdapter {
+
+ private final Pattern pattern;
+
+ public SSLSubjectMatcher(String pattern) {
+ this.pattern = Pattern.compile(pattern);
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object ev) throws Exception {
+ if (ev instanceof SslHandshakeCompletionEvent) {
+ final Channel channel = channelHandlerContext.channel();
+ final ChannelPipeline pipeline = channel.pipeline();
+ final SslHandler ssl = (SslHandler) pipeline.get("ssl");
+ final SSLSession session = ssl.engine().getSession();
+ final X509Certificate x509Certificate = session.getPeerCertificateChain()[0];
+ final Principal subjectDN = x509Certificate.getSubjectDN();
+ final String subject = subjectDN.getName();
+ if (!pattern.matcher(subject).matches()) {
+ throw new Exception(
+ "Pattern match /" + pattern.toString() + "/ failed on: " + subject + "\n" +
+ "Note: the Java implementation of regex always implicitly matches from beginning to\n" +
+ "end, as in ^YOUR_PATTERN$, so if you want to match /acme/ anywhere in the subject\n" +
+ "you'd use .*acme.*");
+ }
+ }
+ }
+}
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java
index 6935ca7..29adcba 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java
@@ -21,6 +21,7 @@ package org.apache.jackrabbit.oak.segment.standby.server;
import static com.google.common.base.Preconditions.checkState;
+import java.io.File;
import java.security.cert.CertificateException;
import java.util.concurrent.TimeUnit;
@@ -40,6 +41,7 @@ import io.netty.handler.codec.compression.SnappyFrameEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.CharsetUtil;
@@ -50,6 +52,7 @@ import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponseEncoder;
import org.apache.jackrabbit.oak.segment.standby.codec.GetReferencesResponseEncoder;
import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponseEncoder;
import org.apache.jackrabbit.oak.segment.standby.codec.RequestDecoder;
+import org.apache.jackrabbit.oak.segment.standby.netty.SSLSubjectMatcher;
import org.apache.jackrabbit.oak.segment.standby.store.CommunicationObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -104,6 +107,16 @@ class StandbyServer implements AutoCloseable {
private StandbyBlobReader standbyBlobReader;
+ private String sslKeyFile;
+
+ private String sslKeyPassword;
+
+ private String sslChainFile;
+
+ private boolean sslClientValidation;
+
+ private String sslSubjectPattern;
+
private Builder(final int port, final StoreProvider storeProvider, final int blobChunkSize) {
this.port = port;
this.storeProvider = storeProvider;
@@ -150,6 +163,31 @@ class StandbyServer implements AutoCloseable {
return this;
}
+ Builder withSSLKeyFile(String sslKeyFile) {
+ this.sslKeyFile = sslKeyFile;
+ return this;
+ }
+
+ Builder withSSLKeyPassword(String sslKeyPassword) {
+ this.sslKeyPassword = sslKeyPassword;
+ return this;
+ }
+
+ Builder withSSLChainFile(String sslChainFile) {
+ this.sslChainFile = sslChainFile;
+ return this;
+ }
+
+ Builder withSSLClientValidation(boolean sslValidateClient) {
+ this.sslClientValidation = sslValidateClient;
+ return this;
+ }
+
+ Builder withSSLSubjectPattern(String sslSubjectPattern) {
+ this.sslSubjectPattern = sslSubjectPattern;
+ return this;
+ }
+
StandbyServer build() throws CertificateException, SSLException {
checkState(storeProvider != null);
@@ -173,15 +211,18 @@ class StandbyServer implements AutoCloseable {
return new StandbyServer(this);
}
-
}
private StandbyServer(final Builder builder) throws CertificateException, SSLException {
this.port = builder.port;
if (builder.secure) {
- SelfSignedCertificate ssc = new SelfSignedCertificate();
- sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
+ if (builder.sslKeyFile != null && !"".equals(builder.sslKeyFile)) {
+ sslContext = SslContextBuilder.forServer(new File(builder.sslChainFile), new File(builder.sslKeyFile), builder.sslKeyPassword).build();
+ } else {
+ SelfSignedCertificate ssc = new SelfSignedCertificate();
+ sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
+ }
}
bossGroup = new NioEventLoopGroup(1, new NamedThreadFactory("primary-run"));
@@ -205,7 +246,13 @@ class StandbyServer implements AutoCloseable {
p.addLast(new ClientFilterHandler(new ClientIpFilter(builder.allowedClientIPRanges)));
if (sslContext != null) {
- p.addLast("ssl", sslContext.newHandler(ch.alloc()));
+ SslHandler handler = sslContext.newHandler(ch.alloc());
+ handler.engine().setNeedClientAuth(builder.sslClientValidation);
+ p.addLast("ssl", handler);
+
+ if (builder.sslSubjectPattern != null) {
+ p.addLast(new SSLSubjectMatcher(builder.sslSubjectPattern));
+ }
}
// Decoders
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java
index 7d5dd46..5917d5e 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServerSync.java
@@ -63,6 +63,16 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto
private StandbySegmentReader standbySegmentReader;
+ private String sslKeyFile;
+
+ private String sslChainFile;
+
+ private boolean sslValidateClient;
+
+ private String sslKeyPassword;
+
+ private String sslSubjectPattern;
+
private Builder() {
// Prevent external instantiation
}
@@ -119,13 +129,37 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto
return this;
}
+ public Builder withSSLKeyFile(String sslKeyFile) {
+ this.sslKeyFile = sslKeyFile;
+ return this;
+ }
+
+ public Builder withSSLKeyPassword(String sslKeyPassword) {
+ this.sslKeyPassword = sslKeyPassword;
+ return this;
+ }
+
+ public Builder withSSLChainFile(String sslChainFile) {
+ this.sslChainFile = sslChainFile;
+ return this;
+ }
+
+ public Builder withSSLClientValidation(boolean sslValidateClient) {
+ this.sslValidateClient = sslValidateClient;
+ return this;
+ }
+
+ public Builder withSSLSubjectPattern(String sslSubjectPattern) {
+ this.sslSubjectPattern = sslSubjectPattern;
+ return this;
+ }
+
public StandbyServerSync build() {
checkArgument(port > 0);
checkArgument(fileStore != null);
checkArgument(blobChunkSize > 0);
return new StandbyServerSync(this);
}
-
}
private static final Logger log = LoggerFactory.getLogger(StandbyServer.class);
@@ -156,6 +190,16 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto
private StandbyServer server;
+ private final String sslKeyFile;
+
+ private final String sslKeyPassword;
+
+ private final String sslChainFile;
+
+ private final boolean sslValidateClient;
+
+ private final String sslSubjectPattern;
+
private StandbyServerSync(Builder builder) {
this.port = builder.port;
this.fileStore = builder.fileStore;
@@ -166,6 +210,11 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto
this.standbyHeadReader = builder.standbyHeadReader;
this.standbyReferencesReader = builder.standbyReferencesReader;
this.standbySegmentReader = builder.standbySegmentReader;
+ this.sslKeyFile = builder.sslKeyFile;
+ this.sslKeyPassword = builder.sslKeyPassword;
+ this.sslChainFile = builder.sslChainFile;
+ this.sslValidateClient = builder.sslValidateClient;
+ this.sslSubjectPattern = builder.sslSubjectPattern;
this.observer = new CommunicationObserver("primary");
final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer();
@@ -196,7 +245,7 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto
state = STATUS_STARTING;
try {
- server = StandbyServer.builder(port, this, blobChunkSize)
+ StandbyServer.Builder builder = StandbyServer.builder(port, this, blobChunkSize)
.secure(secure)
.allowIPRanges(allowedClientIPRanges)
.withStateConsumer(this)
@@ -205,7 +254,13 @@ public class StandbyServerSync implements StandbyStatusMBean, StateConsumer, Sto
.withStandbyHeadReader(standbyHeadReader)
.withStandbyReferencesReader(standbyReferencesReader)
.withStandbySegmentReader(standbySegmentReader)
- .build();
+ .withSSLKeyFile(sslKeyFile)
+ .withSSLKeyPassword(sslKeyPassword)
+ .withSSLChainFile(sslChainFile)
+ .withSSLClientValidation(sslValidateClient)
+ .withSSLSubjectPattern(sslSubjectPattern);
+
+ server = builder.build();
server.start();
state = STATUS_RUNNING;
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
index 89fcda6..8efbb9e 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
@@ -39,6 +39,7 @@ import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.Option;
@@ -117,6 +118,35 @@ public class StandbyStoreService {
)
boolean standby_autoclean() default true;
+ @AttributeDefinition(
+ name = "SSL Key File",
+ description = "The file name which contains the SSL key. If this is empty, a key will be generated on-the-fly."
+ )
+ String sslKeyFile();
+
+ @AttributeDefinition(
+ name = "SSL Key Password",
+ description = "Password for the SSL key. If this is empty, an unencrypted key is expected.",
+ type = AttributeType.PASSWORD)
+ String sslKeyPassword() default "";
+
+ @AttributeDefinition(
+ name = "SSL Certificate Chain File",
+ description = "The file name which contains the SSL certificate chain."
+ )
+ String sslChainFile();
+
+ @AttributeDefinition(
+ name = "SSL Validate Client",
+ description = "Validate the client's SSL certificate."
+ )
+ boolean sslValidateClient() default false;
+
+ @AttributeDefinition(
+ name = "SSL Certificate Subject Pattern",
+ description = "The peer certificate's subject must match this pattern in order to be accepted."
+ )
+ String sslSubjectPattern();
}
@Reference(policy = STATIC, policyOption = GREEDY)
@@ -137,12 +167,12 @@ public class StandbyStoreService {
String mode = config.mode();
if (mode.equals("primary")) {
- bootstrapMaster(config, fileStore);
+ bootstrapPrimary(config, fileStore);
return;
}
if (mode.equals("standby")) {
- bootstrapSlave(context, config, fileStore);
+ bootstrapSecondary(context, config, fileStore);
return;
}
@@ -154,18 +184,31 @@ public class StandbyStoreService {
closer.close();
}
- private void bootstrapMaster(Configuration config, FileStore fileStore) {
+ private void bootstrapPrimary(Configuration config, FileStore fileStore) {
int port = config.port();
String[] ranges = config.primary_allowed$_$client$_$ip$_$ranges();
boolean secure = config.secure();
+ String sslKeyFile = config.sslKeyFile();
+ String sslChainFile = config.sslChainFile();
+ boolean sslValidateClient = config.sslValidateClient();
+ String sslSubjectPattern = config.sslSubjectPattern();
- StandbyServerSync standbyServerSync = StandbyServerSync.builder()
+ StandbyServerSync.Builder builder = StandbyServerSync.builder()
.withPort(port)
.withFileStore(fileStore)
.withBlobChunkSize(BLOB_CHUNK_SIZE)
.withAllowedClientIPRanges(ranges)
- .withSecureConnection(secure)
- .build();
+ .withSecureConnection(true)
+ .withSSLKeyFile(sslKeyFile)
+ .withSSLChainFile(sslChainFile)
+ .withSSLClientValidation(sslValidateClient)
+ .withSSLSubjectPattern(sslSubjectPattern);
+
+ if (!"".equals(config.sslKeyPassword())) {
+ builder.withSSLKeyPassword(config.sslKeyPassword());
+ }
+
+ StandbyServerSync standbyServerSync = builder.build();
closer.register(standbyServerSync);
standbyServerSync.start();
@@ -173,24 +216,35 @@ public class StandbyStoreService {
log.info("Started primary on port {} with allowed IP ranges {}", port, ranges);
}
- private void bootstrapSlave(ComponentContext context, Configuration config, FileStore fileStore) {
- int port = config.port();
- long interval = config.interval();
- String host = config.primary_host();
- boolean secure = config.secure();
- int readTimeout = config.standby_readtimeout();
- boolean clean = config.standby_autoclean();
+ private void bootstrapSecondary(ComponentContext context, Configuration config, FileStore fileStore) {
+
+ StandbyClientSync.Builder builder = StandbyClientSync.builder()
+ .withHost(config.primary_host())
+ .withPort(config.port())
+ .withFileStore(fileStore)
+ .withSecureConnection(config.secure())
+ .withReadTimeoutMs(config.standby_readtimeout())
+ .withAutoClean(config.standby_autoclean())
+ .withSpoolFolder(new File(StandardSystemProperty.JAVA_IO_TMPDIR.value()))
+ .withSecureConnection(config.secure())
+ .withSSLKeyFile(config.sslKeyFile())
+ .withSSLChainFile(config.sslChainFile())
+ .withSSLSubjectPattern(config.sslSubjectPattern());
+
+ if (!"".equals(config.sslKeyPassword())) {
+ builder.withSSLKeyPassword(config.sslKeyPassword());
+ }
- StandbyClientSync standbyClientSync = new StandbyClientSync(host, port, fileStore, secure, readTimeout, clean, new File(StandardSystemProperty.JAVA_IO_TMPDIR.value()));
+ StandbyClientSync standbyClientSync = builder.build();
closer.register(standbyClientSync);
Dictionary<Object, Object> dictionary = new Hashtable<Object, Object>();
- dictionary.put("scheduler.period", interval);
+ dictionary.put("scheduler.period", config.interval());
dictionary.put("scheduler.concurrent", false);
ServiceRegistration registration = context.getBundleContext().registerService(Runnable.class.getName(), standbyClientSync, dictionary);
closer.register(registration::unregister);
- log.info("Started standby on port {} with {}s sync frequency", port, interval);
+ log.info("Started standby on port {} with {}s sync frequency", config.port(), config.interval());
}
}
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java
index cb7dce2..1d90cb5 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkIT.java
@@ -73,7 +73,15 @@ public class BrokenNetworkIT extends TestBase {
.withBlobChunkSize(MB)
.withSecureConnection(false)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientStore, false, getClientTimeout(), false, folder.newFolder());
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(clientStore)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
clientSync.run();
@@ -98,7 +106,15 @@ public class BrokenNetworkIT extends TestBase {
.withBlobChunkSize(MB)
.withSecureConnection(true)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, true, getClientTimeout(), false, folder.newFolder());
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
clientSync.run();
@@ -179,7 +195,15 @@ public class BrokenNetworkIT extends TestBase {
try (
NetworkErrorProxy ignored = new NetworkErrorProxy(proxyPort.getPort(), getServerHost(), serverPort.getPort(), flipPosition, skipPosition, skipBytes);
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), proxyPort.getPort(), clientStore, ssl, getClientTimeout(), false, spoolFolder)
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(proxyPort.getPort())
+ .withFileStore(clientStore)
+ .withSecureConnection(ssl)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(spoolFolder)
+ .build()
) {
clientSync.run();
}
@@ -191,10 +215,19 @@ public class BrokenNetworkIT extends TestBase {
serverStore.flush();
}
- try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientStore, ssl, getClientTimeout(), false, spoolFolder)) {
- clientSync.run();
+ try (StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(clientStore)
+ .withSecureConnection(ssl)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(spoolFolder)
+ .build()
+ ) {
+ clientSync.run();
}
- }
+ }
assertEquals("stores are not equal", serverStore.getHead(), clientStore.getHead());
}
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java
index 28a845c..7b4b6e9 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java
@@ -155,7 +155,15 @@ public abstract class DataStoreTestBase extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, 4_000, false, spoolFolder)
+ StandbyClientSync cl = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(4_000)
+ .withAutoClean(false)
+ .withSpoolFolder(spoolFolder)
+ .build()
) {
serverSync.start();
// no persisted head on primary
@@ -172,7 +180,15 @@ public abstract class DataStoreTestBase extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, 4_000, false, spoolFolder)
+ StandbyClientSync cl = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(4_000)
+ .withAutoClean(false)
+ .withSpoolFolder(spoolFolder)
+ .build()
) {
serverSync.start();
// this time persisted head will be available on primary
@@ -213,7 +229,15 @@ public abstract class DataStoreTestBase extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync cl = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
primary.flush();
@@ -259,7 +283,15 @@ public abstract class DataStoreTestBase extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(8 * MB)
.build();
- StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, 2 * 60 * 1000, false, folder.newFolder())
+ StandbyClientSync cl = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(2 * 60 * 1000)
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
primary.flush();
@@ -302,7 +334,15 @@ public abstract class DataStoreTestBase extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
@@ -376,7 +416,15 @@ public abstract class DataStoreTestBase extends TestBase {
try (
NetworkErrorProxy ignored = new NetworkErrorProxy(proxyPort.getPort(), getServerHost(), serverPort.getPort(), flipPosition, skipPosition, skipBytes);
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), proxyPort.getPort(), secondary, false, getClientTimeout(), false, spoolFolder)
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(proxyPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(spoolFolder)
+ .build()
) {
clientSync.run();
}
@@ -391,7 +439,16 @@ public abstract class DataStoreTestBase extends TestBase {
primary.flush();
}
- try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, spoolFolder)) {
+ try (StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(spoolFolder)
+ .build()
+ ) {
clientSync.run();
}
}
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java
index 3deb539..c50983d 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeIT.java
@@ -163,7 +163,15 @@ public class FailoverIPRangeIT extends TestBase {
.withBlobChunkSize(MB)
.withAllowedClientIPRanges(ipRanges)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(host, serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(host)
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
addTestContent(store, "server");
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java
index 0e52b53..fc1d373 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java
@@ -68,8 +68,24 @@ public class FailoverMultipleClientsTestIT extends TestBase {
.withFileStore(storeS)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync cl1 = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder());
- StandbyClientSync cl2 = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC2, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync cl1 = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build();
+ StandbyClientSync cl2 = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC2)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
SegmentTestUtils.addTestContent(store, "server");
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java
index f45f4a0..a13d679 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java
@@ -64,7 +64,15 @@ public class FailoverSslTestIT extends TestBase {
.withBlobChunkSize(MB)
.withSecureConnection(true)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, true, getClientTimeout(), false, folder.newFolder());
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
assertTrue(synchronizeAndCompareHead(serverSync, clientSync));
}
@@ -81,7 +89,15 @@ public class FailoverSslTestIT extends TestBase {
.withBlobChunkSize(MB)
.withSecureConnection(true)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder());
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
assertFalse(synchronizeAndCompareHead(serverSync, clientSync));
}
@@ -97,7 +113,15 @@ public class FailoverSslTestIT extends TestBase {
.withFileStore(storeS)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, true, getClientTimeout(), false, folder.newFolder());
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
assertFalse(synchronizeAndCompareHead(serverSync, clientSync));
}
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java
index 7a138f1..0357ce1 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanIT.java
@@ -99,7 +99,16 @@ public class MBeanIT extends TestBase {
public void testClientEmptyConfigNoServer() throws Exception {
MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer();
ObjectName status = new ObjectName(StandbyStatusMBean.JMX_NAME + ",id=*");
- try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientFileStore.fileStore(), false, getClientTimeout(), false, folder.newFolder())) {
+ try (StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(clientFileStore.fileStore())
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build();
+ ) {
clientSync.start();
clientSync.run();
@@ -136,7 +145,16 @@ public class MBeanIT extends TestBase {
System.setProperty(StandbyClientSync.CLIENT_ID_PROPERTY_NAME, "Foo");
MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer();
ObjectName status;
- try (StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientFileStore.fileStore(), false, getClientTimeout(), false, folder.newFolder())) {
+ try (StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(clientFileStore.fileStore())
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build();
+ ) {
clientSync.start();
clientSync.run();
@@ -164,7 +182,15 @@ public class MBeanIT extends TestBase {
.withFileStore(serverFileStore.fileStore())
.withBlobChunkSize(MB)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), clientFileStore.fileStore(), false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(clientFileStore.fileStore())
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
serverFileStore.fileStore().flush();
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java
index f760bb7..d2ced58 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java
@@ -67,7 +67,15 @@ public class RecoverTestIT extends TestBase {
.withFileStore(storeS)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync cl = new StandbyClientSync(getServerHost(), serverPort.getPort(), storeC, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync cl = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(storeC)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
store = SegmentNodeStoreBuilders.builder(storeS).build();
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java
index 567a697..2fad0ea 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbySegmentBlobTestIT.java
@@ -99,7 +99,15 @@ public class StandbySegmentBlobTestIT extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
addTestContent(store, "server");
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java
index 892670b..b437b98 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java
@@ -21,17 +21,23 @@ package org.apache.jackrabbit.oak.segment.standby;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
import java.util.Random;
import com.google.common.io.ByteStreams;
import org.apache.jackrabbit.oak.api.Blob;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.IOUtils;
import org.apache.jackrabbit.oak.commons.junit.TemporaryPort;
import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders;
import org.apache.jackrabbit.oak.segment.file.FileStore;
@@ -42,6 +48,7 @@ import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -63,6 +70,9 @@ public class StandbyTestIT extends TestBase {
.around(serverFileStore)
.around(clientFileStore);
+ /**
+ * This test syncs a few segments over an unencrypted connection.
+ */
@Test
public void testSync() throws Exception {
int blobSize = 5 * MB;
@@ -76,7 +86,68 @@ public class StandbyTestIT extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
+ ) {
+ serverSync.start();
+ byte[] data = addTestContent(store, "server", blobSize, 150);
+ primary.flush();
+
+ clientSync.run();
+
+ assertEquals(primary.getHead(), secondary.getHead());
+
+ assertTrue(primary.getStats().getApproximateSize() > blobSize);
+ assertTrue(secondary.getStats().getApproximateSize() > blobSize);
+
+ PropertyState ps = secondary.getHead().getChildNode("root")
+ .getChildNode("server").getProperty("testBlob");
+ assertNotNull(ps);
+ assertEquals(Type.BINARY.tag(), ps.getType().tag());
+ Blob b = ps.getValue(Type.BINARY);
+ assertEquals(blobSize, b.length());
+
+ byte[] testData = new byte[blobSize];
+ ByteStreams.readFully(b.getNewStream(), testData);
+ assertArrayEquals(data, testData);
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * Both server and client certificates are generated on-the-fly.
+ */
+ @Test
+ @Ignore("This test takes ~4s and is therefore disabled by default")
+ public void testSyncSSL() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
byte[] data = addTestContent(store, "server", blobSize, 150);
@@ -103,6 +174,735 @@ public class StandbyTestIT extends TestBase {
}
/**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore.
+ * The server does not validate the client certificate.
+ * The client creates its certificate on-the-fly.
+ */
+ @Test
+ @Ignore("This test takes ~2s and is therefore disabled by default")
+ public void testSyncSSLNoClientValidation() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(false)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
+ ) {
+ serverSync.start();
+ byte[] data = addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertEquals(primary.getHead(), secondary.getHead());
+
+ assertTrue(primary.getStats().getApproximateSize() > blobSize);
+ assertTrue(secondary.getStats().getApproximateSize() > blobSize);
+
+ PropertyState ps = secondary.getHead().getChildNode("root")
+ .getChildNode("server").getProperty("testBlob");
+ assertNotNull(ps);
+ assertEquals(Type.BINARY.tag(), ps.getType().tag());
+ Blob b = ps.getValue(Type.BINARY);
+ assertEquals(blobSize, b.length());
+
+ byte[] testData = new byte[blobSize];
+ ByteStreams.readFully(b.getNewStream(), testData);
+ assertArrayEquals(data, testData);
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore.
+ */
+ @Test
+ @Ignore("This test takes ~2s and is therefore disabled by default")
+ public void testSyncSSLValidClient() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, clientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .build()
+ ) {
+ serverSync.start();
+ byte[] data = addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertEquals(primary.getHead(), secondary.getHead());
+
+ assertTrue(primary.getStats().getApproximateSize() > blobSize);
+ assertTrue(secondary.getStats().getApproximateSize() > blobSize);
+
+ PropertyState ps = secondary.getHead().getChildNode("root")
+ .getChildNode("server").getProperty("testBlob");
+ assertNotNull(ps);
+ assertEquals(Type.BINARY.tag(), ps.getType().tag());
+ Blob b = ps.getValue(Type.BINARY);
+ assertEquals(blobSize, b.length());
+
+ byte[] testData = new byte[blobSize];
+ ByteStreams.readFully(b.getNewStream(), testData);
+ assertArrayEquals(data, testData);
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore.
+ * All the keys are encrypted.
+ */
+ @Test
+ @Ignore("This test takes ~2s and is therefore disabled by default")
+ public void testSyncSSLValidClientEncryptedKeys() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, encryptedServerKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, encryptedClientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLKeyPassword(secretPassword)
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLKeyPassword(secretPassword)
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .build()
+ ) {
+ serverSync.start();
+ byte[] data = addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertEquals(primary.getHead(), secondary.getHead());
+
+ assertTrue(primary.getStats().getApproximateSize() > blobSize);
+ assertTrue(secondary.getStats().getApproximateSize() > blobSize);
+
+ PropertyState ps = secondary.getHead().getChildNode("root")
+ .getChildNode("server").getProperty("testBlob");
+ assertNotNull(ps);
+ assertEquals(Type.BINARY.tag(), ps.getType().tag());
+ Blob b = ps.getValue(Type.BINARY);
+ assertEquals(blobSize, b.length());
+
+ byte[] testData = new byte[blobSize];
+ ByteStreams.readFully(b.getNewStream(), testData);
+ assertArrayEquals(data, testData);
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore.
+ * The server validates the client certificate.
+ * The client has a configured certificate which cannot be validated with the truststore.
+ * The SSL connection is expected to fail.
+ */
+ @Test
+ @Ignore("This test takes ~7s and is therefore disabled by default")
+ public void testSyncSSLInvalidClient() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, arbitraryKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, arbitraryCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .build()
+ ) {
+ serverSync.start();
+ addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertNotEquals(primary.getHead(), secondary.getHead());
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which cannot be validated with the truststore.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore.
+ * The SSL connection is expected to fail.
+ */
+ @Test
+ @Ignore("This test takes ~7s and is therefore disabled by default")
+ public void testSyncSSLInvalidServer() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, arbitraryKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, clientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, arbitraryCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .build()
+ ) {
+ serverSync.start();
+ addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertNotEquals(primary.getHead(), secondary.getHead());
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore,
+ * but cannot be validated against the required subject pattern.
+ * The SSL connection is expected to fail.
+ */
+ @Test
+ @Ignore("This test takes ~7s and is therefore disabled by default")
+ public void testSyncSSLInvalidClientSubject() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, clientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .withSSLSubjectPattern("foobar")
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .build()
+ ) {
+ serverSync.start();
+ addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertNotEquals(primary.getHead(), secondary.getHead());
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore,
+ * and can also be validated against the required subject pattern.
+ */
+ @Test
+ @Ignore("This test takes ~7s and is therefore disabled by default")
+ public void testSyncSSLValidClientSubject() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, clientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .withSSLSubjectPattern(".*.esting.*")
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .build()
+ ) {
+ serverSync.start();
+ addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertEquals(primary.getHead(), secondary.getHead());
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore,
+ * but cannot be validated against the required subject pattern.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore.
+ * The SSL connection is expected to fail.
+ */
+ @Test
+ @Ignore("This test takes ~7s and is therefore disabled by default")
+ public void testSyncSSLInvalidServerSubject() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, clientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .withSSLSubjectPattern("foobar")
+ .build()
+ ) {
+ serverSync.start();
+ addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertNotEquals(primary.getHead(), secondary.getHead());
+ }
+ }
+
+ /**
+ * This test syncs a few segments over an encrypted connection.
+ * The server has a configured certificate which can be validated with the truststore,
+ * and can also be validated against the required subject pattern.
+ * The server validates the client certificate.
+ * The client has a configured certificate which can be validated with the truststore.
+ */
+ @Test
+ @Ignore("This test takes ~7s and is therefore disabled by default")
+ public void testSyncSSLValidServerSubject() throws Exception {
+ int blobSize = 5 * MB;
+ FileStore primary = serverFileStore.fileStore();
+ FileStore secondary = clientFileStore.fileStore();
+
+ FileOutputStream fos;
+
+ File serverKeyFile = folder.newFile();
+ fos = new FileOutputStream(serverKeyFile);
+ IOUtils.writeString(fos, serverKey);
+ fos.close();
+
+ File clientKeyFile = folder.newFile();
+ fos = new FileOutputStream(clientKeyFile);
+ IOUtils.writeString(fos, clientKey);
+ fos.close();
+
+ File serverCertFile = folder.newFile();
+ fos = new FileOutputStream(serverCertFile);
+ IOUtils.writeString(fos, serverCert);
+ fos.close();
+
+ File clientCertFile = folder.newFile();
+ fos = new FileOutputStream(clientCertFile);
+ IOUtils.writeString(fos, clientCert);
+ fos.close();
+
+ File keyStoreFile = folder.newFile();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, "changeit".toCharArray());
+ Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(caCert.getBytes()));
+ keyStore.setCertificateEntry("the-ca-cert", c);
+ keyStore.store(new FileOutputStream(keyStoreFile), "changeit".toCharArray());
+ System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath());
+
+ NodeStore store = SegmentNodeStoreBuilders.builder(primary).build();
+ try (
+ StandbyServerSync serverSync = StandbyServerSync.builder()
+ .withPort(serverPort.getPort())
+ .withFileStore(primary)
+ .withBlobChunkSize(MB)
+ .withSecureConnection(true)
+ .withSSLKeyFile(serverKeyFile.getAbsolutePath())
+ .withSSLChainFile(serverCertFile.getAbsolutePath())
+ .withSSLClientValidation(true)
+ .build();
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(true)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .withSSLKeyFile(clientKeyFile.getAbsolutePath())
+ .withSSLChainFile(clientCertFile.getAbsolutePath())
+ .withSSLSubjectPattern(".*.esting.*")
+ .build()
+ ) {
+ serverSync.start();
+ addTestContent(store, "server", blobSize, 1);
+ primary.flush();
+
+ clientSync.run();
+
+ assertEquals(primary.getHead(), secondary.getHead());
+ }
+ }
+
+ /**
* OAK-2430
*/
@Test
@@ -120,7 +920,15 @@ public class StandbyTestIT extends TestBase {
.withFileStore(primary)
.withBlobChunkSize(MB)
.build();
- StandbyClientSync clientSync = new StandbyClientSync(getServerHost(), serverPort.getPort(), secondary, false, getClientTimeout(), false, folder.newFolder())
+ StandbyClientSync clientSync = StandbyClientSync.builder()
+ .withHost(getServerHost())
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(getClientTimeout())
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
serverSync.start();
byte[] data = addTestContent(store, "server", blobSize, dataNodes);
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java
index 880423f..e89e374 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java
@@ -48,4 +48,292 @@ public class TestBase {
return timeout;
}
+ // A self-signed certificate representing the "Testing CA"
+ static final String caCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDxjCCAq4CCQDWKVsDO4p3MDANBgkqhkiG9w0BAQUFADCBpDELMAkGA1UEBhMC\n" +
+ "VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x\n" +
+ "FjAUBgNVBAoMDUNBVGVzdGluZyBDby4xEDAOBgNVBAsMB1Rlc3RpbmcxFjAUBgNV\n" +
+ "BAMMDWNhdGVzdGluZy5jb20xJjAkBgkqhkiG9w0BCQEWF2NhdGVzdGluZ0BjYXRl\n" +
+ "c3RpbmcuY29tMB4XDTIxMDUzMTEzNTczOFoXDTQ4MTAxNTEzNTczOFowgaQxCzAJ\n" +
+ "BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJh\n" +
+ "bmNpc2NvMRYwFAYDVQQKDA1DQVRlc3RpbmcgQ28uMRAwDgYDVQQLDAdUZXN0aW5n\n" +
+ "MRYwFAYDVQQDDA1jYXRlc3RpbmcuY29tMSYwJAYJKoZIhvcNAQkBFhdjYXRlc3Rp\n" +
+ "bmdAY2F0ZXN0aW5nLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" +
+ "AKmolPe+fw1pQ91MNrlHBdRCX5J55vuV7zqbKU8UdZVuI3iajvhFtMm6xxIyISIM\n" +
+ "cO+h0JYKH0NHgCV0wqUKh+T16FEPo8BskxMCXIn0R0IQBBeUIIS3+FtNzRhLK3ff\n" +
+ "R7jtpv4YMp50tx8FVj9zIYFnLq5RObH7hENrxLTk04G3Gr8ENqapH21pfVlkb4GP\n" +
+ "RXBonxyR/q997ZGqutxiEbsCeWDMl/uvPDL3zKbd2e/Hx0Xy5kBOqAF35JTmKxax\n" +
+ "up33ld3u3r2MlFzcD7l/2uG7ur42MNDFBG2yQXsmPFUE6/Ogk/IfuGiqVYYjjRZV\n" +
+ "ixO+HK3WnICJggsNDQO6CqsCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAOjhaAvUw\n" +
+ "AeQ6PO485xoNpnadrFu+Q1RafqZCHd/UJ/c2VLeEogsBjZpLMMclF01T6k7X/6N5\n" +
+ "44oy9MS90/IVeRsfL0fT0pPGao2by4eja5di2j8UiiyuWSUI4J5PqBn48pNNYt4P\n" +
+ "W0MKS74ea/lDWmsGWw9B/mGZ/0znIo2WUCpifIGBqUfqfmBAFjMU/4aK1bMfzhZc\n" +
+ "OJNcpa2iOgrqd9Irjrs0kM5j0omN42be65FXUEoTVH5zOJr9sHaThTVM8PZ+A5Vm\n" +
+ "uSxSBG00WgrVuuCixz1DDrXI+LR/ipVU/fTGWRywBQRunrQsY7V9ODtmSipczi+o\n" +
+ "udwFdKx9QrsPEA==\n" +
+ "-----END CERTIFICATE-----\n";
+
+ // A client certificate signed by the "Testing CA"
+ static final String clientCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDxDCCAqwCAQIwDQYJKoZIhvcNAQEFBQAwgaQxCzAJBgNVBAYTAlVTMRMwEQYD\n" +
+ "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRYwFAYDVQQK\n" +
+ "DA1DQVRlc3RpbmcgQ28uMRAwDgYDVQQLDAdUZXN0aW5nMRYwFAYDVQQDDA1jYXRl\n" +
+ "c3RpbmcuY29tMSYwJAYJKoZIhvcNAQkBFhdjYXRlc3RpbmdAY2F0ZXN0aW5nLmNv\n" +
+ "bTAeFw0yMTA1MzExMzU3MzhaFw00ODEwMTUxMzU3MzhaMIGqMQswCQYDVQQGEwJV\n" +
+ "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEU\n" +
+ "MBIGA1UECgwLRXhhbXBsZSBDby4xFzAVBgNVBAsMDlRlc3RpbmcgQ2xpZW50MRQw\n" +
+ "EgYDVQQDDAtleGFtcGxlLmNvbTEpMCcGCSqGSIb3DQEJARYadGVzdGluZy1jbGll\n" +
+ "bnRAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp\n" +
+ "izibTCEp21PffO6UN8wX7PW+dd7HTepbwqd2mT3iYqC2/IuO8mzS5Sk/mNqL2T9T\n" +
+ "GPcx4cLRGGovBw/Ig3A3nv4uBxzFVHJqtHZJl+VPgZiUDmyBaNsv0g5fqluiktcU\n" +
+ "MI0HQWABOEcwCyBYHXadxp8DUAUdjniopax7cMTujrWRFJAH7s4++yu5kK06mMwD\n" +
+ "Rsh65CGKvP5tz+cszoabo5I8dT+8tTPHrgdaCqjI9536VtbgmU3LKNG1DNkmR6nq\n" +
+ "ICo1Lg0+GFnkFnEQpbV8WsckwFempDND3MtZKw6U5VxGs+EdTj0s0UEbCNVwLkW1\n" +
+ "uBFyfcnODpWe2VY0wPafAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHf1P4+fzuln\n" +
+ "KHIBpkZTE++0tW0KUwoXfRIWATOp3XFqYJTHQm4KtTQ+mVo5FejavC57KrgeTp73\n" +
+ "mljxKJhzT6Oh89shLxYPF/mvgfhWgDpwPLXmFkepigZ88MLeMV3UG9MJVcB5UrTv\n" +
+ "RTxire5Ga1iaWRtHHeY4OXKp1foBxPyc9l+XHfxHeAjM4Oj4UoOKdV9sr+UYKXP7\n" +
+ "N0QBXdTHa70mhzOQP6PE5VUu98HFAA6oYlbIWwetXCBDdJ1sTeh+Uw5NAzfsUw5S\n" +
+ "NGib2Ru0SeKvjrpIzOto5DO6ZQdlb3Yfbj2S4ea3HBUHe6dNYQNa+dnq4xvrA3Iv\n" +
+ "CT7KNBGK8AQ=\n" +
+ "-----END CERTIFICATE-----\n";
+
+ // A server certificate signed by the "Testing CA"
+ static final String serverCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDxDCCAqwCAQEwDQYJKoZIhvcNAQEFBQAwgaQxCzAJBgNVBAYTAlVTMRMwEQYD\n" +
+ "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRYwFAYDVQQK\n" +
+ "DA1DQVRlc3RpbmcgQ28uMRAwDgYDVQQLDAdUZXN0aW5nMRYwFAYDVQQDDA1jYXRl\n" +
+ "c3RpbmcuY29tMSYwJAYJKoZIhvcNAQkBFhdjYXRlc3RpbmdAY2F0ZXN0aW5nLmNv\n" +
+ "bTAeFw0yMTA1MzExMzU3MzhaFw00ODEwMTUxMzU3MzhaMIGqMQswCQYDVQQGEwJV\n" +
+ "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEU\n" +
+ "MBIGA1UECgwLRXhhbXBsZSBDby4xFzAVBgNVBAsMDlRlc3RpbmcgU2VydmVyMRQw\n" +
+ "EgYDVQQDDAtleGFtcGxlLmNvbTEpMCcGCSqGSIb3DQEJARYadGVzdGluZy1zZXJ2\n" +
+ "ZXJAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA\n" +
+ "7nYmXdUcCgkDnwDPV0MEjbNQvVNxL9Sd/zJZBy02jcn2mklBJtAb18Nvra7tw14L\n" +
+ "KmEILFC4GwKVEtF1vqE6gBCxaohE0ZZ/dJGgPVvuRn+r1nzIwT4LKmDoayfne29K\n" +
+ "w2oDvxX+0vU9BI9c2N8ZN3Ge9n/r8Rp25SP0cyRKa8Q862kPXgsFsYX/D/mZpK/p\n" +
+ "KfwhLGl/6tCKTsOteetEvHPnirHIYgb81zI5NGirrckO6PJ3b+PJzdjgW3/12jWg\n" +
+ "9JrqT9Wg3Uf3VESGiUoAr9xnMun026jpWhRJL0zzZnpP0hr8RYAJ2Mfh8JpZjlx6\n" +
+ "0ePzHPTckRydLPc8tvCvAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHq0euTu0433\n" +
+ "nPwt9CuyC10o4wyTBCW0tXupM/06Hqk0U9rOQsPw0zNuQaW/Ww1rqpyG8S3Mw27f\n" +
+ "oua6Usfnwog5eREUi1XGe2HcGeqSca+34+WPUyi6agS2NEqpXyZWiRgfLUEngo3d\n" +
+ "Ph1BFHsxOXqLk+LCouA1OxeS8WZHdMRt6lzP+3FEJhiiEBAF8YIoQD6kD1lVBo4J\n" +
+ "e5Vof1Zs6frxbi12nh/Iu7YUEgm0IZ6X5GSs5c+nj0hHdPPN86Pul3fc9tWJ8MJO\n" +
+ "KYmsEX4YiIebo+dzrFEZywSQXOG5xkMhOprdc28stiIGkJCRSf/1lPy70i5YHMjr\n" +
+ "VqwFqu16zZg=\n" +
+ "-----END CERTIFICATE-----\n";
+
+ // An arbitrary self-signed certificate
+ static final String arbitraryCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIID3jCCAsYCCQD5JkW9FPJFYzANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\n" +
+ "VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x\n" +
+ "FDASBgNVBAoMC0V4YW1wbGUgQ28uMRowGAYDVQQLDBFUZXN0aW5nIEFyYml0cmFy\n" +
+ "eTEUMBIGA1UEAwwLZXhhbXBsZS5jb20xLDAqBgkqhkiG9w0BCQEWHXRlc3Rpbmct\n" +
+ "YXJiaXRyYXJ5QGV4YW1wbGUuY29tMB4XDTIxMDYwMjA4MDkwNloXDTQ4MTAxNzA4\n" +
+ "MDkwNlowgbAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD\n" +
+ "VQQHDA1TYW4gRnJhbmNpc2NvMRQwEgYDVQQKDAtFeGFtcGxlIENvLjEaMBgGA1UE\n" +
+ "CwwRVGVzdGluZyBBcmJpdHJhcnkxFDASBgNVBAMMC2V4YW1wbGUuY29tMSwwKgYJ\n" +
+ "KoZIhvcNAQkBFh10ZXN0aW5nLWFyYml0cmFyeUBleGFtcGxlLmNvbTCCASIwDQYJ\n" +
+ "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrvljMgkh0nLoNH9fNolHJWZ74xRDYT\n" +
+ "fjpMjNvJZqKvveQMhIWgLFkPM1fMsXQEeXxfrLMEt5bh0Crbr7GfODaoltH+HM8d\n" +
+ "xoXDqaXcDFtRdVyf4nM7RdwEQGhZHfa3oiewQG6UOrWpOU5s4GQL0Pk0oKNZFRGJ\n" +
+ "nR8hyPxnyB5p+M5VBOO/ATuOsKS+uXYjjI5ndof8bTx9xDykebiMTLmfy745req6\n" +
+ "KnaLC7NKSHsBUEvJlaQrRzyK5zouEz0Sfk3QfvnCOoLOng11WZ3I88jTl69J2X6E\n" +
+ "TLwAiNrsI24zvuCa24jKeehBvDIGjfa68+YQq7Zkdlsere9EoEe7JSMCAwEAATAN\n" +
+ "BgkqhkiG9w0BAQUFAAOCAQEAEwDID0JaJaI2WQMT0oZ9+Zz8Mf7wZbngrf9uG9MW\n" +
+ "oljQNwtFtcUYcMd6q+wsGd24bR//YBGkAXke6x+DNyRtbbTbFLFQgko/7Do6jB7U\n" +
+ "p75oNJgRBKsNO6eGIaLBxIYWxZcZ77IhRoX82WPRRLaT2iAc8p2I3QMra5Y+E+Aj\n" +
+ "f9gKqRKzwDgWOApD27KosJJZd2zddHZm/Fj+T8kWPTHFCt0FnzgGAVxKp1bFmzxX\n" +
+ "qGGFKW4IThE87fTFcgbRkUZdnrKTo6tCDDjIi2Rb2jJv7ip6BvI2cHip+UvQmvhe\n" +
+ "qRQdDY0scDekIRZ0WQYg4h/kmfNp9Zw55Ce18aMTXEwGrQ==\n" +
+ "-----END CERTIFICATE-----\n";
+
+ static final String clientKey =
+ "-----BEGIN PRIVATE KEY-----\n" +
+ "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCpizibTCEp21Pf\n" +
+ "fO6UN8wX7PW+dd7HTepbwqd2mT3iYqC2/IuO8mzS5Sk/mNqL2T9TGPcx4cLRGGov\n" +
+ "Bw/Ig3A3nv4uBxzFVHJqtHZJl+VPgZiUDmyBaNsv0g5fqluiktcUMI0HQWABOEcw\n" +
+ "CyBYHXadxp8DUAUdjniopax7cMTujrWRFJAH7s4++yu5kK06mMwDRsh65CGKvP5t\n" +
+ "z+cszoabo5I8dT+8tTPHrgdaCqjI9536VtbgmU3LKNG1DNkmR6nqICo1Lg0+GFnk\n" +
+ "FnEQpbV8WsckwFempDND3MtZKw6U5VxGs+EdTj0s0UEbCNVwLkW1uBFyfcnODpWe\n" +
+ "2VY0wPafAgMBAAECggEAZvBnsyq66/4F46in9ogWO+ScpEJOu/XbuFDsen66ayx0\n" +
+ "5gVZ+rXISxfmPn3hG44Q+7QpyjiHn4rSVbFU7OqZBLxdGbcpycnnGlBtjWtTSD2o\n" +
+ "VSSYzs3KXzOLlJwLvR6oxdJgniocT0FLP6lRvw5MiakhvNIl+Pca3VKR8fTbLPet\n" +
+ "wXsTSQ+80GRAUyLMKMn1Xs7A6AIrXodFmwuVFB8MUdAf8g6jGEacagz26SBzl34v\n" +
+ "YfcF6RWKnBJOwz0ej0sCX8rUyB5rBqGO/z9Ey9XzuaCYJgc4SBwcohsvVsBCGD+E\n" +
+ "8aXYPC5v9nWOSBRnzZruw/hg/j8YHP8Coy8bavtTEQKBgQDWVM6AI7CDH2wbqKdL\n" +
+ "L3S4cILpQ4nP0ajF7Ql5TCAxLTWSCvryMSBeas4tnG5oM9+z5JQiIzveCOqtMd8s\n" +
+ "geysLwk39wBN50uZPiOSlq+PxraPibDo9cI/hmi5IKIXqxnJS3OQ1LqJ4vDun7Yh\n" +
+ "rsXhk0UN62irRqXTzVY0qPlZBwKBgQDKgVvi63Lm5KVTLDB8Q162Hr83QV6VEMjK\n" +
+ "yd3AEaps+Q1McyKuub6VSucj3JyTZXDpUYUSoZaRrD8qqNDZ+h/uxHs8d8PD+jW7\n" +
+ "c3z3YVkeUezAUOdwZMvQ0SXa1zdf159rRAKl7Xj+VLhR0+3HXxw6RYsxCPhfHYy5\n" +
+ "Lukh1BoHqQKBgBFrBvUm8VtWnGSLCj1z99pdWmY2lOaMtViQcOqoox0b/XSG6+nu\n" +
+ "0CCcMXFHezmArbdi5h74Gg9rThcRLH/jdyZvFCK2MhIir+QeRqnNEStwDLoRiI0G\n" +
+ "G+kptS0GV+Xwg8H2HcgxYY9/H/FkjVqjZ3VzkHMXJIR201cpIs5YxRrVAoGAM+CB\n" +
+ "vpccn2PRqoX2gc7sc3FbAPfBGCTtm22tXifoZfRDYONZ7jLtTOecYQaCIgxpqYvV\n" +
+ "sFku7nCW2gHXRxAZoBw7idkQkKMHotbKG8GXh/nq0bWoJJXd1MfPj8l0iRv+3gbV\n" +
+ "OtakGVtwwJ2vG1UVMSRhrRUkM5GpXENVO/JPHMkCgYBn8rw3sPZ6gNk2ttF1yKGA\n" +
+ "Kr3Wb2rwcG5Pf5ESCNktuvY6ipxnOGuvmbMnWMQe9KnEbFrjiEDgYTxcnHztPAt1\n" +
+ "cK8KDB8cBrj2OHIMc83Yfon5mM8VybTTWx3Cd/AAfhyNB0vohbJNeHO80sQH+2Cn\n" +
+ "6aAcVdLuUdYcYq6RrIPM8w==\n" +
+ "-----END PRIVATE KEY-----\n";
+
+ static final String serverKey =
+ "-----BEGIN PRIVATE KEY-----\n" +
+ "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDA7nYmXdUcCgkD\n" +
+ "nwDPV0MEjbNQvVNxL9Sd/zJZBy02jcn2mklBJtAb18Nvra7tw14LKmEILFC4GwKV\n" +
+ "EtF1vqE6gBCxaohE0ZZ/dJGgPVvuRn+r1nzIwT4LKmDoayfne29Kw2oDvxX+0vU9\n" +
+ "BI9c2N8ZN3Ge9n/r8Rp25SP0cyRKa8Q862kPXgsFsYX/D/mZpK/pKfwhLGl/6tCK\n" +
+ "TsOteetEvHPnirHIYgb81zI5NGirrckO6PJ3b+PJzdjgW3/12jWg9JrqT9Wg3Uf3\n" +
+ "VESGiUoAr9xnMun026jpWhRJL0zzZnpP0hr8RYAJ2Mfh8JpZjlx60ePzHPTckRyd\n" +
+ "LPc8tvCvAgMBAAECggEAScjpFrM8FYUg/WmJ/cH5t3wZ3/8IMnmAbwxyTOoZuItx\n" +
+ "egZ3jZsya/OQot1h0Tyucsa6ZU3NcRujWS/hO460SpM/zxpXEzq0u/nw17+fsPj1\n" +
+ "Stq0znJZMBv9A+Y3VKg4X/dsTBKAbvxvHe7ohTHL4PD7WzgapDmJTX9EyPBgKLVz\n" +
+ "+6z6wOtP2KwjtgWBipm3gMFNUSJVEtgD4XQP1nOTF+lUI51dhW9iVFbx5l85zrtX\n" +
+ "gzqTMNVxQTPpdTvILUmjJBFEgZcGrq8mGYcXSHZhdIgPYLk3G1bexvtUTVzq8zbV\n" +
+ "/vrCADb25B/1jT+u+txAkh9XpJM/sQFylUoo4EDGgQKBgQDss0JVZ4504zqlncFe\n" +
+ "IizOOEy1Ba+M5DNZCqx9RlHJdMTDCdJi0phHvvaOhwXvyKLJkJuzxByUb5QthQ5p\n" +
+ "Yc1yU14ps8K7d9Ck8HX3K035SPprYQrBd0XMHzaDS2+2zr+/c+xyf/xMTC8iaUvl\n" +
+ "lizbu8O442PlyMHUbrVONeS/xwKBgQDQqZl8biSO0ljYBgYNc8INtS/TTFq5lUqP\n" +
+ "LgFfi1He2wj44nGd8iSHuNbaJJCv0UJVjKlZYpazN4bwzJSckrlU72lp6WFNapqF\n" +
+ "CDRV295PDbwaE2NRi2pJ+inTFpN1nvRC2Rx54UVKBDDcU+bZQw/WuQJ3LQcMlSVz\n" +
+ "QYoQXb6X2QKBgBSkES3HaQnSYuPcXOdrjYKyMCY9B7D+mWezYZVPE4TA1QO5EIqj\n" +
+ "mLnw8ik9pwvg8CkpnhpQCLn8/Ov3RWl1KOhGUtjKHzof2ab4fSD/ur35WjUQ8lIq\n" +
+ "p4CEXEmYw3Yqk1gLsNvPQ14X6qhSjFbKAMFsn0W5NpXsKtLukIrwcjEzAoGAWGql\n" +
+ "KP6a6xHip5bV1blpTtmprEU8ZEsITudVmaC1TlNN1/hL4HuMUx5VnBXGYVmwXAPA\n" +
+ "dqm55bLvsPVfO4FImt7fsgs8OcukMh6p3n/OEX1maT4x5YnHvhUMx+9XCI4UPoc0\n" +
+ "88gqzhQ8h//dX8501a2Lh+hChmhkeBQbZpfyfPECgYAj5dPvdITryOyjsEzkPcex\n" +
+ "zhwrgrdrSk/B+AlhmT3y6AwQ4ZI0uABJeVMpzQE6sS7lMkg7intgGIsienW60vdH\n" +
+ "oGxJxL7KGnzcY32N66a9SRReU3k6KWC0dMbWOZi6ebLLDXcjc4DIlUscUcHFEwfS\n" +
+ "AkZ3d5ywA+rEzXzaAquoBg==\n" +
+ "-----END PRIVATE KEY-----\n";
+
+ static final String arbitraryKey =
+ "-----BEGIN PRIVATE KEY-----\n" +
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDK75YzIJIdJy6D\n" +
+ "R/XzaJRyVme+MUQ2E346TIzbyWair73kDISFoCxZDzNXzLF0BHl8X6yzBLeW4dAq\n" +
+ "26+xnzg2qJbR/hzPHcaFw6ml3AxbUXVcn+JzO0XcBEBoWR32t6InsEBulDq1qTlO\n" +
+ "bOBkC9D5NKCjWRURiZ0fIcj8Z8geafjOVQTjvwE7jrCkvrl2I4yOZ3aH/G08fcQ8\n" +
+ "pHm4jEy5n8u+Oa3quip2iwuzSkh7AVBLyZWkK0c8iuc6LhM9En5N0H75wjqCzp4N\n" +
+ "dVmdyPPI05evSdl+hEy8AIja7CNuM77gmtuIynnoQbwyBo32uvPmEKu2ZHZbHq3v\n" +
+ "RKBHuyUjAgMBAAECggEBAMi9jtosUdy8sWnzePu6a31es2iT22GSjr6kkoGnC/vJ\n" +
+ "1BENwNldxACk5KjpNnAJLRM2oOLEu8ZowT5j6bvOQBDxW5+FuoG2dnZDQkFrFl4O\n" +
+ "igWBssNB0qz9F6kg3l7671BLLLE1t42TQ7isQps0hRa+VFjA+fJLKj1tch8bmf1a\n" +
+ "Gfcscs0ornwerXlMtRXcunVh64/aRQpT7/f3RhDvucASXrly28gLh1qx7AxtpgJ0\n" +
+ "tHtaenq4yYJujZHTJfIkB6WzAwT0RhVJYL8hm+DjJePG65u9b/W2oHCt3Mi3Rj+b\n" +
+ "XlDEPFmD2SXvuu2tenkEHkOMQRQpoOh69Vc6d/bQlMECgYEA9yNHnHrctc6N56k+\n" +
+ "zCPMI4nvgWEeKPczdfVmzYQonwqNhPim5R70hAKmRMznrcvPjCNH71u63BvPfMFQ\n" +
+ "PasKCBG10TGIRlpT2V0uOplTenV7xeOM7jVkQpjaz+NN7PoApL7bv9vCLL0l5TGS\n" +
+ "t7+s8DyIjRHJqxHmgLhWTbk4aMkCgYEA0jaIv4xEq9rjhneQ3cjNNn6hbunXqRuR\n" +
+ "8hxwDYFGhl6LcxhWF7chQGNl4TTJvXtTms4Umt+vcTJL7iSkLlpITQBWuyU310cL\n" +
+ "1+DXEh5J4f3iqmgkbdsVHcYmOOLQnuYssn7+cblB6dadSB/mZui6zkIrvopnoLVb\n" +
+ "YL9DjxioQIsCgYA5NZaHN73N7GHXJcuesA66j1y9I4E61Ha6MLO6kYRhxKycAn+H\n" +
+ "/JF32bEprhFXnx2NgEFPvHlWKK3wYEO18tkgoxDmu0OjnZdZcwOXlxTG/VlIpvNh\n" +
+ "1UQ/UmkcxK6uU/VALdpq4HFjr+mM09v1404iUrD9jweTLVKhq4p29ZCEWQKBgQCO\n" +
+ "mD+a7+OFUC4XAPRb/eJ2nN+VBTstk24k9fVss8zLSUb/A/siiy8bJlHtuok+53GH\n" +
+ "CVQg2qt/9cZb/K8CYmu5EAnFWTHP7nmyLuq1d6ZWjoo7XfmYK4zfbZJv9CvgHfMk\n" +
+ "AdFIA4savGJkkn8QP764O1rBHdG9ykf6EMQbRXackQKBgH4GygOQ3ndk+h+ArM78\n" +
+ "P8v0Cn8/30qHyQH8X8y9/BEk3Wz3aJ+AwgORaW4FZxVelWiOTJgHdZNy6Rg90q7I\n" +
+ "pKHE9hkL/dxsEBXkVdaRHzrfxCBxsP3b3EmOJ4xdl1phGhedUk/+RUWGY8VSnyaq\n" +
+ "uVMy1feUqV+AebfUSPzPVubV\n" +
+ "-----END PRIVATE KEY-----\n";
+
+ static final String encryptedClientKey =
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+ "MIIE6TAbBgkqhkiG9w0BBQMwDgQIZuy9oMDzt8wCAggABIIEyH7Rbg5VcLV/ZDqp\n" +
+ "AJ6tzPqMlnLVtspB/qk5nZRz2/n42yaqgO2eO7DJ5qNbBGZ6KzLosC6DHd1aXA7E\n" +
+ "8l5RTWQj0u16ANeKsjJ86o5/MRJkeUMh2tPLdVcG0e5nifgbWzXYpS6LuwKQpGEL\n" +
+ "ufSAWhF9BY1hMe58qzCFBsFjrd0tB232sDOnhopaLiO5tQ6h3iMvwK/EPXJGPKHX\n" +
+ "5++DlV/QNaENb/EsCvPlrRgpSWn6dAJ1kemKcvLbnHzTwK88B1n3GUr1B3Z+0jSV\n" +
+ "9IGNeIRVz97KGxW0v33SD1Kw4QpPH+PHH/HhmJ48Vv/mf1rINwA/yRE0iS89glhW\n" +
+ "NPh5O7HD2l2qZZDq74RUfvSzaW738/108ppck/uo+nOvEHnl346XJ7Etbp+fRYj1\n" +
+ "y/irfT7yZ1ZMUkms39M+yBwCp/AqdhvJemOLCAgSJcWWWsNnNqlQlJJsuvkcRPBr\n" +
+ "foYAsPWnZRcowZLyQ80gG5ZKoTa8Kyf/V2CuULHwYv16MLf0koLpGAEjd4QDLrun\n" +
+ "ybsRisrAFupgxNBLovyh6I4b97n0Qeotf/iIfmBOH7azYqpmvgJxNB6hNVwz2LAM\n" +
+ "JgnirRgY+FEdAyTd44/qPQpwXoz4ac3wrdGEU0oLwKk/J2bX1Eqh3tfziPycCO6v\n" +
+ "2IC3WGP59/ty3T1xA/LI0TqInAkUD+0Rkp1HhmPL4xEL/bGLL5FViFkybjO6pgoK\n" +
+ "xjq/dqTbrnmjyhVIjfdCbl2MFEsqUgemH/5J5o75P6mS0HOBaK6ERI9tM6dHz5Wk\n" +
+ "2AbwUUvEebLA6zR5+57dFKSQXhOOekRCQ9w8gDhPHMxyYp3YAdiFitweY/5Xhokt\n" +
+ "l3OXtLPNiqONcYp/Xkz5/CgsjoIHDGgQGy4sWYUQYCdAFXP8/ogR4zVpsUBP0Hjv\n" +
+ "9isg5MlXEEj+YOFYQr/HbON9zxP4LhzuYUWynvVJDfPxY1x7JI+aequHZRPj9Qct\n" +
+ "X6Va5c+G8q87fj/qCJCplGslhJsxBidTQw88+wNeVAVPd+456bszwIE5ey7glvTb\n" +
+ "gwmU4mXDm75T+PW3Y7Gz1HZ9ZCMuUBelBEKxVSOOXCxkO4zzfI9PVxVzkqfurVOO\n" +
+ "9R9i3gV3J6rQj98XtbvBTSrq9nylnzRiFSP2T0aRNLVRmGBahm9YIK+kll/VM2uL\n" +
+ "VzUYPJv22Q/cXR2lkwNFbScOGNzJ3UXiuWOM1CkBgadoHpKhmD4IypvMpA/B0tTL\n" +
+ "l4kL0z7Cg50kXYY77pl6cxorRnPs9fTpPWTPymqWp8Se+pfn/Fxqz11xD0P45Rlo\n" +
+ "0CHLS3536c2zX3/1Uh4ZIGTAwoWeiCfizTnaS7GbIniqJ1/KVx9L5gIu/uWHGSnY\n" +
+ "sU/gOHQw+HxkYGXYI5AqWUp79CZUEnKsNVZDAexKWraG3TBJSydZcyl3OcEfez+f\n" +
+ "ISw//WpK5Wc+Zc+KabT8eWGwN1bIV/5HP1hSx6kggtQBs6BSkEDirzzzvQeIvdol\n" +
+ "ddjGSudEjO2EBE25utg+5omiR8uCQwwInNNOr2otMikmWRgQ3AuIuqxYAF0qH1+f\n" +
+ "n6ePw/b30oy4xIKWz8Kxrn8oPLYgs1Dl3lF6LjRwq1urC2leYb6ZPaX5QjdarMJp\n" +
+ "sxOT5nOmsaG0vinlsQ==\n" +
+ "-----END ENCRYPTED PRIVATE KEY-----";
+
+ static final String encryptedServerKey =
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+ "MIIE6TAbBgkqhkiG9w0BBQMwDgQIto3hFjULIx0CAggABIIEyNWG0f8x6yY3XeE+\n" +
+ "WZjuFYe5WG4ftC8hvKgYCY1NhkFSH3R4FIkAdRanuskUI9rU6tzXEEVKpnsMNTf5\n" +
+ "AhQz/bI4fdu57vyz/ujwmdBMtP9m5hBjyfcJq6w/g1bECC6garbeTYvXgUljyV1n\n" +
+ "h8WQ4rClFhAZ1Qrujc6DTd63BGVoGxY+Jxe6FA2mZj2Z1bCsiWFGU0ShnU+QDF/O\n" +
+ "2SPcHHy37uckIdSrSFAuY8+iwQ5fL5mRH3Gw1CopQY07R8RKyRKpuZV56zJDXmGv\n" +
+ "j0NfJul26EmqXJSTLJyMlYdIP+xSPo50qTWqOl7w37fRcAw7duVP2jIlHJEJD9Y1\n" +
+ "UJe4ypihLp402Amw19wnsaxwk9PDDUd7kN1xFQMJ/eVF5k2hEL3m+2g6PbuFoSHt\n" +
+ "FVQEMDGqeAIPvrUr9FwO0qU7x0hJ1ce/v6IyYhJwbuVDzUFP0nHalTSVM/lXOshO\n" +
+ "dUqY97hOMh+q9drNUCQM08gq3XD/HQP/zA6LYH+X9Ts9Y0R+cocOXwbxc2jKAimo\n" +
+ "MwmP9bSD9MPabGGTv4ZeTVK9JTq4uetCk3ehqNEpS7d69bq0pJ3F6xvGaL7GjiQA\n" +
+ "8YyEUFtbyaBDgg4iwIiPsy/jo7h6Gj1Io/TjcDnp38h1YMTrER2nljB8lSm3t7DA\n" +
+ "5KOzLKHvvB8gUfMiM3OzbIcXtBJIWWidELToqEljAVHWt054yaI4oX1laHOa3zPG\n" +
+ "yriTUARkp5lA2llrm9E1AevlOxpUz3cr94ohvSI40QZgehcvraHJ7FCP7SUi54uY\n" +
+ "o7MRDe27zLsjYRoSLD6saSFL1XigjCa4dpOu3yN8q/R0NyjmBySxOlPbglyl/OmD\n" +
+ "czWFvUQcHcuSsPosbp30nsDD5SIGY6p+XR8tqJdKrCh+vgFlQPb1OkvGcXD7uzjI\n" +
+ "0jYVc23pUwrM3oynAjll2uEBlMfHsdYTB5ehv/sp7tqnc3/VbPhW9WOercbQvVnq\n" +
+ "Gb13aL4ra94glERJFH5LitvmjkylD6tjOttDONYg9ow1dA5g2q/A7WD0UV4TrW2w\n" +
+ "LRze30kKG2p94DE1if54VMA91QLhU3YwntDSk8jZ6bO7rG91d98npwHWZfMzJI6J\n" +
+ "blHM5gaAmHCTvwX6/Px/tS+8HmG5l8BBBqz70gh24uYjMgsTWsfRsjfypBmXzEFt\n" +
+ "JiFJIBwGQ0uHDM2JXqIdCf1em875eK3XR6qvrQDPiA5HVXFaGdRGbx0aK7lzDHrn\n" +
+ "7cn02kNLb4/kbZnpj04p7/j6dpfGLMnkSH7Pxx4f0cfirfDvcZQwjpl/n4bHnIxT\n" +
+ "Qv6XWq67dQObvbiCZF14CayEEqNU6Q9wq+EBji2739FhAAbZ0P1Uj792wk50tOgg\n" +
+ "+EqXzCGaqSRn7RrMEiQQTGBson9L6aBlNxyBRH6LMLAsvcKzlYB5zMQHwObd/eaU\n" +
+ "43WdwOly9CLeJbTpPNvZoeDGT7jdin3XiuauMnDa4W1wwKHKgpbD543ScZfIkNm1\n" +
+ "Wcibp27rE4/5eIsNyQnxkDRN7f3x0a9iwhlgrToXGabileLsonx3Jdf/YXgiqiQY\n" +
+ "4lLGr4EIVjKg8YbJvpnrEZQNeTHwPr8/N5N51VZvoVePBq/ZnBi9EK/aCL+f+0Dx\n" +
+ "yZio9eDGsuOXwR7iOg==\n" +
+ "-----END ENCRYPTED PRIVATE KEY-----";
+
+ static final String encryptedArbitraryKey =
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+ "MIIE6TAbBgkqhkiG9w0BBQMwDgQIT+Z6cmw0caMCAggABIIEyCCZWzPzrAGr7nsi\n" +
+ "ifNxQbX0GO6wYb3ND7yinc/0Yw+d38l0S5LTLg+5GD7SbXfoMmC44rUxSGnBV5Bj\n" +
+ "/bUW30B/s0+I3Ya1pXXyVP98vjy71Pspw/sHH3tYU73fPdqzh538Jf9qEgqgaJej\n" +
+ "MRdiQfQmXexmkyeWAYQutbONrwfuH8NqLGxuPjrc21WjKkijnv9X8CuUk6M1MCPc\n" +
+ "jN2iPhTsYiOkgKkyjF817792jDBYJHtoOuG5OmhSAdw6I3Tm5TRqAO/6IGPuDqWb\n" +
+ "7ho3ZyLBxBaLHTOawQkGm842lhwn5fACCCYNGsQhVGdbZN4KVgSYzJwvGw8vaawE\n" +
+ "VhD+zcDzhAydlhMXwFxDMgoZD5qRZujuDoBLgBYrDFDDEUfDNoUJSmWPpHXVc2tq\n" +
+ "RBiP3xb152lWiqRcXLZVHfXfxT79qZAxvlRBUgYEA/u2ud3p2/7R9UBvRzE6P6IA\n" +
+ "9ZuO6CgJgGvHXY2u721/MvvhY/75X1c1t0VJsdU2H6+E34QDtSqmQD08zBlZw5TK\n" +
+ "qltQpxgjMO1jtdEuHWPbR5Aoa5smOL9qBleptSGsm0I5zET+hv4y3f5AHIx3X3xy\n" +
+ "UH6Nw30oQtSbbZzPX4sqZuzA66W9Z4yW3Pn1EYcpuacypty/gvRZrvnwb6gYNB+h\n" +
+ "XTTaikNiGebcAZhSukcKmL3mjlpbUZC3Bjbz5AstZJyd0tMSaw0E4C8VQQHYyX5X\n" +
+ "lteW1O2TvHyGdeb0LNwsXzIMqsqmgsuNsms12Xx7KurxhPGk3Za2SdXQUASSryWX\n" +
+ "XPlKxlQDDM9DvSQsRw6BCUoUKZ/2YiXnnL/zrY1+xegXumwolgT8Dsu6CFxvzT7E\n" +
+ "16/4FeaVDB0/A5nmR71gkwm/b/JJL/Qj0H+E0rhCyZJeW9ddg4IWolEFTvCrVmHG\n" +
+ "gZ8DQIHJHSPilG8gdjzJ8cs89pctswnjuLMI2wT2/jqtM76Ibr9d0eIB6CzT17dh\n" +
+ "ASchmnUUXgjY/djAO8M3LuMUgu6OTCpIwAjfz7DOaakI42Dvrz0sTCkBwjD3XY7v\n" +
+ "1EyxoxCPoxTlHE1BLNgT5OD0B9SPC9aQLt+DOcDXVh8vQvIGtqMbxthIoi61XTeo\n" +
+ "sm35wkdiC0wSCgqPKIiz/LcyaWcOwbK2F7Q1dKluBT29L6X35FLqbp8RNZLJSX18\n" +
+ "53gJJLH4Hd7XcAn2Sat+vyuY3Z3vNiFMmx+vbho3ZgGL5KTNdmpN1fM7EqpGOUfp\n" +
+ "easRPOw3P1KuVqTVCvm/osf4VniV3xCvgwffJ/kOlHChnIrHGZpeBFmiV4xk7ucb\n" +
+ "7PSVt8BLV2+krXSTijFepeuVckitj3Kf4o++WePE6wlPi0rPYG47IE10xHojSKgr\n" +
+ "xdEonxiGL07/MEdI9+9PHLg5+ZakymRmMnEcJdpd7+WxaFy8jhvCCZ+7ppZBpYQC\n" +
+ "azdkciNz9d/YZRQH5M6VGk1VqqkQbenasUuZpXAxDOraLPlq801JyYyPqeROAgb8\n" +
+ "hTQXseAe+7lHWpmUGKuN0Y9EcV/3fiAP+PeMt/QXeGF5jcRVGWLs6nDVOaBQ/SB3\n" +
+ "syFXwJR6WtajR5OC7dEGZPTxaNfiu4GFuE73i3cVEJH2MH7+c/yB/40RJQdJ4kd6\n" +
+ "7KJYwr8P4TrNulM60Q==\n" +
+ "-----END ENCRYPTED PRIVATE KEY-----\n";
+
+ static final String secretPassword = "secret";
}
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java
index 44e7fdf..0edecca 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/server/SlowServerIT.java
@@ -97,7 +97,15 @@ public class SlowServerIT {
// binary is requested, the delay on the server guarantees that the
// timeout on the client will expire.
- StandbyClientSync client = new StandbyClientSync("localhost", serverPort.getPort(), secondary, false, 1000, false, folder.newFolder())
+ StandbyClientSync client = StandbyClientSync.builder()
+ .withHost("localhost")
+ .withPort(serverPort.getPort())
+ .withFileStore(secondary)
+ .withSecureConnection(false)
+ .withReadTimeoutMs(1000)
+ .withAutoClean(false)
+ .withSpoolFolder(folder.newFolder())
+ .build()
) {
server.start();
client.run();