api: Add mTLS and Trust/KeyManager Credentials API
diff --git a/api/src/main/java/io/grpc/TlsChannelCredentials.java b/api/src/main/java/io/grpc/TlsChannelCredentials.java
index b529bf5..2d3751f 100644
--- a/api/src/main/java/io/grpc/TlsChannelCredentials.java
+++ b/api/src/main/java/io/grpc/TlsChannelCredentials.java
@@ -16,9 +16,19 @@
 
 package io.grpc;
 
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.TrustManager;
 
 /**
  * TLS credentials, providing server authentication and encryption. Consumers of this credential
@@ -34,9 +44,82 @@
   }
 
   private final boolean fakeFeature;
+  private final byte[] certificateChain;
+  private final byte[] privateKey;
+  private final String privateKeyPassword;
+  private final List<KeyManager> keyManagers;
+  private final byte[] rootCertificates;
+  private final List<TrustManager> trustManagers;
 
   TlsChannelCredentials(Builder builder) {
     fakeFeature = builder.fakeFeature;
+    certificateChain = builder.certificateChain;
+    privateKey = builder.privateKey;
+    privateKeyPassword = builder.privateKeyPassword;
+    keyManagers = builder.keyManagers;
+    rootCertificates = builder.rootCertificates;
+    trustManagers = builder.trustManagers;
+  }
+
+  /**
+   * The certificate chain for the client's identity, as a new byte array. Generally should be
+   * PEM-encoded. If {@code null}, some feature is providing key manager information via a different
+   * method or no client identity is available.
+   */
+  public byte[] getCertificateChain() {
+    if (certificateChain == null) {
+      return null;
+    }
+    return Arrays.copyOf(certificateChain, certificateChain.length);
+  }
+
+  /**
+   * The private key for the client's identity, as a new byte array. Generally should be in PKCS#8
+   * format. If encrypted, {@link #getPrivateKeyPassword} is the decryption key. If unencrypted, the
+   * password will be {@code null}. If {@code null}, some feature is providing key manager
+   * information via a different method or no client identity is available.
+   */
+  public byte[] getPrivateKey() {
+    if (privateKey == null) {
+      return null;
+    }
+    return Arrays.copyOf(privateKey, privateKey.length);
+  }
+
+  /** Returns the password to decrypt the private key, or {@code null} if unencrypted. */
+  public String getPrivateKeyPassword() {
+    return privateKeyPassword;
+  }
+
+  /**
+   * Returns the key manager list which provides the client's identity. Entries are scanned checking
+   * for specific types, like {@link javax.net.ssl.X509KeyManager}. Only a single entry for a type
+   * is used. Entries earlier in the list are higher priority. If {@code null}, key manager
+   * information is provided via a different method or no client identity is available.
+   */
+  public List<KeyManager> getKeyManagers() {
+    return keyManagers;
+  }
+
+  /**
+   * Root trust certificates for verifying the server's identity that override the system's
+   * defaults. Generally PEM-encoded with multiple certificates concatenated.
+   */
+  public byte[] getRootCertificates() {
+    if (rootCertificates == null) {
+      return null;
+    }
+    return Arrays.copyOf(rootCertificates, rootCertificates.length);
+  }
+
+  /**
+   * Returns the trust manager list which verifies the server's identity. Entries are scanned
+   * checking for specific types, like {@link javax.net.ssl.X509TrustManager}. Only a single entry
+   * for a type is used. Entries earlier in the list are higher priority. If {@code null}, trust
+   * manager information is provided via the system's default or a different method.
+   */
+  public List<TrustManager> getTrustManagers() {
+    return trustManagers;
   }
 
   /**
@@ -71,6 +154,12 @@
     if (fakeFeature) {
       requiredFeature(understoodFeatures, incomprehensible, Feature.FAKE);
     }
+    if (rootCertificates != null || privateKey != null || keyManagers != null) {
+      requiredFeature(understoodFeatures, incomprehensible, Feature.MTLS);
+    }
+    if (keyManagers != null || trustManagers != null) {
+      requiredFeature(understoodFeatures, incomprehensible, Feature.CUSTOM_MANAGERS);
+    }
     return Collections.unmodifiableSet(incomprehensible);
   }
 
@@ -95,6 +184,34 @@
      * a call to {@link #incomprehensible incomprehensible()} is implemented properly.
      */
     FAKE,
