From fa18fec36e8b8dda00bb2ca005938cea26180e24 Mon Sep 17 00:00:00 2001
From: Riya Mehta <55350838+rmehta19@users.noreply.github.com>
Date: Fri, 27 Sep 2024 08:47:56 -0700
Subject: [PATCH] s2a: Address minor comments on PR#11113 (#11540)
* Use StandardCharsets in FakeS2AServerTest.
* Use add instead of offer in S2AStub.
* remove dead code in ProtoUtil.java.
* Mark convertTlsProtocolVersion as VisibleForTesting.
* S2AStub doesn't return responses at front of queue.
* Remove global SHARED_RESOURCE_CHANNELS.
* Don't suppress RethrowReflectiveOperationExceptionAsLinkageError.
* Update javadoc.
* Make clear which certs are used in tests + add how to regenerate.
---
.../channel/S2AHandshakerServiceChannel.java | 14 +-
.../io/grpc/s2a/handshaker/ProtoUtil.java | 28 +--
.../java/io/grpc/s2a/handshaker/S2AStub.java | 15 +-
.../tokenmanager/AccessTokenManager.java | 3 +-
.../S2AHandshakerServiceChannelTest.java | 12 +-
.../io/grpc/s2a/handshaker/FakeS2AServer.java | 7 +-
.../s2a/handshaker/FakeS2AServerTest.java | 18 +-
.../io/grpc/s2a/handshaker/FakeWriter.java | 165 ++++++++----------
.../grpc/s2a/handshaker/IntegrationTest.java | 80 +--------
.../io/grpc/s2a/handshaker/ProtoUtilTest.java | 41 -----
.../handshaker/S2APrivateKeyMethodTest.java | 5 +-
.../S2AProtocolNegotiatorFactoryTest.java | 7 +-
.../io/grpc/s2a/handshaker/S2AStubTest.java | 38 ++--
s2a/src/test/resources/README.md | 37 ++++
s2a/src/test/resources/cert_chain_ec.pem | 36 ++++
s2a/src/test/resources/int_cert1_.cnf | 14 ++
s2a/src/test/resources/int_cert1_ec.pem | 12 ++
s2a/src/test/resources/int_cert2.cnf | 14 ++
s2a/src/test/resources/int_cert2_ec.pem | 12 ++
s2a/src/test/resources/int_key1_ec.pem | 5 +
s2a/src/test/resources/int_key2_ec.pem | 5 +
s2a/src/test/resources/leaf.cnf | 14 ++
s2a/src/test/resources/leaf_cert_ec.pem | 12 ++
s2a/src/test/resources/leaf_key_ec.pem | 5 +
s2a/src/test/resources/root_cert_ec.pem | 12 ++
s2a/src/test/resources/root_ec.cnf | 14 ++
s2a/src/test/resources/root_key_ec.pem | 5 +
27 files changed, 345 insertions(+), 285 deletions(-)
create mode 100644 s2a/src/test/resources/cert_chain_ec.pem
create mode 100644 s2a/src/test/resources/int_cert1_.cnf
create mode 100644 s2a/src/test/resources/int_cert1_ec.pem
create mode 100644 s2a/src/test/resources/int_cert2.cnf
create mode 100644 s2a/src/test/resources/int_cert2_ec.pem
create mode 100644 s2a/src/test/resources/int_key1_ec.pem
create mode 100644 s2a/src/test/resources/int_key2_ec.pem
create mode 100644 s2a/src/test/resources/leaf.cnf
create mode 100644 s2a/src/test/resources/leaf_cert_ec.pem
create mode 100644 s2a/src/test/resources/leaf_key_ec.pem
create mode 100644 s2a/src/test/resources/root_cert_ec.pem
create mode 100644 s2a/src/test/resources/root_ec.cnf
create mode 100644 s2a/src/test/resources/root_key_ec.pem
diff --git a/s2a/src/main/java/io/grpc/s2a/channel/S2AHandshakerServiceChannel.java b/s2a/src/main/java/io/grpc/s2a/channel/S2AHandshakerServiceChannel.java
index 443ea553e5..9d6950ce04 100644
--- a/s2a/src/main/java/io/grpc/s2a/channel/S2AHandshakerServiceChannel.java
+++ b/s2a/src/main/java/io/grpc/s2a/channel/S2AHandshakerServiceChannel.java
@@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Maps;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ChannelCredentials;
@@ -30,16 +29,15 @@ import io.grpc.MethodDescriptor;
import io.grpc.internal.SharedResourceHolder.Resource;
import io.grpc.netty.NettyChannelBuilder;
import java.time.Duration;
-import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
/**
- * Provides APIs for managing gRPC channels to S2A servers. Each channel is local and plaintext. If
- * credentials are provided, they are used to secure the channel.
+ * Provides APIs for managing gRPC channels to an S2A server. Each channel is local and plaintext.
+ * If credentials are provided, they are used to secure the channel.
*
- *
This is done as follows: for each S2A server, provides an implementation of gRPC's {@link
+ *
This is done as follows: for an S2A server, provides an implementation of gRPC's {@link
* SharedResourceHolder.Resource} interface called a {@code Resource}. A {@code
* Resource} is a factory for creating gRPC channels to the S2A server at a given address,
* and a channel must be returned to the {@code Resource} when it is no longer needed.
@@ -56,8 +54,6 @@ import javax.annotation.concurrent.ThreadSafe;
*/
@ThreadSafe
public final class S2AHandshakerServiceChannel {
- private static final ConcurrentMap> SHARED_RESOURCE_CHANNELS =
- Maps.newConcurrentMap();
private static final Duration CHANNEL_SHUTDOWN_TIMEOUT = Duration.ofSeconds(10);
/**
@@ -72,9 +68,7 @@ public final class S2AHandshakerServiceChannel {
public static Resource getChannelResource(
String s2aAddress, ChannelCredentials s2aChannelCredentials) {
checkNotNull(s2aAddress);
- checkNotNull(s2aChannelCredentials);
- return SHARED_RESOURCE_CHANNELS.computeIfAbsent(
- s2aAddress, channelResource -> new ChannelResource(s2aAddress, s2aChannelCredentials));
+ return new ChannelResource(s2aAddress, s2aChannelCredentials);
}
/**
diff --git a/s2a/src/main/java/io/grpc/s2a/handshaker/ProtoUtil.java b/s2a/src/main/java/io/grpc/s2a/handshaker/ProtoUtil.java
index 59e3931d9e..129cc2d60f 100644
--- a/s2a/src/main/java/io/grpc/s2a/handshaker/ProtoUtil.java
+++ b/s2a/src/main/java/io/grpc/s2a/handshaker/ProtoUtil.java
@@ -16,36 +16,11 @@
package io.grpc.s2a.handshaker;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
/** Converts proto messages to Netty strings. */
final class ProtoUtil {
- /**
- * Converts {@link Ciphersuite} to its {@link String} representation.
- *
- * @param ciphersuite the {@link Ciphersuite} to be converted.
- * @return a {@link String} representing the ciphersuite.
- * @throws AssertionError if the {@link Ciphersuite} is not one of the supported ciphersuites.
- */
- static String convertCiphersuite(Ciphersuite ciphersuite) {
- switch (ciphersuite) {
- case CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
- return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
- case CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
- return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
- case CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
- return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256";
- case CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
- return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
- case CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
- return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
- case CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
- return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
- default:
- throw new AssertionError(
- String.format("Ciphersuite %d is not supported.", ciphersuite.getNumber()));
- }
- }
/**
* Converts a {@link TLSVersion} object to its {@link String} representation.
@@ -54,6 +29,7 @@ final class ProtoUtil {
* @return a {@link String} representation of the TLS version.
* @throws AssertionError if the {@code tlsVersion} is not one of the supported TLS versions.
*/
+ @VisibleForTesting
static String convertTlsProtocolVersion(TLSVersion tlsVersion) {
switch (tlsVersion) {
case TLS_VERSION_1_3:
diff --git a/s2a/src/main/java/io/grpc/s2a/handshaker/S2AStub.java b/s2a/src/main/java/io/grpc/s2a/handshaker/S2AStub.java
index 8249ca59d0..bf9b866ef9 100644
--- a/s2a/src/main/java/io/grpc/s2a/handshaker/S2AStub.java
+++ b/s2a/src/main/java/io/grpc/s2a/handshaker/S2AStub.java
@@ -84,6 +84,7 @@ class S2AStub implements AutoCloseable {
* @throws IOException if an unexpected response is received, or if the {@code reader} or {@code
* writer} calls their {@code onError} method.
*/
+ @SuppressWarnings("CheckReturnValue")
public SessionResp send(SessionReq req) throws IOException, InterruptedException {
if (doneWriting && doneReading) {
logger.log(Level.INFO, "Stream to the S2A is closed.");
@@ -92,9 +93,8 @@ class S2AStub implements AutoCloseable {
createWriterIfNull();
if (!responses.isEmpty()) {
IOException exception = null;
- SessionResp resp = null;
try {
- resp = responses.take().getResultOrThrow();
+ responses.take().getResultOrThrow();
} catch (IOException e) {
exception = e;
}
@@ -104,14 +104,15 @@ class S2AStub implements AutoCloseable {
"Received an unexpected response from a host at the S2A's address. The S2A might be"
+ " unavailable."
+ exception.getMessage());
+ } else {
+ throw new IOException("Received an unexpected response from a host at the S2A's address.");
}
- return resp;
}
try {
writer.onNext(req);
} catch (RuntimeException e) {
writer.onError(e);
- responses.offer(Result.createWithThrowable(e));
+ responses.add(Result.createWithThrowable(e));
}
try {
return responses.take().getResultOrThrow();
@@ -159,7 +160,7 @@ class S2AStub implements AutoCloseable {
@Override
public void onNext(SessionResp resp) {
verify(!doneReading);
- responses.offer(Result.createWithResponse(resp));
+ responses.add(Result.createWithResponse(resp));
}
/**
@@ -169,7 +170,7 @@ class S2AStub implements AutoCloseable {
*/
@Override
public void onError(Throwable t) {
- responses.offer(Result.createWithThrowable(t));
+ responses.add(Result.createWithThrowable(t));
}
/**
@@ -180,7 +181,7 @@ class S2AStub implements AutoCloseable {
public void onCompleted() {
logger.log(Level.INFO, "Reading from the S2A is complete.");
doneReading = true;
- responses.offer(
+ responses.add(
Result.createWithThrowable(
new ConnectionClosedException("Reading from the S2A is complete.")));
}
diff --git a/s2a/src/main/java/io/grpc/s2a/handshaker/tokenmanager/AccessTokenManager.java b/s2a/src/main/java/io/grpc/s2a/handshaker/tokenmanager/AccessTokenManager.java
index 94549d11c8..da75cf0d4d 100644
--- a/s2a/src/main/java/io/grpc/s2a/handshaker/tokenmanager/AccessTokenManager.java
+++ b/s2a/src/main/java/io/grpc/s2a/handshaker/tokenmanager/AccessTokenManager.java
@@ -27,7 +27,6 @@ public final class AccessTokenManager {
private final TokenFetcher tokenFetcher;
/** Creates an {@code AccessTokenManager} based on the environment where the application runs. */
- @SuppressWarnings("RethrowReflectiveOperationExceptionAsLinkageError")
public static Optional create() {
Optional> tokenFetcher;
try {
@@ -38,7 +37,7 @@ public final class AccessTokenManager {
} catch (ClassNotFoundException e) {
tokenFetcher = Optional.empty();
} catch (ReflectiveOperationException e) {
- throw new AssertionError(e);
+ throw new LinkageError(e.getMessage(), e);
}
return tokenFetcher.isPresent()
? Optional.of(new AccessTokenManager((TokenFetcher) tokenFetcher.get()))
diff --git a/s2a/src/test/java/io/grpc/s2a/channel/S2AHandshakerServiceChannelTest.java b/s2a/src/test/java/io/grpc/s2a/channel/S2AHandshakerServiceChannelTest.java
index 7845e7c3bc..7281adb979 100644
--- a/s2a/src/test/java/io/grpc/s2a/channel/S2AHandshakerServiceChannelTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/channel/S2AHandshakerServiceChannelTest.java
@@ -89,10 +89,10 @@ public final class S2AHandshakerServiceChannelTest {
/**
* Creates two {@code Resoure}s for the same target address and verifies that they are
- * equal.
+ * distinct.
*/
@Test
- public void getChannelResource_twoEqualChannels() {
+ public void getChannelResource_twoUnEqualChannels() {
Resource resource =
S2AHandshakerServiceChannel.getChannelResource(
"localhost:" + plaintextServer.getPort(),
@@ -101,19 +101,19 @@ public final class S2AHandshakerServiceChannelTest {
S2AHandshakerServiceChannel.getChannelResource(
"localhost:" + plaintextServer.getPort(),
InsecureChannelCredentials.create());
- assertThat(resource).isEqualTo(resourceTwo);
+ assertThat(resource).isNotEqualTo(resourceTwo);
}
- /** Same as getChannelResource_twoEqualChannels, but use mTLS. */
+ /** Same as getChannelResource_twoUnEqualChannels, but use mTLS. */
@Test
- public void getChannelResource_mtlsTwoEqualChannels() throws Exception {
+ public void getChannelResource_mtlsTwoUnEqualChannels() throws Exception {
Resource resource =
S2AHandshakerServiceChannel.getChannelResource(
"localhost:" + mtlsServer.getPort(), getTlsChannelCredentials());
Resource resourceTwo =
S2AHandshakerServiceChannel.getChannelResource(
"localhost:" + mtlsServer.getPort(), getTlsChannelCredentials());
- assertThat(resource).isEqualTo(resourceTwo);
+ assertThat(resource).isNotEqualTo(resourceTwo);
}
/**
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServer.java b/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServer.java
index 66f636ada2..d630f57d90 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServer.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServer.java
@@ -17,6 +17,7 @@
package io.grpc.s2a.handshaker;
import io.grpc.stub.StreamObserver;
+import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.logging.Logger;
@@ -38,7 +39,11 @@ public final class FakeS2AServer extends S2AServiceGrpc.S2AServiceImplBase {
@Override
public void onNext(SessionReq req) {
logger.info("Received a request from client.");
- responseObserver.onNext(writer.handleResponse(req));
+ try {
+ responseObserver.onNext(writer.handleResponse(req));
+ } catch (IOException e) {
+ responseObserver.onError(e);
+ }
}
@Override
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServerTest.java b/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServerTest.java
index e200d11986..a8868744f8 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServerTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/FakeS2AServerTest.java
@@ -29,6 +29,9 @@ import io.grpc.ServerBuilder;
import io.grpc.benchmarks.Utils;
import io.grpc.s2a.handshaker.ValidatePeerCertificateChainReq.VerificationMode;
import io.grpc.stub.StreamObserver;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -45,9 +48,7 @@ public final class FakeS2AServerTest {
private static final Logger logger = Logger.getLogger(FakeS2AServerTest.class.getName());
private static final ImmutableList FAKE_CERT_DER_CHAIN =
- ImmutableList.of(
- ByteString.copyFrom(
- new byte[] {'f', 'a', 'k', 'e', '-', 'd', 'e', 'r', '-', 'c', 'h', 'a', 'i', 'n'}));
+ ImmutableList.of(ByteString.copyFrom("fake-der-chain".getBytes(StandardCharsets.US_ASCII)));
private int port;
private String serverAddress;
private SessionResp response = null;
@@ -68,7 +69,7 @@ public final class FakeS2AServerTest {
@Test
public void callS2AServerOnce_getTlsConfiguration_returnsValidResult()
- throws InterruptedException {
+ throws InterruptedException, IOException {
ExecutorService executor = Executors.newSingleThreadExecutor();
logger.info("Client connecting to: " + serverAddress);
ManagedChannel channel =
@@ -122,9 +123,12 @@ public final class FakeS2AServerTest {
GetTlsConfigurationResp.newBuilder()
.setClientTlsConfiguration(
GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
- .addCertificateChain(FakeWriter.LEAF_CERT)
- .addCertificateChain(FakeWriter.INTERMEDIATE_CERT_2)
- .addCertificateChain(FakeWriter.INTERMEDIATE_CERT_1)
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.leafCertFile.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert1File.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert2File.toPath()), StandardCharsets.UTF_8))
.setMinTlsVersion(TLSVersion.TLS_VERSION_1_3)
.setMaxTlsVersion(TLSVersion.TLS_VERSION_1_3)
.addCiphersuites(
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/FakeWriter.java b/s2a/src/test/java/io/grpc/s2a/handshaker/FakeWriter.java
index 45961b81b7..b0e84fdf96 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/FakeWriter.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/FakeWriter.java
@@ -23,7 +23,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.protobuf.ByteString;
import io.grpc.stub.StreamObserver;
+import java.io.File;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
@@ -50,59 +53,18 @@ final class FakeWriter implements StreamObserver {
FAILURE
}
- public static final String LEAF_CERT =
- "-----BEGIN CERTIFICATE-----\n"
- + "MIICkDCCAjagAwIBAgIUSAtcrPhNNs1zxv51lIfGOVtkw6QwCgYIKoZIzj0EAwIw\n"
- + "QTEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xEDAOBgNVBAsMB2NvbnRleHQxFDAS\n"
- + "BgorBgEEAdZ5AggBDAQyMDIyMCAXDTIzMDcxNDIyMzYwNFoYDzIwNTAxMTI5MjIz\n"
- + "NjA0WjARMQ8wDQYDVQQDDAZ1bnVzZWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\n"
- + "AAQGFlJpLxJMh4HuUm0DKjnUF7larH3tJvroQ12xpk+pPKQepn4ILoq9lZ8Xd3jz\n"
- + "U98eDRXG5f4VjnX98DDHE4Ido4IBODCCATQwDgYDVR0PAQH/BAQDAgeAMCAGA1Ud\n"
- + "JQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMIGxBgNV\n"
- + "HREBAf8EgaYwgaOGSnNwaWZmZTovL3NpZ25lci1yb2xlLmNvbnRleHQuc2VjdXJp\n"
- + "dHktcmVhbG0ucHJvZC5nb29nbGUuY29tL3JvbGUvbGVhZi1yb2xlgjNzaWduZXIt\n"
- + "cm9sZS5jb250ZXh0LnNlY3VyaXR5LXJlYWxtLnByb2Quc3BpZmZlLmdvb2eCIGZx\n"
- + "ZG4tb2YtdGhlLW5vZGUucHJvZC5nb29nbGUuY29tMB0GA1UdDgQWBBSWSd5Fw6dI\n"
- + "TGpt0m1Uxwf0iKqebzAfBgNVHSMEGDAWgBRm5agVVdpWfRZKM7u6OMuzHhqPcDAK\n"
- + "BggqhkjOPQQDAgNIADBFAiB0sjRPSYy2eFq8Y0vQ8QN4AZ2NMajskvxnlifu7O4U\n"
- + "RwIhANTh5Fkyx2nMYFfyl+W45dY8ODTw3HnlZ4b51hTAdkWl\n"
- + "-----END CERTIFICATE-----";
- public static final String INTERMEDIATE_CERT_2 =
- "-----BEGIN CERTIFICATE-----\n"
- + "MIICQjCCAeigAwIBAgIUKxXRDlnWXefNV5lj5CwhDuXEq7MwCgYIKoZIzj0EAwIw\n"
- + "OzEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xEDAOBgNVBAsMB2NvbnRleHQxDjAM\n"
- + "BgNVBAMMBTEyMzQ1MCAXDTIzMDcxNDIyMzYwNFoYDzIwNTAxMTI5MjIzNjA0WjBB\n"
- + "MRcwFQYDVQQKDA5zZWN1cml0eS1yZWFsbTEQMA4GA1UECwwHY29udGV4dDEUMBIG\n"
- + "CisGAQQB1nkCCAEMBDIwMjIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT/Zu7x\n"
- + "UYVyg+T/vg2H+y4I6t36Kc4qxD0eqqZjRLYBVKkUQHxBqc14t0DpoROMYQCNd4DF\n"
- + "pcxv/9m6DaJbRk6Ao4HBMIG+MA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\n"
- + "AQH/AgEBMFgGA1UdHgEB/wROMEygSjA1gjNzaWduZXItcm9sZS5jb250ZXh0LnNl\n"
- + "Y3VyaXR5LXJlYWxtLnByb2Quc3BpZmZlLmdvb2cwEYIPcHJvZC5nb29nbGUuY29t\n"
- + "MB0GA1UdDgQWBBRm5agVVdpWfRZKM7u6OMuzHhqPcDAfBgNVHSMEGDAWgBQcjNAh\n"
- + "SCHTj+BW8KrzSSLo2ASEgjAKBggqhkjOPQQDAgNIADBFAiEA6KyGd9VxXDZceMZG\n"
- + "IsbC40rtunFjLYI0mjZw9RcRWx8CIHCIiIHxafnDaCi+VB99NZfzAdu37g6pJptB\n"
- + "gjIY71MO\n"
- + "-----END CERTIFICATE-----";
- public static final String INTERMEDIATE_CERT_1 =
- "-----BEGIN CERTIFICATE-----\n"
- + "MIICODCCAd6gAwIBAgIUXtZECORWRSKnS9rRTJYkiALUXswwCgYIKoZIzj0EAwIw\n"
- + "NzEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xDTALBgNVBAsMBHJvb3QxDTALBgNV\n"
- + "BAMMBDEyMzQwIBcNMjMwNzE0MjIzNjA0WhgPMjA1MDExMjkyMjM2MDRaMDsxFzAV\n"
- + "BgNVBAoMDnNlY3VyaXR5LXJlYWxtMRAwDgYDVQQLDAdjb250ZXh0MQ4wDAYDVQQD\n"
- + "DAUxMjM0NTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAycVTZrjockbpD59f1a\n"
- + "4l1SNL7nSyXz66Guz4eDveQqLmaMBg7vpACfO4CtiAGnolHEffuRtSkdM434m5En\n"
- + "bXCjgcEwgb4wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQIwWAYD\n"
- + "VR0eAQH/BE4wTKBKMDWCM3NpZ25lci1yb2xlLmNvbnRleHQuc2VjdXJpdHktcmVh\n"
- + "bG0ucHJvZC5zcGlmZmUuZ29vZzARgg9wcm9kLmdvb2dsZS5jb20wHQYDVR0OBBYE\n"
- + "FByM0CFIIdOP4FbwqvNJIujYBISCMB8GA1UdIwQYMBaAFMX+vebuj/lYfYEC23IA\n"
- + "8HoIW0HsMAoGCCqGSM49BAMCA0gAMEUCIQCfxeXEBd7UPmeImT16SseCRu/6cHxl\n"
- + "kTDsq9sKZ+eXBAIgA+oViAVOUhUQO1/6Mjlczg8NmMy2vNtG4V/7g9dMMVU=\n"
- + "-----END CERTIFICATE-----";
+ public static final File leafCertFile =
+ new File("src/test/resources/leaf_cert_ec.pem");
+ public static final File cert2File =
+ new File("src/test/resources/int_cert2_ec.pem");
+ public static final File cert1File =
+ new File("src/test/resources/int_cert1_ec.pem");
+ // src/test/resources/leaf_key_ec.pem
private static final String PRIVATE_KEY =
- "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqA2U0ld1OOHLMXWf"
- + "uyN4GSaqhhudEIaKkll3rdIq0M+hRANCAAQGFlJpLxJMh4HuUm0DKjnUF7larH3t"
- + "JvroQ12xpk+pPKQepn4ILoq9lZ8Xd3jzU98eDRXG5f4VjnX98DDHE4Id";
+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgR2HBqtWTWu4NLiow"
+ + "ar8vh+9vAmCONE59C+jXNAb9r8ehRANCAATRM8ozcr8PTOVsZNWh+rTmJ6t+rODu"
+ + "g3LwWpUQq9h7AddjGlLrrTNrceOyO7nh9aEk5plKhs/h7PO8+vkEFsEx";
private static final ImmutableMap
ALGORITHM_TO_SIGNATURE_INSTANCE_IDENTIFIER =
ImmutableMap.of(
@@ -167,24 +129,32 @@ final class FakeWriter implements StreamObserver {
}
void sendGetTlsConfigResp() {
- reader.onNext(
- SessionResp.newBuilder()
- .setGetTlsConfigurationResp(
- GetTlsConfigurationResp.newBuilder()
- .setClientTlsConfiguration(
- GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
- .addCertificateChain(LEAF_CERT)
- .addCertificateChain(INTERMEDIATE_CERT_2)
- .addCertificateChain(INTERMEDIATE_CERT_1)
- .setMinTlsVersion(TLS_VERSION_1_3)
- .setMaxTlsVersion(TLS_VERSION_1_3)
- .addCiphersuites(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
- .addCiphersuites(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
- .addCiphersuites(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)))
- .build());
+ try {
+ reader.onNext(
+ SessionResp.newBuilder()
+ .setGetTlsConfigurationResp(
+ GetTlsConfigurationResp.newBuilder()
+ .setClientTlsConfiguration(
+ GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.leafCertFile.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert1File.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert2File.toPath()), StandardCharsets.UTF_8))
+ .setMinTlsVersion(TLS_VERSION_1_3)
+ .setMaxTlsVersion(TLS_VERSION_1_3)
+ .addCiphersuites(
+ Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
+ .addCiphersuites(
+ Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
+ .addCiphersuites(
+ Ciphersuite
+ .CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)))
+ .build());
+ } catch (IOException e) {
+ reader.onError(e);
+ }
}
boolean isFakeWriterClosed() {
@@ -195,7 +165,11 @@ final class FakeWriter implements StreamObserver {
public void onNext(SessionReq sessionReq) {
switch (behavior) {
case OK_STATUS:
- reader.onNext(handleResponse(sessionReq));
+ try {
+ reader.onNext(handleResponse(sessionReq));
+ } catch (IOException e) {
+ reader.onError(e);
+ }
break;
case EMPTY_RESPONSE:
reader.onNext(SessionResp.getDefaultInstance());
@@ -216,25 +190,36 @@ final class FakeWriter implements StreamObserver {
reader.onCompleted();
break;
case BAD_TLS_VERSION_RESPONSE:
- reader.onNext(
- SessionResp.newBuilder()
- .setGetTlsConfigurationResp(
- GetTlsConfigurationResp.newBuilder()
- .setClientTlsConfiguration(
- GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
- .addCertificateChain(LEAF_CERT)
- .addCertificateChain(INTERMEDIATE_CERT_2)
- .addCertificateChain(INTERMEDIATE_CERT_1)
- .setMinTlsVersion(TLS_VERSION_1_3)
- .setMaxTlsVersion(TLS_VERSION_1_2)))
- .build());
+ try {
+ reader.onNext(
+ SessionResp.newBuilder()
+ .setGetTlsConfigurationResp(
+ GetTlsConfigurationResp.newBuilder()
+ .setClientTlsConfiguration(
+ GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.leafCertFile.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert1File.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert2File.toPath()), StandardCharsets.UTF_8))
+ .setMinTlsVersion(TLS_VERSION_1_3)
+ .setMaxTlsVersion(TLS_VERSION_1_2)))
+ .build());
+ } catch (IOException e) {
+ reader.onError(e);
+ }
break;
default:
- reader.onNext(handleResponse(sessionReq));
+ try {
+ reader.onNext(handleResponse(sessionReq));
+ } catch (IOException e) {
+ reader.onError(e);
+ }
}
}
- SessionResp handleResponse(SessionReq sessionReq) {
+ SessionResp handleResponse(SessionReq sessionReq) throws IOException {
if (sessionReq.hasGetTlsConfigurationReq()) {
return handleGetTlsConfigurationReq(sessionReq.getGetTlsConfigurationReq());
}
@@ -253,7 +238,8 @@ final class FakeWriter implements StreamObserver {
.build();
}
- private SessionResp handleGetTlsConfigurationReq(GetTlsConfigurationReq req) {
+ private SessionResp handleGetTlsConfigurationReq(GetTlsConfigurationReq req)
+ throws IOException {
if (!req.getConnectionSide().equals(ConnectionSide.CONNECTION_SIDE_CLIENT)) {
return SessionResp.newBuilder()
.setStatus(
@@ -267,9 +253,12 @@ final class FakeWriter implements StreamObserver {
GetTlsConfigurationResp.newBuilder()
.setClientTlsConfiguration(
GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
- .addCertificateChain(LEAF_CERT)
- .addCertificateChain(INTERMEDIATE_CERT_2)
- .addCertificateChain(INTERMEDIATE_CERT_1)
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.leafCertFile.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert1File.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert2File.toPath()), StandardCharsets.UTF_8))
.setMinTlsVersion(TLS_VERSION_1_3)
.setMaxTlsVersion(TLS_VERSION_1_3)
.addCiphersuites(
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/IntegrationTest.java b/s2a/src/test/java/io/grpc/s2a/handshaker/IntegrationTest.java
index bae58f2f9e..2a2b1f246e 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/IntegrationTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/IntegrationTest.java
@@ -17,7 +17,6 @@
package io.grpc.s2a.handshaker;
import static com.google.common.truth.Truth.assertThat;
-import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;
import io.grpc.ChannelCredentials;
@@ -42,7 +41,6 @@ import io.netty.handler.ssl.OpenSslSessionContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.concurrent.FutureTask;
import java.util.logging.Logger;
@@ -58,72 +56,12 @@ import org.junit.runners.JUnit4;
public final class IntegrationTest {
private static final Logger logger = Logger.getLogger(FakeS2AServer.class.getName());
- private static final String CERT_CHAIN =
- "-----BEGIN CERTIFICATE-----\n"
- + "MIICkDCCAjagAwIBAgIUSAtcrPhNNs1zxv51lIfGOVtkw6QwCgYIKoZIzj0EAwIw\n"
- + "QTEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xEDAOBgNVBAsMB2NvbnRleHQxFDAS\n"
- + "BgorBgEEAdZ5AggBDAQyMDIyMCAXDTIzMDcxNDIyMzYwNFoYDzIwNTAxMTI5MjIz\n"
- + "NjA0WjARMQ8wDQYDVQQDDAZ1bnVzZWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\n"
- + "AAQGFlJpLxJMh4HuUm0DKjnUF7larH3tJvroQ12xpk+pPKQepn4ILoq9lZ8Xd3jz\n"
- + "U98eDRXG5f4VjnX98DDHE4Ido4IBODCCATQwDgYDVR0PAQH/BAQDAgeAMCAGA1Ud\n"
- + "JQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMIGxBgNV\n"
- + "HREBAf8EgaYwgaOGSnNwaWZmZTovL3NpZ25lci1yb2xlLmNvbnRleHQuc2VjdXJp\n"
- + "dHktcmVhbG0ucHJvZC5nb29nbGUuY29tL3JvbGUvbGVhZi1yb2xlgjNzaWduZXIt\n"
- + "cm9sZS5jb250ZXh0LnNlY3VyaXR5LXJlYWxtLnByb2Quc3BpZmZlLmdvb2eCIGZx\n"
- + "ZG4tb2YtdGhlLW5vZGUucHJvZC5nb29nbGUuY29tMB0GA1UdDgQWBBSWSd5Fw6dI\n"
- + "TGpt0m1Uxwf0iKqebzAfBgNVHSMEGDAWgBRm5agVVdpWfRZKM7u6OMuzHhqPcDAK\n"
- + "BggqhkjOPQQDAgNIADBFAiB0sjRPSYy2eFq8Y0vQ8QN4AZ2NMajskvxnlifu7O4U\n"
- + "RwIhANTh5Fkyx2nMYFfyl+W45dY8ODTw3HnlZ4b51hTAdkWl\n"
- + "-----END CERTIFICATE-----\n"
- + "-----BEGIN CERTIFICATE-----\n"
- + "MIICQjCCAeigAwIBAgIUKxXRDlnWXefNV5lj5CwhDuXEq7MwCgYIKoZIzj0EAwIw\n"
- + "OzEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xEDAOBgNVBAsMB2NvbnRleHQxDjAM\n"
- + "BgNVBAMMBTEyMzQ1MCAXDTIzMDcxNDIyMzYwNFoYDzIwNTAxMTI5MjIzNjA0WjBB\n"
- + "MRcwFQYDVQQKDA5zZWN1cml0eS1yZWFsbTEQMA4GA1UECwwHY29udGV4dDEUMBIG\n"
- + "CisGAQQB1nkCCAEMBDIwMjIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT/Zu7x\n"
- + "UYVyg+T/vg2H+y4I6t36Kc4qxD0eqqZjRLYBVKkUQHxBqc14t0DpoROMYQCNd4DF\n"
- + "pcxv/9m6DaJbRk6Ao4HBMIG+MA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\n"
- + "AQH/AgEBMFgGA1UdHgEB/wROMEygSjA1gjNzaWduZXItcm9sZS5jb250ZXh0LnNl\n"
- + "Y3VyaXR5LXJlYWxtLnByb2Quc3BpZmZlLmdvb2cwEYIPcHJvZC5nb29nbGUuY29t\n"
- + "MB0GA1UdDgQWBBRm5agVVdpWfRZKM7u6OMuzHhqPcDAfBgNVHSMEGDAWgBQcjNAh\n"
- + "SCHTj+BW8KrzSSLo2ASEgjAKBggqhkjOPQQDAgNIADBFAiEA6KyGd9VxXDZceMZG\n"
- + "IsbC40rtunFjLYI0mjZw9RcRWx8CIHCIiIHxafnDaCi+VB99NZfzAdu37g6pJptB\n"
- + "gjIY71MO\n"
- + "-----END CERTIFICATE-----\n"
- + "-----BEGIN CERTIFICATE-----\n"
- + "MIICODCCAd6gAwIBAgIUXtZECORWRSKnS9rRTJYkiALUXswwCgYIKoZIzj0EAwIw\n"
- + "NzEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xDTALBgNVBAsMBHJvb3QxDTALBgNV\n"
- + "BAMMBDEyMzQwIBcNMjMwNzE0MjIzNjA0WhgPMjA1MDExMjkyMjM2MDRaMDsxFzAV\n"
- + "BgNVBAoMDnNlY3VyaXR5LXJlYWxtMRAwDgYDVQQLDAdjb250ZXh0MQ4wDAYDVQQD\n"
- + "DAUxMjM0NTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAycVTZrjockbpD59f1a\n"
- + "4l1SNL7nSyXz66Guz4eDveQqLmaMBg7vpACfO4CtiAGnolHEffuRtSkdM434m5En\n"
- + "bXCjgcEwgb4wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQIwWAYD\n"
- + "VR0eAQH/BE4wTKBKMDWCM3NpZ25lci1yb2xlLmNvbnRleHQuc2VjdXJpdHktcmVh\n"
- + "bG0ucHJvZC5zcGlmZmUuZ29vZzARgg9wcm9kLmdvb2dsZS5jb20wHQYDVR0OBBYE\n"
- + "FByM0CFIIdOP4FbwqvNJIujYBISCMB8GA1UdIwQYMBaAFMX+vebuj/lYfYEC23IA\n"
- + "8HoIW0HsMAoGCCqGSM49BAMCA0gAMEUCIQCfxeXEBd7UPmeImT16SseCRu/6cHxl\n"
- + "kTDsq9sKZ+eXBAIgA+oViAVOUhUQO1/6Mjlczg8NmMy2vNtG4V/7g9dMMVU=\n"
- + "-----END CERTIFICATE-----";
- private static final String ROOT_PEM =
- "-----BEGIN CERTIFICATE-----\n"
- + "MIIBtTCCAVqgAwIBAgIUbAe+8OocndQXRBCElLBxBSdfdV8wCgYIKoZIzj0EAwIw\n"
- + "NzEXMBUGA1UECgwOc2VjdXJpdHktcmVhbG0xDTALBgNVBAsMBHJvb3QxDTALBgNV\n"
- + "BAMMBDEyMzQwIBcNMjMwNzE0MjIzNjA0WhgPMjA1MDExMjkyMjM2MDRaMDcxFzAV\n"
- + "BgNVBAoMDnNlY3VyaXR5LXJlYWxtMQ0wCwYDVQQLDARyb290MQ0wCwYDVQQDDAQx\n"
- + "MjM0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaMY2tBW5r1t0+vhayz0ZoGMF\n"
- + "boX/ZmmCmIh0iTWg4madvwNOh74CMVVvDUlXZcuVqZ3vVIX/a7PTFVqUwQlKW6NC\n"
- + "MEAwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMX+\n"
- + "vebuj/lYfYEC23IA8HoIW0HsMAoGCCqGSM49BAMCA0kAMEYCIQDETd27nsUTXKWY\n"
- + "CiOno78O09gK95NoTkPU5e2chJYMqAIhALYFAyh7PU5xgFQsN9hiqgsHUc5/pmBG\n"
- + "BGjJ1iz8rWGJ\n"
- + "-----END CERTIFICATE-----";
- private static final String PRIVATE_KEY =
- "-----BEGIN PRIVATE KEY-----\n"
- + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqA2U0ld1OOHLMXWf\n"
- + "uyN4GSaqhhudEIaKkll3rdIq0M+hRANCAAQGFlJpLxJMh4HuUm0DKjnUF7larH3t\n"
- + "JvroQ12xpk+pPKQepn4ILoq9lZ8Xd3jzU98eDRXG5f4VjnX98DDHE4Id\n"
- + "-----END PRIVATE KEY-----";
-
+ public static final File privateKeyFile =
+ new File("src/test/resources/leaf_key_ec.pem");
+ public static final File rootCertFile =
+ new File("src/test/resources/root_cert_ec.pem");
+ public static final File certChainFile =
+ new File("src/test/resources/cert_chain_ec.pem");
private String s2aAddress;
private Server s2aServer;
private String s2aDelayAddress;
@@ -252,13 +190,11 @@ public final class IntegrationTest {
private static SslContext buildSslContext() throws SSLException {
SslContextBuilder sslServerContextBuilder =
- SslContextBuilder.forServer(
- new ByteArrayInputStream(CERT_CHAIN.getBytes(UTF_8)),
- new ByteArrayInputStream(PRIVATE_KEY.getBytes(UTF_8)));
+ SslContextBuilder.forServer(certChainFile, privateKeyFile);
SslContext sslServerContext =
GrpcSslContexts.configure(sslServerContextBuilder, SslProvider.OPENSSL)
.protocols("TLSv1.3", "TLSv1.2")
- .trustManager(new ByteArrayInputStream(ROOT_PEM.getBytes(UTF_8)))
+ .trustManager(rootCertFile)
.clientAuth(ClientAuth.REQUIRE)
.build();
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/ProtoUtilTest.java b/s2a/src/test/java/io/grpc/s2a/handshaker/ProtoUtilTest.java
index 6d134b43f7..f54063b9e0 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/ProtoUtilTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/ProtoUtilTest.java
@@ -30,47 +30,6 @@ import org.junit.runners.JUnit4;
public final class ProtoUtilTest {
@Rule public final Expect expect = Expect.create();
- @Test
- public void convertCiphersuite_success() {
- expect
- .that(
- ProtoUtil.convertCiphersuite(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256))
- .isEqualTo("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
- expect
- .that(
- ProtoUtil.convertCiphersuite(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384))
- .isEqualTo("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
- expect
- .that(
- ProtoUtil.convertCiphersuite(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256))
- .isEqualTo("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
- expect
- .that(
- ProtoUtil.convertCiphersuite(Ciphersuite.CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256))
- .isEqualTo("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
- expect
- .that(
- ProtoUtil.convertCiphersuite(Ciphersuite.CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384))
- .isEqualTo("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
- expect
- .that(
- ProtoUtil.convertCiphersuite(
- Ciphersuite.CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256))
- .isEqualTo("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
- }
-
- @Test
- public void convertCiphersuite_withUnspecifiedCiphersuite_fails() {
- AssertionError expected =
- assertThrows(
- AssertionError.class,
- () -> ProtoUtil.convertCiphersuite(Ciphersuite.CIPHERSUITE_UNSPECIFIED));
- expect.that(expected).hasMessageThat().isEqualTo("Ciphersuite 0 is not supported.");
- }
-
@Test
public void convertTlsProtocolVersion_success() {
expect
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/S2APrivateKeyMethodTest.java b/s2a/src/test/java/io/grpc/s2a/handshaker/S2APrivateKeyMethodTest.java
index 8252aa245d..fc8d42d09c 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/S2APrivateKeyMethodTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/S2APrivateKeyMethodTest.java
@@ -30,6 +30,8 @@ import io.grpc.s2a.handshaker.S2AIdentity;
import io.netty.handler.ssl.OpenSslPrivateKeyMethod;
import io.netty.handler.ssl.SslContextBuilder;
import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
@@ -61,7 +63,8 @@ public final class S2APrivateKeyMethodTest {
private static boolean verifySignature(
byte[] dataToSign, byte[] signature, String signatureAlgorithm) throws Exception {
Signature sig = Signature.getInstance(signatureAlgorithm);
- sig.initVerify(extractPublicKeyFromPem(FakeWriter.LEAF_CERT));
+ sig.initVerify(extractPublicKeyFromPem(new String(
+ Files.readAllBytes(FakeWriter.leafCertFile.toPath()), StandardCharsets.UTF_8)));
sig.update(dataToSign);
return sig.verify(signature);
}
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/S2AProtocolNegotiatorFactoryTest.java b/s2a/src/test/java/io/grpc/s2a/handshaker/S2AProtocolNegotiatorFactoryTest.java
index 404910e8be..fa6c4fc858 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/S2AProtocolNegotiatorFactoryTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/S2AProtocolNegotiatorFactoryTest.java
@@ -50,6 +50,7 @@ import io.netty.handler.codec.http2.Http2ConnectionDecoder;
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.util.AsciiString;
+import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -246,7 +247,11 @@ public class S2AProtocolNegotiatorFactoryTest {
return new StreamObserver() {
@Override
public void onNext(SessionReq req) {
- responseObserver.onNext(writer.handleResponse(req));
+ try {
+ responseObserver.onNext(writer.handleResponse(req));
+ } catch (IOException e) {
+ responseObserver.onError(e);
+ }
}
@Override
diff --git a/s2a/src/test/java/io/grpc/s2a/handshaker/S2AStubTest.java b/s2a/src/test/java/io/grpc/s2a/handshaker/S2AStubTest.java
index 47fd154d94..a1daf9948a 100644
--- a/s2a/src/test/java/io/grpc/s2a/handshaker/S2AStubTest.java
+++ b/s2a/src/test/java/io/grpc/s2a/handshaker/S2AStubTest.java
@@ -28,6 +28,8 @@ import io.grpc.s2a.channel.S2AGrpcChannelPool;
import io.grpc.s2a.channel.S2AHandshakerServiceChannel;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -82,9 +84,12 @@ public class S2AStubTest {
GetTlsConfigurationResp.newBuilder()
.setClientTlsConfiguration(
GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
- .addCertificateChain(FakeWriter.LEAF_CERT)
- .addCertificateChain(FakeWriter.INTERMEDIATE_CERT_2)
- .addCertificateChain(FakeWriter.INTERMEDIATE_CERT_1)
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.leafCertFile.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert1File.toPath()), StandardCharsets.UTF_8))
+ .addCertificateChain(new String(Files.readAllBytes(
+ FakeWriter.cert2File.toPath()), StandardCharsets.UTF_8))
.setMinTlsVersion(TLSVersion.TLS_VERSION_1_3)
.setMaxTlsVersion(TLSVersion.TLS_VERSION_1_3)
.addCiphersuites(
@@ -189,26 +194,13 @@ public class S2AStubTest {
@Test
public void send_receiveDelayedResponse() throws Exception {
writer.sendGetTlsConfigResp();
- SessionResp resp = stub.send(SessionReq.getDefaultInstance());
- SessionResp expected =
- SessionResp.newBuilder()
- .setGetTlsConfigurationResp(
- GetTlsConfigurationResp.newBuilder()
- .setClientTlsConfiguration(
- GetTlsConfigurationResp.ClientTlsConfiguration.newBuilder()
- .addCertificateChain(FakeWriter.LEAF_CERT)
- .addCertificateChain(FakeWriter.INTERMEDIATE_CERT_2)
- .addCertificateChain(FakeWriter.INTERMEDIATE_CERT_1)
- .setMinTlsVersion(TLSVersion.TLS_VERSION_1_3)
- .setMaxTlsVersion(TLSVersion.TLS_VERSION_1_3)
- .addCiphersuites(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
- .addCiphersuites(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
- .addCiphersuites(
- Ciphersuite.CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)))
- .build();
- assertThat(resp).ignoringRepeatedFieldOrder().isEqualTo(expected);
+ IOException expectedException =
+ assertThrows(IOException.class, () -> stub.send(SessionReq.getDefaultInstance()));
+ assertThat(expectedException)
+ .hasMessageThat()
+ .contains("Received an unexpected response from a host at the S2A's address.");
+
+ assertThat(stub.getResponses()).isEmpty();
}
@Test
diff --git a/s2a/src/test/resources/README.md b/s2a/src/test/resources/README.md
index 726b921a61..2250ffb1de 100644
--- a/s2a/src/test/resources/README.md
+++ b/s2a/src/test/resources/README.md
@@ -29,4 +29,41 @@ Sign CSRs for server and client
```
openssl x509 -req -CA root_cert.pem -CAkey root_key.pem -in server.csr -out server_cert.pem -days 7305 -extfile config.cnf -extensions req_ext
openssl x509 -req -CA root_cert.pem -CAkey root_key.pem -in client.csr -out client_cert.pem -days 7305
+```
+
+Generate self-signed ECDSA root cert
+
+```
+openssl ecparam -name prime256v1 -genkey -noout -out temp.pem
+openssl pkcs8 -topk8 -in temp.pem -out root_key_ec.pem -nocrypt
+rm temp.pem
+openssl req -x509 -days 7305 -new -key root_key_ec.pem -nodes -out root_cert_ec.pem -config root_ec.cnf -extensions 'v3_req'
+```
+
+Generate a chain of ECDSA certs
+
+```
+openssl ecparam -name prime256v1 -genkey -noout -out temp.pem
+openssl pkcs8 -topk8 -in temp.pem -out int_key2_ec.pem -nocrypt
+rm temp.pem
+openssl req -key int_key2_ec.pem -new -out temp.csr -config int_cert2.cnf
+openssl x509 -req -days 7305 -in temp.csr -CA root_cert_ec.pem -CAkey root_key_ec.pem -CAcreateserial -out int_cert2_ec.pem -extfile int_cert2.cnf -extensions 'v3_req'
+
+
+openssl ecparam -name prime256v1 -genkey -noout -out temp.pem
+openssl pkcs8 -topk8 -in temp.pem -out int_key1_ec.pem -nocrypt
+rm temp.pem
+openssl req -key int_key1_ec.pem -new -out temp.csr -config int_cert1.cnf
+openssl x509 -req -days 7305 -in temp.csr -CA int_cert2_ec.pem -CAkey int_key2_ec.pem -CAcreateserial -out int_cert1_ec.pem -extfile int_cert1.cnf -extensions 'v3_req'
+
+
+openssl ecparam -name prime256v1 -genkey -noout -out temp.pem
+openssl pkcs8 -topk8 -in temp.pem -out leaf_key_ec.pem -nocrypt
+rm temp.pem
+openssl req -key leaf_key_ec.pem -new -out temp.csr -config leaf.cnf
+openssl x509 -req -days 7305 -in temp.csr -CA int_cert1_ec.pem -CAkey int_key1_ec.pem -CAcreateserial -out leaf_cert_ec.pem -extfile leaf.cnf -extensions 'v3_req'
+```
+
+```
+cat leaf_cert_ec.pem int_cert1_ec.pem int_cert2_ec.pem > cert_chain_ec.pem
```
\ No newline at end of file
diff --git a/s2a/src/test/resources/cert_chain_ec.pem b/s2a/src/test/resources/cert_chain_ec.pem
new file mode 100644
index 0000000000..0e097d39bf
--- /dev/null
+++ b/s2a/src/test/resources/cert_chain_ec.pem
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIB0jCCAXigAwIBAgIUBV1dftEhhEMTI83L6jpeJn2tuyQwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIzMDQwMFoXDTQ0MDkxOTIzMDQwMFowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0TPKM3K/
+D0zlbGTVofq05ierfqzg7oNy8FqVEKvYewHXYxpS660za3Hjsju54fWhJOaZSobP
+4ezzvPr5BBbBMaOBgzCBgDAOBgNVHQ8BAf8EBAMCB4AwIAYDVR0lAQH/BBYwFAYI
+KwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPvP7dnB
+dg8ZoLB0w62tbvoIRRHPMB8GA1UdIwQYMBaAFHeH+MNh2fgyjNHYP9hLAv9Sl1yD
+MAoGCCqGSM49BAMCA0gAMEUCIBcsImaxeFjxFXCXYNQJnde+rsEOgbeAHrAC0SZQ
+NlB2AiEA4epDhw/o+6BfgDbqlZsNEHkScPrwupnBQGLQlmNJe2c=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB1zCCAX6gAwIBAgIUW4GYHncSLeb7Tmw7FMjX/DesReIwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIzMDA0MVoXDTQ0MDkxOTIzMDA0MVowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAqtg2E+h
+Wfr5dnewqsCLwM0PohkB83Gh7V3i/TPFkNKF/V6pKdz5a3Z8sicG+g7uJX+eyOoD
+43Z8woO7MgJl8aOBiTCBhjAOBgNVHQ8BAf8EBAMCAQYwIAYDVR0lAQH/BBYwFAYI
+KwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
+FHeH+MNh2fgyjNHYP9hLAv9Sl1yDMB8GA1UdIwQYMBaAFP+PTOryxis9d7HVfqhP
+MyMEgMZOMAoGCCqGSM49BAMCA0cAMEQCIHbzJvxHMIDPBRi1e047K0mqKKBSfViS
+guiDSoQ5g5OuAiBT5ePqDLs4PyrK6XFkiEWoRX8Z5T9y419Go+fpLM+DaA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB1zCCAX6gAwIBAgIUBBKkTrqFxQUist2pK2uj8/DRnKMwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIyNTYwNloXDTQ0MDkxOTIyNTYwNlowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFW7/Te2z
+jS8KlpF8RMMYaZtKf6EZlrZIIo5SO9j6baAKXVna9LmDCrzXnOLIeqOuZq0ODizU
+i4DFALB2yd5BkaOBiTCBhjAOBgNVHQ8BAf8EBAMCAQYwIAYDVR0lAQH/BBYwFAYI
+KwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYE
+FP+PTOryxis9d7HVfqhPMyMEgMZOMB8GA1UdIwQYMBaAFFITbB0BULPtynN9SMki
+lEarWxcKMAoGCCqGSM49BAMCA0cAMEQCIHK4cTTx4Ti7Te9hA9VVtHoMCt5fL4Cl
+XnQR6D5xW4pPAiAQ+CilQdZUhVK5bU6wbrwLgcwf+40ETK/KASId5970rQ==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/int_cert1_.cnf b/s2a/src/test/resources/int_cert1_.cnf
new file mode 100644
index 0000000000..8eaf6570da
--- /dev/null
+++ b/s2a/src/test/resources/int_cert1_.cnf
@@ -0,0 +1,14 @@
+[req]
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+prompt = no
+
+[req_distinguished_name]
+O = o
+OU = ou
+CN = cn
+
+[v3_req]
+keyUsage = critical, keyCertSign, cRLSign
+extendedKeyUsage = critical, clientAuth, serverAuth
+basicConstraints = critical, CA:true, pathlen: 1
\ No newline at end of file
diff --git a/s2a/src/test/resources/int_cert1_ec.pem b/s2a/src/test/resources/int_cert1_ec.pem
new file mode 100644
index 0000000000..980de5aa90
--- /dev/null
+++ b/s2a/src/test/resources/int_cert1_ec.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB1zCCAX6gAwIBAgIUW4GYHncSLeb7Tmw7FMjX/DesReIwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIzMDA0MVoXDTQ0MDkxOTIzMDA0MVowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAqtg2E+h
+Wfr5dnewqsCLwM0PohkB83Gh7V3i/TPFkNKF/V6pKdz5a3Z8sicG+g7uJX+eyOoD
+43Z8woO7MgJl8aOBiTCBhjAOBgNVHQ8BAf8EBAMCAQYwIAYDVR0lAQH/BBYwFAYI
+KwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
+FHeH+MNh2fgyjNHYP9hLAv9Sl1yDMB8GA1UdIwQYMBaAFP+PTOryxis9d7HVfqhP
+MyMEgMZOMAoGCCqGSM49BAMCA0cAMEQCIHbzJvxHMIDPBRi1e047K0mqKKBSfViS
+guiDSoQ5g5OuAiBT5ePqDLs4PyrK6XFkiEWoRX8Z5T9y419Go+fpLM+DaA==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/int_cert2.cnf b/s2a/src/test/resources/int_cert2.cnf
new file mode 100644
index 0000000000..c6a2559ce1
--- /dev/null
+++ b/s2a/src/test/resources/int_cert2.cnf
@@ -0,0 +1,14 @@
+[req]
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+prompt = no
+
+[req_distinguished_name]
+O = o
+OU = ou
+CN = cn
+
+[v3_req]
+keyUsage = critical, keyCertSign, cRLSign
+extendedKeyUsage = critical, clientAuth, serverAuth
+basicConstraints = critical, CA:true, pathlen: 2
\ No newline at end of file
diff --git a/s2a/src/test/resources/int_cert2_ec.pem b/s2a/src/test/resources/int_cert2_ec.pem
new file mode 100644
index 0000000000..574fa0195d
--- /dev/null
+++ b/s2a/src/test/resources/int_cert2_ec.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB1zCCAX6gAwIBAgIUBBKkTrqFxQUist2pK2uj8/DRnKMwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIyNTYwNloXDTQ0MDkxOTIyNTYwNlowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFW7/Te2z
+jS8KlpF8RMMYaZtKf6EZlrZIIo5SO9j6baAKXVna9LmDCrzXnOLIeqOuZq0ODizU
+i4DFALB2yd5BkaOBiTCBhjAOBgNVHQ8BAf8EBAMCAQYwIAYDVR0lAQH/BBYwFAYI
+KwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYE
+FP+PTOryxis9d7HVfqhPMyMEgMZOMB8GA1UdIwQYMBaAFFITbB0BULPtynN9SMki
+lEarWxcKMAoGCCqGSM49BAMCA0cAMEQCIHK4cTTx4Ti7Te9hA9VVtHoMCt5fL4Cl
+XnQR6D5xW4pPAiAQ+CilQdZUhVK5bU6wbrwLgcwf+40ETK/KASId5970rQ==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/int_key1_ec.pem b/s2a/src/test/resources/int_key1_ec.pem
new file mode 100644
index 0000000000..7ff3864746
--- /dev/null
+++ b/s2a/src/test/resources/int_key1_ec.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgLIQUM1HkFM/LWND8
+jCZ4wHXjFZ1ZZmQolahkZB0O1VChRANCAAQCq2DYT6FZ+vl2d7CqwIvAzQ+iGQHz
+caHtXeL9M8WQ0oX9Xqkp3PlrdnyyJwb6Du4lf57I6gPjdnzCg7syAmXx
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/int_key2_ec.pem b/s2a/src/test/resources/int_key2_ec.pem
new file mode 100644
index 0000000000..7f529ae855
--- /dev/null
+++ b/s2a/src/test/resources/int_key2_ec.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGfm6kyaAMMrmYGhS
+jxprBwtcZdP6qXlU1cVIO5bOT8qhRANCAAQVbv9N7bONLwqWkXxEwxhpm0p/oRmW
+tkgijlI72PptoApdWdr0uYMKvNec4sh6o65mrQ4OLNSLgMUAsHbJ3kGR
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/leaf.cnf b/s2a/src/test/resources/leaf.cnf
new file mode 100644
index 0000000000..d5b373cbc7
--- /dev/null
+++ b/s2a/src/test/resources/leaf.cnf
@@ -0,0 +1,14 @@
+[req]
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+prompt = no
+
+[req_distinguished_name]
+O = o
+OU = ou
+CN = cn
+
+[v3_req]
+keyUsage = critical, digitalSignature
+extendedKeyUsage = critical, clientAuth, serverAuth
+basicConstraints = critical, CA:false
\ No newline at end of file
diff --git a/s2a/src/test/resources/leaf_cert_ec.pem b/s2a/src/test/resources/leaf_cert_ec.pem
new file mode 100644
index 0000000000..39692b95fd
--- /dev/null
+++ b/s2a/src/test/resources/leaf_cert_ec.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB0jCCAXigAwIBAgIUBV1dftEhhEMTI83L6jpeJn2tuyQwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIzMDQwMFoXDTQ0MDkxOTIzMDQwMFowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0TPKM3K/
+D0zlbGTVofq05ierfqzg7oNy8FqVEKvYewHXYxpS660za3Hjsju54fWhJOaZSobP
+4ezzvPr5BBbBMaOBgzCBgDAOBgNVHQ8BAf8EBAMCB4AwIAYDVR0lAQH/BBYwFAYI
+KwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPvP7dnB
+dg8ZoLB0w62tbvoIRRHPMB8GA1UdIwQYMBaAFHeH+MNh2fgyjNHYP9hLAv9Sl1yD
+MAoGCCqGSM49BAMCA0gAMEUCIBcsImaxeFjxFXCXYNQJnde+rsEOgbeAHrAC0SZQ
+NlB2AiEA4epDhw/o+6BfgDbqlZsNEHkScPrwupnBQGLQlmNJe2c=
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/leaf_key_ec.pem b/s2a/src/test/resources/leaf_key_ec.pem
new file mode 100644
index 0000000000..d90ad8f4db
--- /dev/null
+++ b/s2a/src/test/resources/leaf_key_ec.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgR2HBqtWTWu4NLiow
+ar8vh+9vAmCONE59C+jXNAb9r8ehRANCAATRM8ozcr8PTOVsZNWh+rTmJ6t+rODu
+g3LwWpUQq9h7AddjGlLrrTNrceOyO7nh9aEk5plKhs/h7PO8+vkEFsEx
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/root_cert_ec.pem b/s2a/src/test/resources/root_cert_ec.pem
new file mode 100644
index 0000000000..0dd465e8e9
--- /dev/null
+++ b/s2a/src/test/resources/root_cert_ec.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBrzCCAVWgAwIBAgIUGV+9j5V61CZaa6mbrchDag5miEQwCgYIKoZIzj0EAwIw
+JjEKMAgGA1UECgwBbzELMAkGA1UECwwCb3UxCzAJBgNVBAMMAmNuMB4XDTI0MDkx
+OTIyNDMwOFoXDTQ0MDkxOTIyNDMwOFowJjEKMAgGA1UECgwBbzELMAkGA1UECwwC
+b3UxCzAJBgNVBAMMAmNuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDVPIq1ds
+/MX52CX9YU1RdEeM89YP4o3BN8OiP2O4qcuc11k4Qu4Mo4RWeN9OJpNElTQJ0K8n
+/rIvbmw8AIMquaNhMF8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRSE2wdAVCz
+7cpzfUjJIpRGq1sXCjAKBggqhkjOPQQDAgNIADBFAiEA1TEfHWArDnepmtMDQ4wd
+Q3uqPrV2Ye2KMO67/BHEGIQCIFu3JutXYYVU/CinwH89AJW+FJ7zokaPjCDkbiOH
++h+H
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/s2a/src/test/resources/root_ec.cnf b/s2a/src/test/resources/root_ec.cnf
new file mode 100644
index 0000000000..bee0b80a16
--- /dev/null
+++ b/s2a/src/test/resources/root_ec.cnf
@@ -0,0 +1,14 @@
+[req]
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+prompt = no
+
+[req_distinguished_name]
+O = o
+OU = ou
+CN = cn
+
+[v3_req]
+keyUsage = critical, keyCertSign, cRLSign
+extendedKeyUsage = serverAuth, clientAuth
+basicConstraints = critical, CA:true
\ No newline at end of file
diff --git a/s2a/src/test/resources/root_key_ec.pem b/s2a/src/test/resources/root_key_ec.pem
new file mode 100644
index 0000000000..ef5ee1445f
--- /dev/null
+++ b/s2a/src/test/resources/root_key_ec.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd5oZmQBOtMF0xfc3
+uRuw5EDhA1thJKKeHfrij9FMkfahRANCAAQNU8irV2z8xfnYJf1hTVF0R4zz1g/i
+jcE3w6I/Y7ipy5zXWThC7gyjhFZ4304mk0SVNAnQryf+si9ubDwAgyq5
+-----END PRIVATE KEY-----
\ No newline at end of file