Skip to content

Commit e95725d

Browse files
committed
Trust manager handling for system root certs.
1 parent d2b722a commit e95725d

File tree

2 files changed

+83
-8
lines changed

2 files changed

+83
-8
lines changed

xds/src/main/java/io/grpc/xds/internal/security/certprovider/CertProviderClientSslContextProvider.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,22 @@
2424
import io.grpc.xds.client.Bootstrapper.CertificateProviderInfo;
2525
import io.grpc.xds.internal.security.trust.XdsTrustManagerFactory;
2626
import io.netty.handler.ssl.SslContextBuilder;
27+
28+
import java.io.IOException;
29+
import java.security.KeyStore;
30+
import java.security.KeyStoreException;
31+
import java.security.NoSuchAlgorithmException;
2732
import java.security.cert.CertStoreException;
33+
import java.security.cert.Certificate;
34+
import java.security.cert.CertificateException;
2835
import java.security.cert.X509Certificate;
29-
import java.util.Map;
36+
import java.util.*;
37+
import java.util.stream.Collectors;
3038
import javax.annotation.Nullable;
3139
import javax.net.ssl.SSLException;
40+
import javax.net.ssl.TrustManager;
41+
import javax.net.ssl.TrustManagerFactory;
42+
import javax.net.ssl.X509TrustManager;
3243

3344
/** A client SslContext provider using CertificateProviderInstance to fetch secrets. */
3445
final class CertProviderClientSslContextProvider extends CertProviderSslContextProvider {
@@ -56,7 +67,7 @@ final class CertProviderClientSslContextProvider extends CertProviderSslContextP
5667
// Instantiate sslContext so that addCallback will immediately update the callback with
5768
// the SslContext.
5869
sslContext = getSslContextBuilder(staticCertificateValidationContext).build();
59-
} catch (SSLException | CertStoreException e) {
70+
} catch (CertStoreException | CertificateException | IOException e) {
6071
throw new RuntimeException(e);
6172
}
6273
}
@@ -65,7 +76,7 @@ final class CertProviderClientSslContextProvider extends CertProviderSslContextP
6576
@Override
6677
protected final SslContextBuilder getSslContextBuilder(
6778
CertificateValidationContext certificateValidationContextdationContext)
68-
throws CertStoreException {
79+
throws CertificateException, IOException, CertStoreException {
6980
SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
7081
if (rootCertInstance != null) {
7182
if (savedSpiffeTrustMap != null) {
@@ -79,10 +90,33 @@ protected final SslContextBuilder getSslContextBuilder(
7990
savedTrustedRoots.toArray(new X509Certificate[0]),
8091
certificateValidationContextdationContext));
8192
}
93+
} else {
94+
try {
95+
sslContextBuilder = sslContextBuilder.trustManager(
96+
new XdsTrustManagerFactory(
97+
getX509CertificatesFromSystemTrustStore(),
98+
certificateValidationContextdationContext));
99+
} catch (KeyStoreException | NoSuchAlgorithmException e) {
100+
throw new CertStoreException(e);
101+
}
82102
}
83103
if (isMtls()) {
84104
sslContextBuilder.keyManager(savedKey, savedCertChain);
85105
}
86106
return sslContextBuilder;
87107
}
108+
109+
private X509Certificate[] getX509CertificatesFromSystemTrustStore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
110+
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
111+
trustManagerFactory.init((KeyStore) null);
112+
113+
List<TrustManager> trustManagers = Arrays.asList(trustManagerFactory.getTrustManagers());
114+
List<X509Certificate> rootCerts = trustManagers.stream()
115+
.filter(X509TrustManager.class::isInstance)
116+
.map(X509TrustManager.class::cast)
117+
.map(trustManager -> Arrays.asList(trustManager.getAcceptedIssuers()))
118+
.flatMap(Collection::stream)
119+
.collect(Collectors.toList());
120+
return rootCerts.toArray(new X509Certificate[rootCerts.size()]);
121+
}
88122
}

xds/src/test/java/io/grpc/xds/XdsSecurityClientServerTest.java

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.google.common.util.concurrent.SettableFuture;
3838
import io.envoyproxy.envoy.config.core.v3.SocketAddress.Protocol;
3939
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
40+
import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
4041
import io.grpc.Attributes;
4142
import io.grpc.EquivalentAddressGroup;
4243
import io.grpc.Grpc;
@@ -117,6 +118,10 @@
117118
@RunWith(Parameterized.class)
118119
public class XdsSecurityClientServerTest {
119120

121+
// TODO: Change this is a specific domain after
122+
// https://github.com/grpc/grpc-java/issues/12326 is fixed
123+
private static final String SAN_TO_MATCH = "*.test.google.fr";
124+
120125
@Parameter
121126
public Boolean enableSpiffe;
122127
private Boolean originalEnableSpiffe;
@@ -217,7 +222,7 @@ public void tlsClientServer_useSystemRootCerts_useCombinedValidationContext() th
217222

218223
UpstreamTlsContext upstreamTlsContext =
219224
setBootstrapInfoAndBuildUpstreamTlsContextForUsingSystemRootCerts(CLIENT_KEY_FILE,
220-
CLIENT_PEM_FILE, true);
225+
CLIENT_PEM_FILE, true, SAN_TO_MATCH);
221226

