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