+    /**
+     * Client identity may be provided and server verification can be tuned. This feature requires
+     * observing {@link #getCertificateChain}, {@link #getPrivateKey}, and {@link
+     * #getPrivateKeyPassword} as well as {@link #getRootCertificates()}. The certificate chain and
+     * private key are used to configure a key manager to provide the client's identity. If no
+     * certificate chain and private key are provided the client will have no identity. The root
+     * certificates are used to configure a trust manager for verifying the server's identity. If no
+     * root certificates are provided the trust manager will default to the system's root
+     * certificates.
+     */
+    MTLS,
+    /**
+     * Key managers and trust managers may be specified as {@link KeyManager} and {@link
+     * TrustManager} objects. This feature requires observing {@link #getKeyManagers()} and {@link
+     * #getTrustManagers()}. Generally {@link #MTLS} should also be supported, as that is the more
+     * common method of configuration. When a manager is non-{@code null}, then it is wholly
+     * responsible for key or trust material and usage; there is no need to check other manager
+     * sources like {@link #getCertificateChain()} or {@link #getPrivateKey()} (if {@code
+     * KeyManager} is available), or {@link #getRootCertificates()} (if {@code TrustManager} is
+     * available).
+     *
+     * <p>If other manager sources are available (e.g., {@code getPrivateKey() != null}), then they
+     * may be alternative representations of the same configuration and the consumer is free to use
+     * those alternative representations if it prefers. But before doing so it <em>must</em> first
+     * check that it understands that alternative representation by using {@link #incomprehensible}
+     * <em>without</em> the {@code CUSTOM_MANAGERS} feature.
+     */
+    CUSTOM_MANAGERS,
     ;
   }
 
@@ -107,6 +224,12 @@
   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
   public static final class Builder {
     private boolean fakeFeature;
+    private byte[] certificateChain;
+    private byte[] privateKey;
+    private String privateKeyPassword;
+    private List<KeyManager> keyManagers;
+    private byte[] rootCertificates;
+    private List<TrustManager> trustManagers;
 
     private Builder() {}
 
@@ -119,6 +242,126 @@
       return this;
     }
 
+    /**
+     * Use the provided certificate chain and private key as the client's identity. Generally they
+     * should be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
+     * CERTIFICATE" and "BEGIN PRIVATE KEY").
+     */
+    public Builder keyManager(File certChain, File privateKey) throws IOException {
+      return keyManager(certChain, privateKey, null);
+    }
+
+    /**
+     * Use the provided certificate chain and possibly-encrypted private key as the client's
+     * identity. Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private
+     * key is unencrypted, then password must be {@code null}.
+     */
+    public Builder keyManager(File certChain, File privateKey, String privateKeyPassword)
+        throws IOException {
+      InputStream certChainIs = new FileInputStream(certChain);
+      try {
+        InputStream privateKeyIs = new FileInputStream(privateKey);
+        try {
+          return keyManager(certChainIs, privateKeyIs, privateKeyPassword);
+        } finally {
+          privateKeyIs.close();
+        }
+      } finally {
+        certChainIs.close();
+      }
+    }
+
+    /**
+     * Use the provided certificate chain and private key as the client's identity. Generally they
+     * should be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
+     * CERTIFICATE" and "BEGIN PRIVATE KEY").
+     */
+    public Builder keyManager(InputStream certChain, InputStream privateKey) throws IOException {
+      return keyManager(certChain, privateKey, null);
+    }
+
+    /**
+     * Use the provided certificate chain and possibly-encrypted private key as the client's
+     * identity. Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private
+     * key is unencrypted, then password must be {@code null}.
+     */
+    public Builder keyManager(
+        InputStream certChain, InputStream privateKey, String privateKeyPassword)
+        throws IOException {
+      byte[] certChainBytes = ByteStreams.toByteArray(certChain);
+      byte[] privateKeyBytes = ByteStreams.toByteArray(privateKey);
+      clearKeyManagers();
+      this.certificateChain = certChainBytes;
+      this.privateKey = privateKeyBytes;
+      this.privateKeyPassword = privateKeyPassword;
+      return this;
+    }
+
+    /**
+     * Have the provided key manager select the client's identity. Although multiple are allowed,
+     * only the first instance implementing a particular interface is used. So generally there will
+     * just be a single entry and it implements {@link javax.net.ssl.X509KeyManager}.
+     */
+    public Builder keyManager(KeyManager... keyManagers) {
+      List<KeyManager> keyManagerList = Collections.unmodifiableList(new ArrayList<>(
+          Arrays.asList(keyManagers)));
+      clearKeyManagers();
+      this.keyManagers = keyManagerList;
+      return this;
+    }
+
+    private void clearKeyManagers() {
+      this.certificateChain = null;
+      this.privateKey = null;
+      this.privateKeyPassword = null;
+      this.keyManagers = null;
+    }
+
+    /**
+     * Use the provided root certificates to verify the server's identity instead of the system's
+     * default. Generally they should be PEM-encoded with all the certificates concatenated together
+     * (file header has "BEGIN CERTIFICATE", and would occur once per certificate).
+     */
+    public Builder trustManager(File rootCerts) throws IOException {
+      InputStream rootCertsIs = new FileInputStream(rootCerts);
+      try {
+        return trustManager(rootCertsIs);
+      } finally {
+        rootCertsIs.close();
+      }
+    }
+
+    /**
+     * Use the provided root certificates to verify the server's identity instead of the system's
+     * default. Generally they should be PEM-encoded with all the certificates concatenated together
+     * (file header has "BEGIN CERTIFICATE", and would occur once per certificate).
+     */
+    public Builder trustManager(InputStream rootCerts) throws IOException {
+      byte[] rootCertsBytes = ByteStreams.toByteArray(rootCerts);
+      clearTrustManagers();
+      this.rootCertificates = rootCertsBytes;
+      return this;
+    }
+
+    /**
+     * Have the provided trust manager verify the server's identity instead of the system's default.
+     * Although multiple are allowed, only the first instance implementing a particular interface is
+     * used. So generally there will just be a single entry and it implements {@link
+     * javax.net.ssl.X509TrustManager}.
+     */
+    public Builder trustManager(TrustManager... trustManagers) {
+      List<TrustManager> trustManagerList = Collections.unmodifiableList(new ArrayList<>(
+          Arrays.asList(trustManagers)));
+      clearTrustManagers();
+      this.trustManagers = trustManagerList;
+      return this;
+    }
+
+    private void clearTrustManagers() {
+      this.rootCertificates = null;
+      this.trustManagers = null;
+    }
+
     /** Construct the credentials. */
     public ChannelCredentials build() {
       return new TlsChannelCredentials(this);
diff --git a/api/src/main/java/io/grpc/TlsServerCredentials.java b/api/src/main/java/io/grpc/TlsServerCredentials.java
index 7619716..8ed1482 100644
--- a/api/src/main/java/io/grpc/TlsServerCredentials.java
+++ b/api/src/main/java/io/grpc/TlsServerCredentials.java
@@ -16,15 +16,20 @@
 
 package io.grpc;
 
+import com.google.common.base.Preconditions;
 import com.google.common.io.ByteStreams;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.TrustManager;
 
 /**
  * TLS credentials, providing server identity and encryption. Consumers of this credential must
@@ -59,27 +64,44 @@
   private final byte[] certificateChain;
   private final byte[] privateKey;
   private final String privateKeyPassword;
+  private final List<KeyManager> keyManagers;
+  private final ClientAuth clientAuth;
+  private final byte[] rootCertificates;
+  private final List<TrustManager> trustManagers;
 
   TlsServerCredentials(Builder builder) {
     fakeFeature = builder.fakeFeature;
     certificateChain = builder.certificateChain;
     privateKey = builder.privateKey;
     privateKeyPassword = builder.privateKeyPassword;
+    keyManagers = builder.keyManagers;
+    clientAuth = builder.clientAuth;
+    rootCertificates = builder.rootCertificates;
+    trustManagers = builder.trustManagers;
   }
 
   /**
-   * The certificate chain, as a new byte array. Generally should be PEM-encoded.
+   * The certificate chain for the server's identity, as a new byte array. Generally should be
+   * PEM-encoded. If {@code null}, some feature is providing key manager information via a different
+   * method.
    */
   public byte[] getCertificateChain() {
+    if (certificateChain == null) {
+      return null;
+    }
     return Arrays.copyOf(certificateChain, certificateChain.length);
   }
 
   /**
-   * The private key, as a new byte array. Generally should be in PKCS#8 format. If encrypted,
-   * {@link #getPrivateKeyPassword} is the decryption key. If unencrypted, the password will be
-   * {@code null}.
+   * The private key for the server's identity, as a new byte array. Generally should be in PKCS#8
+   * format. If encrypted, {@link #getPrivateKeyPassword} is the decryption key. If unencrypted, the
+   * password will be {@code null}. If {@code null}, some feature is providing key manager
+   * information via a different method.
    */
   public byte[] getPrivateKey() {
+    if (privateKey == null) {
+      return null;
+    }
     return Arrays.copyOf(privateKey, privateKey.length);
   }
 
@@ -89,6 +111,42 @@
   }
 
   /**
+   * Returns the key manager list which provides the server's identity. Entries are scanned checking
+   * for specific types, like {@link javax.net.ssl.X509KeyManager}. Only a single entry for a type
+   * is used. Entries earlier in the list are higher priority. If {@code null}, key manager
+   * information is provided via a different method.
+   */
+  public List<KeyManager> getKeyManagers() {
+    return keyManagers;
+  }
+
+  /** Non-{@code null} setting indicating whether the server should expect a client's identity. */
+  public ClientAuth getClientAuth() {
+    return clientAuth;
+  }
+
+  /**
+   * Root trust certificates for verifying the client's identity that override the system's
+   * defaults. Generally PEM-encoded with multiple certificates concatenated.
+   */
+  public byte[] getRootCertificates() {
+    if (rootCertificates == null) {
+      return null;
+    }
+    return Arrays.copyOf(rootCertificates, rootCertificates.length);
+  }
+
+  /**
+   * Returns the trust manager list which verifies the client's identity. Entries are scanned
+   * checking for specific types, like {@link javax.net.ssl.X509TrustManager}. Only a single entry
+   * for a type is used. Entries earlier in the list are higher priority. If {@code null}, trust
+   * manager information is provided via the system's default or a different method.
+   */
+  public List<TrustManager> getTrustManagers() {
+    return trustManagers;
+  }
+
+  /**
    * Returns an empty set if this credential can be adequately understood via
    * the features listed, otherwise returns a hint of features that are lacking
    * to understand the configuration to be used for manual debugging.
@@ -120,6 +178,12 @@
     if (fakeFeature) {
       requiredFeature(understoodFeatures, incomprehensible, Feature.FAKE);
     }
+    if (clientAuth != ClientAuth.NONE) {
+      requiredFeature(understoodFeatures, incomprehensible, Feature.MTLS);
+    }
+    if (keyManagers != null || trustManagers != null) {
+      requiredFeature(understoodFeatures, incomprehensible, Feature.CUSTOM_MANAGERS);
+    }
     return Collections.unmodifiableSet(incomprehensible);
   }
 
@@ -139,10 +203,37 @@
      * a call to {@link #incomprehensible incomprehensible()} is implemented properly.
      */
     FAKE,
+    /**
+     * Client certificates may be requested and verified. This feature requires observing {@link
+     * #getRootCertificates()} and {@link #getClientAuth()}. The root certificates are used to
+     * configure a trust manager for verifying the client's identity. If no root certificates are
+     * provided the trust manager will default to the system's root certificates.
+     */
+    MTLS,
+    /**
+     * Key managers and trust managers may be specified as {@link KeyManager} and {@link
+     * TrustManager} objects. This feature by itself only implies {@link #getKeyManagers()} needs to
+     * be observed. But along with {@link #MTLS}, then {@link #getTrustManagers()} needs to be
+     * observed as well. When a manager is non-{@code null}, then it is wholly responsible for key
+     * or trust material and usage; there is no need to check other manager sources like {@link
+     * #getCertificateChain()} or {@link #getPrivateKey()} (if {@code KeyManager} is available), or
+     * {@link #getRootCertificates()} (if {@code TrustManager} is available).
+     *
+     * <p>If other manager sources are available (e.g., {@code getPrivateKey() != null}), then they
+     * may be alternative representations of the same configuration and the consumer is free to use
+     * those alternative representations if it prefers. But before doing so it <em>must</em> first
+     * check that it understands that alternative representation by using {@link #incomprehensible}
+     * <em>without</em> the {@code CUSTOM_MANAGERS} feature.
+     */
+    CUSTOM_MANAGERS,
     ;
   }
 
-  /** Creates a builder for changing default configuration. */
+  /**
+   * Creates a builder for changing default configuration. There is no default key manager, so key
+   * material must be specified. The default trust manager uses the system's root certificates. By
+   * default no client authentication will occur.
+   */
   public static Builder newBuilder() {
     return new Builder();
   }
@@ -154,6 +245,10 @@
     private byte[] certificateChain;
     private byte[] privateKey;
     private String privateKeyPassword;
+    private List<KeyManager> keyManagers;
+    private ClientAuth clientAuth = ClientAuth.NONE;
+    private byte[] rootCertificates;
+    private List<TrustManager> trustManagers;
 
     private Builder() {}
 
@@ -167,8 +262,8 @@
     }
 
     /**
-     * Creates an instance using provided certificate chain and private key. Generally they should
-     * be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
+     * Use the provided certificate chain and private key as the server's identity. Generally they
+     * should be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
      * CERTIFICATE" and "BEGIN PRIVATE KEY").
      */
     public Builder keyManager(File certChain, File privateKey) throws IOException {
@@ -176,9 +271,9 @@
     }
 
     /**
-     * Creates an instance using provided certificate chain and possibly-encrypted private key.
-     * Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private key is
-     * unencrypted, then password must be {@code null}.
+     * Use the provided certificate chain and possibly-encrypted private key as the server's
+     * identity. Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private
+     * key is unencrypted, then password must be {@code null}.
      */
     public Builder keyManager(File certChain, File privateKey, String privateKeyPassword)
         throws IOException {
@@ -196,8 +291,8 @@
     }
 
     /**
-     * Creates an instance using provided certificate chain and private key. Generally they should
-     * be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
+     * Use the provided certificate chain and private key as the server's identity. Generally they
+     * should be PEM-encoded and the key is an unencrypted PKCS#8 key (file headers have "BEGIN
      * CERTIFICATE" and "BEGIN PRIVATE KEY").
      */
     public Builder keyManager(InputStream certChain, InputStream privateKey) throws IOException {
@@ -205,27 +300,121 @@
     }
 
     /**
-     * Creates an instance using provided certificate chain and possibly-encrypted private key.
-     * Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private key is
-     * unencrypted, then password must be {@code null}.
+     * Use the provided certificate chain and possibly-encrypted private key as the server's
+     * identity. Generally they should be PEM-encoded and the key is a PKCS#8 key. If the private
+     * key is unencrypted, then password must be {@code null}.
      */
     public Builder keyManager(
         InputStream certChain, InputStream privateKey, String privateKeyPassword)
         throws IOException {
       byte[] certChainBytes = ByteStreams.toByteArray(certChain);
       byte[] privateKeyBytes = ByteStreams.toByteArray(privateKey);
+      clearKeyManagers();
       this.certificateChain = certChainBytes;
       this.privateKey = privateKeyBytes;
       this.privateKeyPassword = privateKeyPassword;
       return this;
     }
 
+    /**
+     * Have the provided key manager select the server's identity. Although multiple are allowed,
+     * only the first instance implementing a particular interface is used. So generally there will
+     * just be a single entry and it implements {@link javax.net.ssl.X509KeyManager}.
+     */
+    public Builder keyManager(KeyManager... keyManagers) {
+      List<KeyManager> keyManagerList = Collections.unmodifiableList(new ArrayList<>(
+          Arrays.asList(keyManagers)));
+      clearKeyManagers();
+      this.keyManagers = keyManagerList;
+      return this;
+    }
+
+    private void clearKeyManagers() {
+      this.certificateChain = null;
+      this.privateKey = null;
+      this.privateKeyPassword = null;
+      this.keyManagers = null;
+    }
+
+    /**
+     * Indicates whether the server should expect a client's identity. Must not be {@code null}.
+     * Defaults to {@link ClientAuth#NONE}.
+     */
+    public Builder clientAuth(ClientAuth clientAuth) {
+      Preconditions.checkNotNull(clientAuth, "clientAuth");
+      this.clientAuth = clientAuth;
+      return this;
+    }
+
+    /**
+     * Use the provided root certificates to verify the client's identity instead of the system's
+     * default. Generally they should be PEM-encoded with all the certificates concatenated together
+     * (file header has "BEGIN CERTIFICATE", and would occur once per certificate).
+     */
+    public Builder trustManager(File rootCerts) throws IOException {
+      InputStream rootCertsIs = new FileInputStream(rootCerts);
+      try {
+        return trustManager(rootCertsIs);
+      } finally {
+        rootCertsIs.close();
+      }
+    }
+
+    /**
+     * Use the provided root certificates to verify the client's identity instead of the system's
+     * default. Generally they should be PEM-encoded with all the certificates concatenated together
+     * (file header has "BEGIN CERTIFICATE", and would occur once per certificate).
+     */
+    public Builder trustManager(InputStream rootCerts) throws IOException {
+      byte[] rootCertsBytes = ByteStreams.toByteArray(rootCerts);
+      clearTrustManagers();
+      this.rootCertificates = rootCertsBytes;
+      return this;
+    }
+
+    /**
+     * Have the provided trust manager verify the client's identity instead of the system's default.
+     * Although multiple are allowed, only the first instance implementing a particular interface is
+     * used. So generally there will just be a single entry and it implements {@link
+     * javax.net.ssl.X509TrustManager}.
+     */
+    public Builder trustManager(TrustManager... trustManagers) {
+      List<TrustManager> trustManagerList = Collections.unmodifiableList(new ArrayList<>(
+          Arrays.asList(trustManagers)));
+      clearTrustManagers();
+      this.trustManagers = trustManagerList;
+      return this;
+    }
+
+    private void clearTrustManagers() {
+      this.rootCertificates = null;
+      this.trustManagers = null;
+    }
+
     /** Construct the credentials. */
     public ServerCredentials build() {
-      if (certificateChain == null) {
+      if (certificateChain == null && keyManagers == null) {
         throw new IllegalStateException("A key manager is required");
       }
       return new TlsServerCredentials(this);
     }
   }
+
+  /** The level of authentication the server should expect from the client. */
+  public enum ClientAuth {
+    /** Clients will not present any identity. */
+    NONE,
+
+    /**
+     * Clients are requested to present their identity, but clients without identities are
+     * permitted.
+     */
+    OPTIONAL,
+
+    /**
+     * Clients are requested to present their identity, and are required to provide a valid
+     * identity.
+     */
+    REQUIRE;
+  }
 }