222227
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
223228
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
@@ -244,7 +249,7 @@ public void tlsClientServer_useSystemRootCerts_validationContext() throws Except
244249

245250
UpstreamTlsContext upstreamTlsContext =
246251
setBootstrapInfoAndBuildUpstreamTlsContextForUsingSystemRootCerts(CLIENT_KEY_FILE,
247-
CLIENT_PEM_FILE, false);
252+
CLIENT_PEM_FILE, false, SAN_TO_MATCH);
248253

249254
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
250255
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
@@ -255,6 +260,39 @@ public void tlsClientServer_useSystemRootCerts_validationContext() throws Except
255260
}
256261
}
257262

263+
/**
264+
* Use system root ca cert for TLS channel - no mTLS.
265+
* Subj Alt Names to match are specified in the validaton context.
266+
*/
267+
@Test
268+
public void tlsClientServer_useSystemRootCerts_failureToMatchSubjAltNames() throws Exception {
269+
Path trustStoreFilePath = getCacertFilePathForTestCa();
270+
try {
271+
setTrustStoreSystemProperties(trustStoreFilePath.toAbsolutePath().toString());
272+
DownstreamTlsContext downstreamTlsContext =
273+
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
274+
null, false, false);
275+
buildServerWithTlsContext(downstreamTlsContext);
276+
277+
UpstreamTlsContext upstreamTlsContext =
278+
setBootstrapInfoAndBuildUpstreamTlsContextForUsingSystemRootCerts(CLIENT_KEY_FILE,
279+
CLIENT_PEM_FILE, true, "server1.test.google.in");
280+
281+
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
282+
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
283+
unaryRpc(/* requestMessage= */ "buddy", blockingStub);
284+
fail("Expected handshake failure exception");
285+
} catch (StatusRuntimeException e) {
286+
assertThat(e.getCause()).isInstanceOf(SSLHandshakeException.class);
287+
assertThat(e.getCause().getCause()).isInstanceOf(CertificateException.class);
288+
assertThat(e.getCause().getCause().getMessage()).isEqualTo(
289+
"Peer certificate SAN check failed");
290+
} finally {
291+
Files.deleteIfExists(trustStoreFilePath);
292+
clearTrustStoreSystemProperties();
293+
}
294+
}
295+
258296
/**
259297
* Use system root ca cert for TLS channel - mTLS.
260298
* Uses common_tls_context.combined_validation_context in upstream_tls_context.
@@ -266,12 +304,12 @@ public void tlsClientServer_useSystemRootCerts_requireClientAuth() throws Except
266304
setTrustStoreSystemProperties(trustStoreFilePath.toAbsolutePath().toString());
267305
DownstreamTlsContext downstreamTlsContext =
268306
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
269-
null, false, false);
307+
null, false, true);
270308
buildServerWithTlsContext(downstreamTlsContext);
271309

272310
UpstreamTlsContext upstreamTlsContext =
273311
setBootstrapInfoAndBuildUpstreamTlsContextForUsingSystemRootCerts(CLIENT_KEY_FILE,
274-
CLIENT_PEM_FILE, true);
312+
CLIENT_PEM_FILE, true, SAN_TO_MATCH);
275313

276314
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
277315
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
@@ -552,7 +590,7 @@ private UpstreamTlsContext setBootstrapInfoAndBuildUpstreamTlsContext(String cli
552590
private UpstreamTlsContext setBootstrapInfoAndBuildUpstreamTlsContextForUsingSystemRootCerts(
553591
String clientKeyFile,
554592
String clientPemFile,
555-
boolean useCombinedValidationContext) {
593+
boolean useCombinedValidationContext, String sanToMatch) {
556594
bootstrapInfoForClient = CommonBootstrapperTestUtils
557595
.buildBootstrapInfo("google_cloud_private_spiffe-client", clientKeyFile, clientPemFile,
558596
CA_PEM_FILE, null, null, null, null, null);
@@ -563,6 +601,9 @@ private UpstreamTlsContext setBootstrapInfoAndBuildUpstreamTlsContextForUsingSys
563601
CertificateValidationContext.newBuilder()
564602
.setSystemRootCerts(
565603
CertificateValidationContext.SystemRootCerts.newBuilder().build())
604+
.addMatchSubjectAltNames(
605+
StringMatcher.newBuilder()
606+
.setExact(sanToMatch))
566607
.build());
567608
}
568609
return CommonTlsContextTestsUtil.buildNewUpstreamTlsContextForCertProviderInstance(

0 commit comments

Comments
 (0)