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