• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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;
18 
19 import com.google.common.io.ByteStreams;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.List;
29 import java.util.Set;
30 import javax.net.ssl.KeyManager;
31 import javax.net.ssl.TrustManager;
32 
33 /**
34  * TLS credentials, providing server authentication and encryption. Consumers of this credential
35  * must verify they understand the configuration via the {@link #incomprehensible
36  * incomprehensible()} method. Unless overridden by a {@code Feature}, server verification should
37  * use customary default root certificates.
38  */
39 public final class TlsChannelCredentials extends ChannelCredentials {
40   /** Use TLS with its defaults. */
create()41   public static ChannelCredentials create() {
42     return newBuilder().build();
43   }
44 
45   private final boolean fakeFeature;
46   private final byte[] certificateChain;
47   private final byte[] privateKey;
48   private final String privateKeyPassword;
49   private final List<KeyManager> keyManagers;
50   private final byte[] rootCertificates;
51   private final List<TrustManager> trustManagers;
52 
TlsChannelCredentials(Builder builder)53   TlsChannelCredentials(Builder builder) {
54     fakeFeature = builder.fakeFeature;
55     certificateChain = builder.certificateChain;
56     privateKey = builder.privateKey;
57     privateKeyPassword = builder.privateKeyPassword;
58     keyManagers = builder.keyManagers;
59     rootCertificates = builder.rootCertificates;
60     trustManagers = builder.trustManagers;
61   }
62 
63   /**
64    * The certificate chain for the client's identity, as a new byte array. Generally should be
65    * PEM-encoded. If {@code null}, some feature is providing key manager information via a different
66    * method or no client identity is available.
67    */
getCertificateChain()68   public byte[] getCertificateChain() {
69     if (certificateChain == null) {
70       return null;
71     }
72     return Arrays.copyOf(certificateChain, certificateChain.length);
73   }
74 
75   /**
76    * The private key for the client's identity, as a new byte array. Generally should be in PKCS#8
77    * format. If encrypted, {@link #getPrivateKeyPassword} is the decryption key. If unencrypted, the
78    * password will be {@code null}. If {@code null}, some feature is providing key manager
79    * information via a different method or no client identity is available.
80    */
getPrivateKey()81   public byte[] getPrivateKey() {
82     if (privateKey == null) {
83       return null;
84     }
85     return Arrays.copyOf(privateKey, privateKey.length);
86   }
87 
88   /** Returns the password to decrypt the private key, or {@code null} if unencrypted. */
getPrivateKeyPassword()89   public String getPrivateKeyPassword() {
90     return privateKeyPassword;
91   }
92 
93   /**
94    * Returns the key manager list which provides the client's identity. Entries are scanned checking
95    * for specific types, like {@link javax.net.ssl.X509KeyManager}. Only a single entry for a type
96    * is used. Entries earlier in the list are higher priority. If {@code null}, key manager
97    * information is provided via a different method or no client identity is available.
98    */
getKeyManagers()99   public List<KeyManager> getKeyManagers() {
100     return keyManagers;
101   }
102 
103   /**
104    * Root trust certificates for verifying the server's identity that override the system's
105    * defaults. Generally PEM-encoded with multiple certificates concatenated.
106    */
getRootCertificates()107   public byte[] getRootCertificates() {
108     if (rootCertificates == null) {
109       return null;
110     }
111     return Arrays.copyOf(rootCertificates, rootCertificates.length);
112   }
113 
114   /**
115    * Returns the trust manager list which verifies the server's identity. Entries are scanned
116    * checking for specific types, like {@link javax.net.ssl.X509TrustManager}. Only a single entry
117    * for a type is used. Entries earlier in the list are higher priority. If {@code null}, trust
118    * manager information is provided via the system's default or a different method.
119    */
getTrustManagers()120   public List<TrustManager> getTrustManagers() {
121     return trustManagers;
122   }
123 
124   /**
125    * Returns an empty set if this credential can be adequately understood via
126    * the features listed, otherwise returns a hint of features that are lacking
127    * to understand the configuration to be used for manual debugging.
128    *
129    * <p>An "understood" feature does not imply the caller is able to fully
130    * handle the feature. It simply means the caller understands the feature
131    * enough to use the appropriate APIs to read the configuration. The caller
132    * may support just a subset of a feature, in which case the caller would
133    * need to look at the configuration to determine if only the supported
134    * subset is used.
135    *
136    * <p>This method may not be as simple as a set difference. There may be
137    * multiple features that can independently satisfy a piece of configuration.
138    * If the configuration is incomprehensible, all such features would be
139    * returned, even though only one may be necessary.
140    *
141    * <p>An empty set does not imply that the credentials are fully understood.
142    * There may be optional configuration that can be ignored if not understood.
143    *
144    * <p>Since {@code Feature} is an {@code enum}, {@code understoodFeatures}
145    * should generally be an {@link java.util.EnumSet}. {@code
146    * understoodFeatures} will not be modified.
147    *
148    * @param understoodFeatures the features understood by the caller
149    * @return empty set if the caller can adequately understand the configuration
150    */
incomprehensible(Set<Feature> understoodFeatures)151   public Set<Feature> incomprehensible(Set<Feature> understoodFeatures) {
152     Set<Feature> incomprehensible = EnumSet.noneOf(Feature.class);
153     if (fakeFeature) {
154       requiredFeature(understoodFeatures, incomprehensible, Feature.FAKE);
155     }
156     if (rootCertificates != null || privateKey != null || keyManagers != null) {
157       requiredFeature(understoodFeatures, incomprehensible, Feature.MTLS);
158     }
159     if (keyManagers != null || trustManagers != null) {
160       requiredFeature(understoodFeatures, incomprehensible, Feature.CUSTOM_MANAGERS);
161     }
162     return Collections.unmodifiableSet(incomprehensible);
163   }
164 
requiredFeature( Set<Feature> understoodFeatures, Set<Feature> incomprehensible, Feature feature)165   private static void requiredFeature(
166       Set<Feature> understoodFeatures, Set<Feature> incomprehensible, Feature feature) {
167     if (!understoodFeatures.contains(feature)) {
168       incomprehensible.add(feature);
169     }
170   }
171 
172   @Override
withoutBearerTokens()173   public ChannelCredentials withoutBearerTokens() {
174     return this;
175   }
176 
177   /**
178    * Features to understand TLS configuration. Additional enum values may be added in the future.
179    */
180   public enum Feature {
181     /**
182      * A feature that no consumer should understand. It should be used for unit testing to confirm
183      * a call to {@link #incomprehensible incomprehensible()} is implemented properly.
184      */
185     FAKE,
186     /**
187      * Client identity may be provided and server verification can be tuned. This feature requires
188      * observing {@link #getCertificateChain}, {@link #getPrivateKey}, and {@link
189      * #getPrivateKeyPassword} as well as {@link #getRootCertificates()}. The certificate chain and
190      * private key are used to configure a key manager to provide the client's identity. If no
191      * certificate chain and private key are provided the client will have no identity. The root
192      * certificates are used to configure a trust manager for verifying the server's identity. If no
193      * root certificates are provided the trust manager will default to the system's root
194      * certificates.
195      */
196     MTLS,
197     /**
198      * Key managers and trust managers may be specified as {@link KeyManager} and {@link
199      * TrustManager} objects. This feature requires observing {@link #getKeyManagers()} and {@link
200      * #getTrustManagers()}. Generally {@link #MTLS} should also be supported, as that is the more
201      * common method of configuration. When a manager is non-{@code null}, then it is wholly
202      * responsible for key or trust material and usage; there is no need to check other manager
203      * sources like {@link #getCertificateChain()} or {@link #getPrivateKey()} (if {@code
204      * KeyManager} is available), or {@link #getRootCertificates()} (if {@code TrustManager} is
205      * available).
206      *
207      * <p>If other manager sources are available (e.g., {@code getPrivateKey() != null}), then they
208      * may be alternative representations of the same configuration and the consumer is free to use
209      * those alternative representations if it prefers. But before doing so it <em>must</em> first
210      * check that it understands that alternative representation by using {@link #incomprehensible}
211      * <em>without</em> the {@code CUSTOM_MANAGERS} feature.
212      */
213     CUSTOM_MANAGERS,
214     ;
215   }
216 
217   /** Creates a builder for changing default configuration. */
newBuilder()218   public static Builder newBuilder() {
219     return new Builder();
220   }
221 
222   /** Builder for {@link TlsChannelCredentials}. */
223   public static final class Builder {
224     private boolean fakeFeature;
225     private byte[] certificateChain;
226     private byte[] privateKey;
227     private String privateKeyPassword;
228     private List<KeyManager> keyManagers;
229     private byte[] rootCertificates;
230     private List<TrustManager> trustManagers;
231 
Builder()232     private Builder() {}
233 
234     /**
235      * Requires {@link Feature#FAKE} to be understood. For use in testing consumers of this
236      * credential.
237      */
requireFakeFeature()238     public Builder requireFakeFeature() {
239       fakeFeature = true;
240       return this;
241     }
242 
243     /**
244      * Use the provided certificate chain and private key as the client's identity. Generally they
245      * should be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
246      * CERTIFICATE" and "BEGIN PRIVATE KEY").
247      */
keyManager(File certChain, File privateKey)248     public Builder keyManager(File certChain, File privateKey) throws IOException {
249       return keyManager(certChain, privateKey, null);
250     }
251 
252     /**
253      * Use the provided certificate chain and possibly-encrypted private key as the client's
254      * identity. Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private
255      * key is unencrypted, then password must be {@code null}.
256      */
keyManager(File certChain, File privateKey, String privateKeyPassword)257     public Builder keyManager(File certChain, File privateKey, String privateKeyPassword)
258         throws IOException {
259       InputStream certChainIs = new FileInputStream(certChain);
260       try {
261         InputStream privateKeyIs = new FileInputStream(privateKey);
262         try {
263           return keyManager(certChainIs, privateKeyIs, privateKeyPassword);
264         } finally {
265           privateKeyIs.close();
266         }
267       } finally {
268         certChainIs.close();
269       }
270     }
271 
272     /**
273      * Use the provided certificate chain and private key as the client's identity. Generally they
274      * should be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
275      * CERTIFICATE" and "BEGIN PRIVATE KEY").
276      */
keyManager(InputStream certChain, InputStream privateKey)277     public Builder keyManager(InputStream certChain, InputStream privateKey) throws IOException {
278       return keyManager(certChain, privateKey, null);
279     }
280 
281     /**
282      * Use the provided certificate chain and possibly-encrypted private key as the client's
283      * identity. Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private
284      * key is unencrypted, then password must be {@code null}.
285      */
keyManager( InputStream certChain, InputStream privateKey, String privateKeyPassword)286     public Builder keyManager(
287         InputStream certChain, InputStream privateKey, String privateKeyPassword)
288         throws IOException {
289       byte[] certChainBytes = ByteStreams.toByteArray(certChain);
290       byte[] privateKeyBytes = ByteStreams.toByteArray(privateKey);
291       clearKeyManagers();
292       this.certificateChain = certChainBytes;
293       this.privateKey = privateKeyBytes;
294       this.privateKeyPassword = privateKeyPassword;
295       return this;
296     }
297 
298     /**
299      * Have the provided key manager select the client's identity. Although multiple are allowed,
300      * only the first instance implementing a particular interface is used. So generally there will
301      * just be a single entry and it implements {@link javax.net.ssl.X509KeyManager}.
302      */
keyManager(KeyManager... keyManagers)303     public Builder keyManager(KeyManager... keyManagers) {
304       List<KeyManager> keyManagerList = Collections.unmodifiableList(new ArrayList<>(
305           Arrays.asList(keyManagers)));
306       clearKeyManagers();
307       this.keyManagers = keyManagerList;
308       return this;
309     }
310 
clearKeyManagers()311     private void clearKeyManagers() {
312       this.certificateChain = null;
313       this.privateKey = null;
314       this.privateKeyPassword = null;
315       this.keyManagers = null;
316     }
317 
318     /**
319      * Use the provided root certificates to verify the server's identity instead of the system's
320      * default. Generally they should be PEM-encoded with all the certificates concatenated together
321      * (file header has "BEGIN CERTIFICATE", and would occur once per certificate).
322      */
trustManager(File rootCerts)323     public Builder trustManager(File rootCerts) throws IOException {
324       InputStream rootCertsIs = new FileInputStream(rootCerts);
325       try {
326         return trustManager(rootCertsIs);
327       } finally {
328         rootCertsIs.close();
329       }
330     }
331 
332     /**
333      * Use the provided root certificates to verify the server's identity instead of the system's
334      * default. Generally they should be PEM-encoded with all the certificates concatenated together
335      * (file header has "BEGIN CERTIFICATE", and would occur once per certificate).
336      */
trustManager(InputStream rootCerts)337     public Builder trustManager(InputStream rootCerts) throws IOException {
338       byte[] rootCertsBytes = ByteStreams.toByteArray(rootCerts);
339       clearTrustManagers();
340       this.rootCertificates = rootCertsBytes;
341       return this;
342     }
343 
344     /**
345      * Have the provided trust manager verify the server's identity instead of the system's default.
346      * Although multiple are allowed, only the first instance implementing a particular interface is
347      * used. So generally there will just be a single entry and it implements {@link
348      * javax.net.ssl.X509TrustManager}.
349      */
trustManager(TrustManager... trustManagers)350     public Builder trustManager(TrustManager... trustManagers) {
351       List<TrustManager> trustManagerList = Collections.unmodifiableList(new ArrayList<>(
352           Arrays.asList(trustManagers)));
353       clearTrustManagers();
354       this.trustManagers = trustManagerList;
355       return this;
356     }
357 
clearTrustManagers()358     private void clearTrustManagers() {
359       this.rootCertificates = null;
360       this.trustManagers = null;
361     }
362 
363     /** Construct the credentials. */
build()364     public ChannelCredentials build() {
365       return new TlsChannelCredentials(this);
366     }
367   }
368 }
369