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