• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19544

08 Nov 2024 05:03AM UTC coverage: 84.607% (+0.04%) from 84.566%
#19544

push

github

web-flow
xds: Spiffe Trust Bundle Support (#11627)

Adds verification of SPIFFE based identities using SPIFFE trust bundles.

For in-progress gRFC A87.

34100 of 40304 relevant lines covered (84.61%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.39
/../xds/src/main/java/io/grpc/xds/internal/security/trust/XdsTrustManagerFactory.java
1
/*
2
 * Copyright 2019 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.xds.internal.security.trust;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static com.google.common.base.Preconditions.checkNotNull;
21
import static com.google.common.base.Preconditions.checkState;
22

23
import com.google.common.annotations.VisibleForTesting;
24
import com.google.common.base.Strings;
25
import io.envoyproxy.envoy.config.core.v3.DataSource.SpecifierCase;
26
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
27
import io.netty.handler.ssl.util.SimpleTrustManagerFactory;
28
import java.io.File;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.security.KeyStore;
32
import java.security.KeyStoreException;
33
import java.security.NoSuchAlgorithmException;
34
import java.security.cert.CertStoreException;
35
import java.security.cert.CertificateException;
36
import java.security.cert.X509Certificate;
37
import java.util.HashMap;
38
import java.util.List;
39
import java.util.Map;
40
import java.util.logging.Level;
41
import java.util.logging.Logger;
42
import javax.net.ssl.ManagerFactoryParameters;
43
import javax.net.ssl.TrustManager;
44
import javax.net.ssl.TrustManagerFactory;
45
import javax.net.ssl.X509ExtendedTrustManager;
46

47
/**
48
 * Factory class used to provide a {@link XdsX509TrustManager} for trust and SAN checks.
49
 */
50
public final class XdsTrustManagerFactory extends SimpleTrustManagerFactory {
51

52
  private static final Logger logger = Logger.getLogger(XdsTrustManagerFactory.class.getName());
1✔
53
  private XdsX509TrustManager xdsX509TrustManager;
54

55
  /** Constructor constructs from a {@link CertificateValidationContext}. */
56
  public XdsTrustManagerFactory(CertificateValidationContext certificateValidationContext)
57
      throws CertificateException, IOException, CertStoreException {
58
    this(
1✔
59
        getTrustedCaFromCertContext(certificateValidationContext),
1✔
60
        certificateValidationContext,
61
        false);
62
  }
1✔
63

64
  public XdsTrustManagerFactory(
65
          X509Certificate[] certs, CertificateValidationContext staticCertificateValidationContext)
66
          throws CertStoreException {
67
    this(certs, staticCertificateValidationContext, true);
1✔
68
  }
1✔
69

70
  public XdsTrustManagerFactory(Map<String, List<X509Certificate>> spiffeTrustMap,
71
      CertificateValidationContext staticCertificateValidationContext) throws CertStoreException {
72
    this(spiffeTrustMap, staticCertificateValidationContext, true);
1✔
73
  }
1✔
74

75
  private XdsTrustManagerFactory(
76
      X509Certificate[] certs,
77
      CertificateValidationContext certificateValidationContext,
78
      boolean validationContextIsStatic)
79
      throws CertStoreException {
1✔
80
    if (validationContextIsStatic) {
1✔
81
      checkArgument(
1✔
82
          certificateValidationContext == null || !certificateValidationContext.hasTrustedCa(),
1✔
83
          "only static certificateValidationContext expected");
84
    }
85
    xdsX509TrustManager = createX509TrustManager(certs, certificateValidationContext);
1✔
86
  }
1✔
87

88
  private XdsTrustManagerFactory(
89
      Map<String, List<X509Certificate>> spiffeTrustMap,
90
      CertificateValidationContext certificateValidationContext,
91
      boolean validationContextIsStatic)
92
      throws CertStoreException {
1✔
93
    if (validationContextIsStatic) {
1✔
94
      checkArgument(
1✔
95
          certificateValidationContext == null || !certificateValidationContext.hasTrustedCa(),
1✔
96
          "only static certificateValidationContext expected");
97
      xdsX509TrustManager = createX509TrustManager(spiffeTrustMap, certificateValidationContext);
1✔
98
    }
99
  }
1✔
100

101
  private static X509Certificate[] getTrustedCaFromCertContext(
102
      CertificateValidationContext certificateValidationContext)
103
      throws CertificateException, IOException {
104
    final SpecifierCase specifierCase =
1✔
105
        certificateValidationContext.getTrustedCa().getSpecifierCase();
1✔
106
    if (specifierCase == SpecifierCase.FILENAME) {
1✔
107
      String certsFile = certificateValidationContext.getTrustedCa().getFilename();
1✔
108
      checkState(
1✔
109
          !Strings.isNullOrEmpty(certsFile),
1✔
110
          "trustedCa.file-name in certificateValidationContext cannot be empty");
111
      return CertificateUtils.toX509Certificates(new File(certsFile));
1✔
112
    } else if (specifierCase == SpecifierCase.INLINE_BYTES) {
1✔
113
      try (InputStream is =
1✔
114
          certificateValidationContext.getTrustedCa().getInlineBytes().newInput()) {
1✔
115
        return CertificateUtils.toX509Certificates(is);
1✔
116
      }
117
    } else {
118
      throw new IllegalArgumentException("Not supported: " + specifierCase);
×
119
    }
120
  }
121

122
  @VisibleForTesting
123
  static XdsX509TrustManager createX509TrustManager(
124
      X509Certificate[] certs, CertificateValidationContext certContext) throws CertStoreException {
125
    return new XdsX509TrustManager(certContext, createTrustManager(certs));
1✔
126
  }
127

128
  @VisibleForTesting
129
  static XdsX509TrustManager createX509TrustManager(
130
      Map<String, List<X509Certificate>> spiffeTrustMapFile,
131
      CertificateValidationContext certContext) throws CertStoreException {
132
    checkNotNull(spiffeTrustMapFile, "spiffeTrustMapFile");
1✔
133
    Map<String, X509ExtendedTrustManager> delegates = new HashMap<>();
1✔
134
    for (Map.Entry<String, List<X509Certificate>> entry:spiffeTrustMapFile.entrySet()) {
1✔
135
      delegates.put(entry.getKey(), createTrustManager(
1✔
136
          entry.getValue().toArray(new X509Certificate[0])));
1✔
137
    }
1✔
138
    return new XdsX509TrustManager(certContext, delegates);
1✔
139
  }
140

141
  private static X509ExtendedTrustManager createTrustManager(X509Certificate[] certs)
142
      throws CertStoreException {
143
    TrustManagerFactory tmf = null;
1✔
144
    try {
145
      tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
1✔
146
      KeyStore ks = KeyStore.getInstance("PKCS12");
1✔
147
      // perform a load to initialize KeyStore
148
      ks.load(/* stream= */ null, /* password= */ null);
1✔
149
      int i = 1;
1✔
150
      for (X509Certificate cert : certs) {
1✔
151
        // note: alias lookup uses toLowerCase(Locale.ENGLISH)
152
        // so our alias needs to be all lower-case and unique
153
        ks.setCertificateEntry("alias" + i, cert);
1✔
154
        i++;
1✔
155
      }
156
      tmf.init(ks);
1✔
157
    } catch (NoSuchAlgorithmException | KeyStoreException | IOException | CertificateException e) {
×
158
      logger.log(Level.SEVERE, "createX509TrustManager", e);
×
159
      throw new CertStoreException(e);
×
160
    }
1✔
161
    TrustManager[] tms = tmf.getTrustManagers();
1✔
162
    X509ExtendedTrustManager myDelegate = null;
1✔
163
    if (tms != null) {
1✔
164
      for (TrustManager tm : tms) {
1✔
165
        if (tm instanceof X509ExtendedTrustManager) {
1✔
166
          myDelegate = (X509ExtendedTrustManager) tm;
1✔
167
          break;
1✔
168
        }
169
      }
170
    }
171
    if (myDelegate == null) {
1✔
172
      throw new CertStoreException("Native X509 TrustManager not found.");
×
173
    }
174
    return myDelegate;
1✔
175
  }
176

177
  @Override
178
  protected void engineInit(KeyStore keyStore) throws Exception {
179
    throw new UnsupportedOperationException();
×
180
  }
181

182
  @Override
183
  protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception {
184
    throw new UnsupportedOperationException();
×
185
  }
186

187
  @Override
188
  protected TrustManager[] engineGetTrustManagers() {
189
    return new TrustManager[] {xdsX509TrustManager};
1✔
190
  }
191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc