From dba4c5fd0144b68916b4dc2bbbd02d12c2e12041 Mon Sep 17 00:00:00 2001
From: Deepak Lukose <deepaklukose@google.com>
Date: Fri, 25 Mar 2016 12:54:25 -0700
Subject: [PATCH] Add various options to verify ssl/tls client cert including
 letting the application handle the authentication.

---
 BUILD                                         |   2 +
 Makefile                                      |  37 +
 build.yaml                                    |   2 +
 gRPC.podspec                                  |   1 +
 grpc.def                                      |   1 +
 grpc.gemspec                                  |   1 +
 include/grpc++/security/server_credentials.h  |  15 +-
 include/grpc/grpc_security.h                  |  38 +-
 include/grpc/grpc_security_constants.h        | 114 +++
 package.xml                                   |   1 +
 src/core/lib/security/credentials.c           |  27 +-
 src/core/lib/security/security_connector.c    |  34 +-
 src/core/lib/security/security_connector.h    |   2 +-
 src/core/lib/tsi/ssl_transport_security.c     |  54 +-
 src/core/lib/tsi/ssl_transport_security.h     |  17 +
 .../lib/tsi/transport_security_interface.h    |   9 +
 src/cpp/server/secure_server_credentials.cc   |   8 +-
 src/csharp/ext/grpc_csharp_ext.c              |   9 +-
 src/node/ext/server_credentials.cc            |  13 +-
 src/php/ext/grpc/server_credentials.c         |   9 +-
 src/proto/grpc/binary_log/v1alpha/log.proto   |   2 +-
 .../grpc/_cython/_cygrpc/credentials.pyx.pxi  |   4 +-
 .../grpcio/grpc/_cython/_cygrpc/grpc.pxi      |   7 +
 .../grpcio/grpc/_cython/imports.generated.c   |   2 +
 .../grpcio/grpc/_cython/imports.generated.h   |   3 +
 src/ruby/ext/grpc/rb_grpc_imports.generated.c |   2 +
 src/ruby/ext/grpc/rb_grpc_imports.generated.h |   3 +
 src/ruby/ext/grpc/rb_server_credentials.c     |  24 +-
 test/core/end2end/data/client_certs.c         | 343 +++++++
 test/core/end2end/data/ssl_test_data.h        |   4 +
 test/core/end2end/fixtures/h2_ssl_cert.c      | 376 ++++++++
 test/core/end2end/gen_build_yaml.py           |   1 +
 .../core/surface/public_headers_must_be_c89.c |   1 +
 tools/doxygen/Doxyfile.core                   |   1 +
 tools/doxygen/Doxyfile.core.internal          |   1 +
 tools/run_tests/sources_and_headers.json      |  20 +
 tools/run_tests/tests.json                    | 836 ++++++++++++++++++
 vsprojects/buildtests_c.sln                   |  28 +
 vsprojects/vcxproj/grpc/grpc.vcxproj          |   1 +
 vsprojects/vcxproj/grpc/grpc.vcxproj.filters  |   3 +
 .../grpc_test_util/grpc_test_util.vcxproj     |   2 +
 .../grpc_test_util.vcxproj.filters            |   3 +
 .../h2_ssl_cert_test/h2_ssl_cert_test.vcxproj | 202 +++++
 .../h2_ssl_cert_test.vcxproj.filters          |  24 +
 44 files changed, 2221 insertions(+), 66 deletions(-)
 create mode 100644 include/grpc/grpc_security_constants.h
 create mode 100644 test/core/end2end/data/client_certs.c
 create mode 100644 test/core/end2end/fixtures/h2_ssl_cert.c
 create mode 100644 vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj
 create mode 100644 vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj.filters

diff --git a/BUILD b/BUILD
index b4751a7081..e42505d02f 100644
--- a/BUILD
+++ b/BUILD
@@ -481,6 +481,7 @@ cc_library(
     "include/grpc/impl/codegen/sync_win32.h",
     "include/grpc/impl/codegen/time.h",
     "include/grpc/grpc_security.h",
+    "include/grpc/grpc_security_constants.h",
     "include/grpc/census.h",
   ],
   includes = [
@@ -1492,6 +1493,7 @@ objc_library(
     "include/grpc/impl/codegen/sync_win32.h",
     "include/grpc/impl/codegen/time.h",
     "include/grpc/grpc_security.h",
+    "include/grpc/grpc_security_constants.h",
     "include/grpc/census.h",
     "src/core/lib/channel/channel_args.h",
     "src/core/lib/channel/channel_stack.h",
diff --git a/Makefile b/Makefile
index 50fc16753a..955731e646 100644
--- a/Makefile
+++ b/Makefile
@@ -1106,6 +1106,7 @@ h2_sockpair_test: $(BINDIR)/$(CONFIG)/h2_sockpair_test
 h2_sockpair+trace_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_test
 h2_sockpair_1byte_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_test
 h2_ssl_test: $(BINDIR)/$(CONFIG)/h2_ssl_test
+h2_ssl_cert_test: $(BINDIR)/$(CONFIG)/h2_ssl_cert_test
 h2_ssl_proxy_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_test
 h2_uds_test: $(BINDIR)/$(CONFIG)/h2_uds_test
 h2_census_nosec_test: $(BINDIR)/$(CONFIG)/h2_census_nosec_test
@@ -1333,6 +1334,7 @@ buildtests_c: privatelibs_c \
   $(BINDIR)/$(CONFIG)/h2_sockpair+trace_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_test \
   $(BINDIR)/$(CONFIG)/h2_ssl_test \
+  $(BINDIR)/$(CONFIG)/h2_ssl_cert_test \
   $(BINDIR)/$(CONFIG)/h2_ssl_proxy_test \
   $(BINDIR)/$(CONFIG)/h2_uds_test \
   $(BINDIR)/$(CONFIG)/h2_census_nosec_test \
@@ -2640,6 +2642,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/impl/codegen/sync_win32.h \
     include/grpc/impl/codegen/time.h \
     include/grpc/grpc_security.h \
+    include/grpc/grpc_security_constants.h \
     include/grpc/census.h \
 
 LIBGRPC_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_SRC))))
@@ -2695,6 +2698,7 @@ endif
 
 
 LIBGRPC_TEST_UTIL_SRC = \
+    test/core/end2end/data/client_certs.c \
     test/core/end2end/data/server1_cert.c \
     test/core/end2end/data/server1_key.c \
     test/core/end2end/data/test_root_cert.c \
@@ -13542,6 +13546,38 @@ endif
 endif
 
 
+H2_SSL_CERT_TEST_SRC = \
+    test/core/end2end/fixtures/h2_ssl_cert.c \
+
+H2_SSL_CERT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_SSL_CERT_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/h2_ssl_cert_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/h2_ssl_cert_test: $(H2_SSL_CERT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(H2_SSL_CERT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cert_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_ssl_cert.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_h2_ssl_cert_test: $(H2_SSL_CERT_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(H2_SSL_CERT_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 H2_SSL_PROXY_TEST_SRC = \
     test/core/end2end/fixtures/h2_ssl_proxy.c \
 
@@ -14101,6 +14137,7 @@ src/cpp/server/secure_server_credentials.cc: $(OPENSSL_DEP)
 src/csharp/ext/grpc_csharp_ext.c: $(OPENSSL_DEP)
 test/core/bad_client/bad_client.c: $(OPENSSL_DEP)
 test/core/bad_ssl/server_common.c: $(OPENSSL_DEP)
+test/core/end2end/data/client_certs.c: $(OPENSSL_DEP)
 test/core/end2end/data/server1_cert.c: $(OPENSSL_DEP)
 test/core/end2end/data/server1_key.c: $(OPENSSL_DEP)
 test/core/end2end/data/test_root_cert.c: $(OPENSSL_DEP)
diff --git a/build.yaml b/build.yaml
index a9a9e6ac9f..22c9ff5c06 100644
--- a/build.yaml
+++ b/build.yaml
@@ -525,6 +525,7 @@ filegroups:
 - name: grpc_secure
   public_headers:
   - include/grpc/grpc_security.h
+  - include/grpc/grpc_security_constants.h
   headers:
   - src/core/lib/security/auth_filters.h
   - src/core/lib/security/b64.h
@@ -755,6 +756,7 @@ libs:
   - test/core/end2end/data/ssl_test_data.h
   - test/core/security/oauth2_utils.h
   src:
+  - test/core/end2end/data/client_certs.c
   - test/core/end2end/data/server1_cert.c
   - test/core/end2end/data/server1_key.c
   - test/core/end2end/data/test_root_cert.c
diff --git a/gRPC.podspec b/gRPC.podspec
index 7ede97d1a9..859c2c9672 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -323,6 +323,7 @@ Pod::Spec.new do |s|
                       'include/grpc/impl/codegen/sync_win32.h',
                       'include/grpc/impl/codegen/time.h',
                       'include/grpc/grpc_security.h',
+                      'include/grpc/grpc_security_constants.h',
                       'include/grpc/census.h',
                       'src/core/lib/channel/channel_args.c',
                       'src/core/lib/channel/channel_stack.c',
diff --git a/grpc.def b/grpc.def
index f81aa1b05a..17d2ec47c9 100644
--- a/grpc.def
+++ b/grpc.def
@@ -114,6 +114,7 @@ EXPORTS
     grpc_secure_channel_create
     grpc_server_credentials_release
     grpc_ssl_server_credentials_create
+    grpc_ssl_server_credentials_create_ex
     grpc_server_add_secure_http2_port
     grpc_call_set_credentials
     grpc_server_credentials_set_auth_metadata_processor
diff --git a/grpc.gemspec b/grpc.gemspec
index 9c858b2579..bac1f186f2 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -171,6 +171,7 @@ Gem::Specification.new do |s|
   s.files += %w( include/grpc/impl/codegen/sync_win32.h )
   s.files += %w( include/grpc/impl/codegen/time.h )
   s.files += %w( include/grpc/grpc_security.h )
+  s.files += %w( include/grpc/grpc_security_constants.h )
   s.files += %w( include/grpc/census.h )
   s.files += %w( src/core/lib/channel/channel_args.h )
   s.files += %w( src/core/lib/channel/channel_stack.h )
diff --git a/include/grpc++/security/server_credentials.h b/include/grpc++/security/server_credentials.h
index 5a9f8a42e2..229bab8d84 100644
--- a/include/grpc++/security/server_credentials.h
+++ b/include/grpc++/security/server_credentials.h
@@ -39,6 +39,7 @@
 
 #include <grpc++/security/auth_metadata_processor.h>
 #include <grpc++/support/config.h>
+#include <grpc/grpc_security_constants.h>
 
 struct grpc_server;
 
@@ -69,7 +70,13 @@ class ServerCredentials {
 
 /// Options to create ServerCredentials with SSL
 struct SslServerCredentialsOptions {
-  SslServerCredentialsOptions() : force_client_auth(false) {}
+  // Deprecated
+  SslServerCredentialsOptions()
+      : force_client_auth(false),
+        client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {}
+  SslServerCredentialsOptions(
+      grpc_ssl_client_certificate_request_type request_type)
+      : force_client_auth(false), client_certificate_request(request_type) {}
 
   struct PemKeyCertPair {
     grpc::string private_key;
@@ -77,7 +84,13 @@ struct SslServerCredentialsOptions {
   };
   grpc::string pem_root_certs;
   std::vector<PemKeyCertPair> pem_key_cert_pairs;
+  // Deprecated
   bool force_client_auth;
+
+  // If both force_client_auth and client_certificate_request fields are set,
+  // force_client_auth takes effect i.e
+  // REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY will be enforced.
+  grpc_ssl_client_certificate_request_type client_certificate_request;
 };
 
 /// Builds SSL ServerCredentials given SSL specific options
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index a36926b23e..79199cc5d6 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -35,6 +35,7 @@
 #define GRPC_GRPC_SECURITY_H
 
 #include <grpc/grpc.h>
+#include <grpc/grpc_security_constants.h>
 #include <grpc/status.h>
 
 #ifdef __cplusplus
@@ -43,13 +44,6 @@ extern "C" {
 
 /* --- Authentication Context. --- */
 
-#define GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME "transport_security_type"
-#define GRPC_SSL_TRANSPORT_SECURITY_TYPE "ssl"
-
-#define GRPC_X509_CN_PROPERTY_NAME "x509_common_name"
-#define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name"
-#define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert"
-
 typedef struct grpc_auth_context grpc_auth_context;
 
 typedef struct grpc_auth_property_iterator {
@@ -130,29 +124,11 @@ typedef struct grpc_channel_credentials grpc_channel_credentials;
    The creator of the credentials object is responsible for its release. */
 GRPCAPI void grpc_channel_credentials_release(grpc_channel_credentials *creds);
 
-/* Environment variable that points to the google default application
-   credentials json key or refresh token. Used in the
-   grpc_google_default_credentials_create function. */
-#define GRPC_GOOGLE_CREDENTIALS_ENV_VAR "GOOGLE_APPLICATION_CREDENTIALS"
-
 /* Creates default credentials to connect to a google gRPC service.
    WARNING: Do NOT use this credentials to connect to a non-google service as
    this could result in an oauth2 token leak. */
 GRPCAPI grpc_channel_credentials *grpc_google_default_credentials_create(void);
 
-/* Environment variable that points to the default SSL roots file. This file
-   must be a PEM encoded file with all the roots such as the one that can be
-   downloaded from https://pki.google.com/roots.pem.  */
-#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
-  "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
-
-/* Results for the SSL roots override callback. */
-typedef enum {
-  GRPC_SSL_ROOTS_OVERRIDE_OK,
-  GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY, /* Do not try fallback options. */
-  GRPC_SSL_ROOTS_OVERRIDE_FAIL
-} grpc_ssl_roots_override_result;
-
 /* Callback for getting the SSL roots override from the application.
    In case of success, *pem_roots_certs must be set to a NULL terminated string
    containing the list of PEM encoded root certificates. The ownership is passed
@@ -334,7 +310,8 @@ typedef struct grpc_server_credentials grpc_server_credentials;
    */
 GRPCAPI void grpc_server_credentials_release(grpc_server_credentials *creds);
 
-/* Creates an SSL server_credentials object.
+/* Deprecated in favor of grpc_ssl_server_credentials_create_ex.
+   Creates an SSL server_credentials object.
    - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
      the client root certificates. This parameter may be NULL if the server does
      not want the client to be authenticated with SSL.
@@ -349,6 +326,15 @@ GRPCAPI grpc_server_credentials *grpc_ssl_server_credentials_create(
     const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
     size_t num_key_cert_pairs, int force_client_auth, void *reserved);
 
+/* Same as grpc_ssl_server_credentials_create method except uses
+   grpc_ssl_client_certificate_request_type enum to support more ways to
+   authenticate client cerificates.*/
+GRPCAPI grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
+    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    void *reserved);
+
 /* --- Server-side secure ports. --- */
 
 /* Add a HTTP2 over an encrypted link over tcp listener.
diff --git a/include/grpc/grpc_security_constants.h b/include/grpc/grpc_security_constants.h
new file mode 100644
index 0000000000..da05c5a97b
--- /dev/null
+++ b/include/grpc/grpc_security_constants.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_GRPC_SECURITY_CONSTANTS_H
+#define GRPC_GRPC_SECURITY_CONSTANTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME "transport_security_type"
+#define GRPC_SSL_TRANSPORT_SECURITY_TYPE "ssl"
+
+#define GRPC_X509_CN_PROPERTY_NAME "x509_common_name"
+#define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name"
+#define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert"
+
+/* Environment variable that points to the default SSL roots file. This file
+   must be a PEM encoded file with all the roots such as the one that can be
+   downloaded from https://pki.google.com/roots.pem.  */
+#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
+  "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
+
+/* Environment variable that points to the google default application
+   credentials json key or refresh token. Used in the
+   grpc_google_default_credentials_create function. */
+#define GRPC_GOOGLE_CREDENTIALS_ENV_VAR "GOOGLE_APPLICATION_CREDENTIALS"
+
+/* Results for the SSL roots override callback. */
+typedef enum {
+  GRPC_SSL_ROOTS_OVERRIDE_OK,
+  GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY, /* Do not try fallback options. */
+  GRPC_SSL_ROOTS_OVERRIDE_FAIL
+} grpc_ssl_roots_override_result;
+
+typedef enum {
+  /* Server does not request client certificate. A client can present a self
+     signed or signed certificates if it wishes to do so and they would be
+     accepted. */
+  GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+  /* Server requests client certificate but does not enforce that the client
+     presents a certificate.
+
+     If the client presents a certificate, the client authentication is left to
+     the application based on the metadata like certificate etc.
+
+     The key cert pair should still be valid for the SSL connection to be
+     established. */
+  GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  /* Server requests client certificate but does not enforce that the client
+     presents a certificate.
+
+     If the client presents a certificate, the client authentication is done by
+     grpc framework (The client needs to either present a signed cert or skip no
+     certificate for a successful connection).
+
+     The key cert pair should still be valid for the SSL connection to be
+     established. */
+  GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY,
+  /* Server requests client certificate but enforces that the client presents a
+     certificate.
+
+     If the client presents a certificate, the client authentication is left to
+     the application based on the metadata like certificate etc.
+
+     The key cert pair should still be valid for the SSL connection to be
+     established. */
+  GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  /* Server requests client certificate but enforces that the client presents a
+     certificate.
+
+     The cerificate presented by the client is verified by grpc framework (The
+     client needs to present signed certs for a successful connection).
+
+     The key cert pair should still be valid for the SSL connection to be
+     established. */
+  GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+} grpc_ssl_client_certificate_request_type;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_GRPC_SECURITY_CONSTANTS_H */
diff --git a/package.xml b/package.xml
index ced62b63d6..99ef0b8c70 100644
--- a/package.xml
+++ b/package.xml
@@ -174,6 +174,7 @@
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_win32.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/time.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/grpc_security.h" role="src" />
+    <file baseinstalldir="/" name="include/grpc/grpc_security_constants.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/census.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_args.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.h" role="src" />
diff --git a/src/core/lib/security/credentials.c b/src/core/lib/security/credentials.c
index 2c7d31519c..fd5ad3589b 100644
--- a/src/core/lib/security/credentials.c
+++ b/src/core/lib/security/credentials.c
@@ -338,10 +338,11 @@ static void ssl_build_config(const char *pem_root_certs,
 
 static void ssl_build_server_config(
     const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
-    size_t num_key_cert_pairs, int force_client_auth,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
     grpc_ssl_server_config *config) {
   size_t i;
-  config->force_client_auth = force_client_auth;
+  config->client_certificate_request = client_certificate_request;
   if (pem_root_certs != NULL) {
     ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
                           &config->pem_root_certs_size);
@@ -391,21 +392,35 @@ grpc_channel_credentials *grpc_ssl_credentials_create(
 grpc_server_credentials *grpc_ssl_server_credentials_create(
     const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
     size_t num_key_cert_pairs, int force_client_auth, void *reserved) {
+  return grpc_ssl_server_credentials_create_ex(
+      pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs,
+      force_client_auth
+          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+          : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+      reserved);
+}
+
+grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
+    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    void *reserved) {
   grpc_ssl_server_credentials *c =
       gpr_malloc(sizeof(grpc_ssl_server_credentials));
   GRPC_API_TRACE(
-      "grpc_ssl_server_credentials_create("
+      "grpc_ssl_server_credentials_create_ex("
       "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, "
-      "force_client_auth=%d, reserved=%p)",
+      "client_certificate_request=%d, reserved=%p)",
       5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs,
-          force_client_auth, reserved));
+          client_certificate_request, reserved));
   GPR_ASSERT(reserved == NULL);
   memset(c, 0, sizeof(grpc_ssl_server_credentials));
   c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL;
   gpr_ref_init(&c->base.refcount, 1);
   c->base.vtable = &ssl_server_vtable;
   ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
-                          num_key_cert_pairs, force_client_auth, &c->config);
+                          num_key_cert_pairs, client_certificate_request,
+                          &c->config);
   return &c->base;
 }
 
diff --git a/src/core/lib/security/security_connector.c b/src/core/lib/security/security_connector.c
index 59863ba064..2d2023bdf5 100644
--- a/src/core/lib/security/security_connector.c
+++ b/src/core/lib/security/security_connector.c
@@ -668,6 +668,31 @@ gpr_slice grpc_get_default_ssl_roots_for_testing(void) {
   return compute_default_pem_root_certs_once();
 }
 
+static tsi_client_certificate_request_type
+get_tsi_client_certificate_request_type(
+    grpc_ssl_client_certificate_request_type grpc_request_type) {
+  switch (grpc_request_type) {
+    case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE:
+      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
+
+    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+      return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
+
+    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
+      return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY;
+
+    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
+
+    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
+      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY;
+
+    default:
+      // Is this a sane default
+      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
+  }
+}
+
 size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) {
   /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in
      loading all the roots once for the lifetime of the process. */
@@ -782,15 +807,16 @@ grpc_security_status grpc_ssl_server_security_connector_create(
   gpr_ref_init(&c->base.base.refcount, 1);
   c->base.base.url_scheme = GRPC_SSL_URL_SCHEME;
   c->base.base.vtable = &ssl_server_vtable;
-  result = tsi_create_ssl_server_handshaker_factory(
+  result = tsi_create_ssl_server_handshaker_factory_ex(
       (const unsigned char **)config->pem_private_keys,
       config->pem_private_keys_sizes,
       (const unsigned char **)config->pem_cert_chains,
       config->pem_cert_chains_sizes, config->num_key_cert_pairs,
       config->pem_root_certs, config->pem_root_certs_size,
-      config->force_client_auth, ssl_cipher_suites(), alpn_protocol_strings,
-      alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols,
-      &c->handshaker_factory);
+      get_tsi_client_certificate_request_type(
+          config->client_certificate_request),
+      ssl_cipher_suites(), alpn_protocol_strings, alpn_protocol_string_lengths,
+      (uint16_t)num_alpn_protocols, &c->handshaker_factory);
   if (result != TSI_OK) {
     gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
             tsi_result_to_string(result));
diff --git a/src/core/lib/security/security_connector.h b/src/core/lib/security/security_connector.h
index c9e262b1ad..2c893cd5e9 100644
--- a/src/core/lib/security/security_connector.h
+++ b/src/core/lib/security/security_connector.h
@@ -241,7 +241,7 @@ typedef struct {
   size_t num_key_cert_pairs;
   unsigned char *pem_root_certs;
   size_t pem_root_certs_size;
-  int force_client_auth;
+  grpc_ssl_client_certificate_request_type client_certificate_request;
 } grpc_ssl_server_config;
 
 /* Creates an SSL server_security_connector.
diff --git a/src/core/lib/tsi/ssl_transport_security.c b/src/core/lib/tsi/ssl_transport_security.c
index 045901cc72..e91c6316e7 100644
--- a/src/core/lib/tsi/ssl_transport_security.c
+++ b/src/core/lib/tsi/ssl_transport_security.c
@@ -718,6 +718,14 @@ static tsi_result build_alpn_protocol_name_list(
   return TSI_OK;
 }
 
+// The verification callback is used for clients that don't really care about
+// the server's certificate, but we need to pull it anyway, in case a higher
+// layer wants to look at it. In this case the verification may fail, but
+// we don't really care.
+static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX *ctx) {
+  return 1;
+}
+
 /* --- tsi_frame_protector methods implementation. ---*/
 
 static tsi_result ssl_protector_protect(tsi_frame_protector *self,
@@ -1390,6 +1398,26 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
     const char *cipher_list, const unsigned char **alpn_protocols,
     const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
     tsi_ssl_handshaker_factory **factory) {
+  return tsi_create_ssl_server_handshaker_factory_ex(
+      pem_private_keys, pem_private_keys_sizes, pem_cert_chains,
+      pem_cert_chains_sizes, key_cert_pair_count, pem_client_root_certs,
+      pem_client_root_certs_size,
+      force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+                        : TSI_DONT_REQUEST_CLIENT_CERTIFICATE,
+      cipher_list, alpn_protocols, alpn_protocols_lengths, num_alpn_protocols,
+      factory);
+}
+
+tsi_result tsi_create_ssl_server_handshaker_factory_ex(
+    const unsigned char **pem_private_keys,
+    const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains,
+    const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count,
+    const unsigned char *pem_client_root_certs,
+    size_t pem_client_root_certs_size,
+    tsi_client_certificate_request_type client_certificate_request,
+    const char *cipher_list, const unsigned char **alpn_protocols,
+    const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
+    tsi_ssl_handshaker_factory **factory) {
   tsi_ssl_server_handshaker_factory *impl = NULL;
   tsi_result result = TSI_OK;
   size_t i = 0;
@@ -1445,7 +1473,6 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
       if (result != TSI_OK) break;
 
       if (pem_client_root_certs != NULL) {
-        int flags = SSL_VERIFY_PEER;
         STACK_OF(X509_NAME) *root_names = NULL;
         result = ssl_ctx_load_verification_certs(
             impl->ssl_contexts[i], pem_client_root_certs,
@@ -1455,8 +1482,29 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
           break;
         }
         SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names);
-        if (force_client_auth) flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
-        SSL_CTX_set_verify(impl->ssl_contexts[i], flags, NULL);
+        switch (client_certificate_request) {
+          case TSI_DONT_REQUEST_CLIENT_CERTIFICATE:
+            SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, NULL);
+            break;
+          case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+            SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER,
+                               NullVerifyCallback);
+            break;
+          case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
+            SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, NULL);
+            break;
+          case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+            SSL_CTX_set_verify(
+                impl->ssl_contexts[i],
+                SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                NullVerifyCallback);
+            break;
+          case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
+            SSL_CTX_set_verify(
+                impl->ssl_contexts[i],
+                SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+            break;
+        }
         /* TODO(jboeuf): Add revocation verification. */
       }
 
diff --git a/src/core/lib/tsi/ssl_transport_security.h b/src/core/lib/tsi/ssl_transport_security.h
index 211c8f9656..7407246118 100644
--- a/src/core/lib/tsi/ssl_transport_security.h
+++ b/src/core/lib/tsi/ssl_transport_security.h
@@ -142,6 +142,23 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
     const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
     tsi_ssl_handshaker_factory **factory);
 
+/* Same as tsi_create_ssl_server_handshaker_factory method except uses
+   tsi_client_certificate_request_type to support more ways to handle client
+   certificate authentication.
+   - client_certificate_request, if set to non-zero will force the client to
+     authenticate with an SSL cert. Note that this option is ignored if
+     pem_client_root_certs is NULL or pem_client_roots_certs_size is 0 */
+tsi_result tsi_create_ssl_server_handshaker_factory_ex(
+    const unsigned char **pem_private_keys,
+    const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains,
+    const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count,
+    const unsigned char *pem_client_root_certs,
+    size_t pem_client_root_certs_size,
+    tsi_client_certificate_request_type client_certificate_request,
+    const char *cipher_suites, const unsigned char **alpn_protocols,
+    const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
+    tsi_ssl_handshaker_factory **factory);
+
 /* Creates a handshaker.
   - self is the factory from which the handshaker will be created.
   - server_name_indication indicates the name of the server the client is
diff --git a/src/core/lib/tsi/transport_security_interface.h b/src/core/lib/tsi/transport_security_interface.h
index d81ec0963a..3e8c9d7ffe 100644
--- a/src/core/lib/tsi/transport_security_interface.h
+++ b/src/core/lib/tsi/transport_security_interface.h
@@ -59,6 +59,15 @@ typedef enum {
   TSI_OUT_OF_RESOURCES = 12
 } tsi_result;
 
+typedef enum {
+  // Default option
+  TSI_DONT_REQUEST_CLIENT_CERTIFICATE,
+  TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY,
+  TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY,
+} tsi_client_certificate_request_type;
+
 const char *tsi_result_to_string(tsi_result result);
 
 /* --- tsi tracing --- */
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index d472667a7e..33bdc2a1f4 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -130,10 +130,14 @@ std::shared_ptr<ServerCredentials> SslServerCredentials(
                                     key_cert_pair->cert_chain.c_str()};
     pem_key_cert_pairs.push_back(p);
   }
-  grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create(
+  grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create_ex(
       options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
       pem_key_cert_pairs.empty() ? nullptr : &pem_key_cert_pairs[0],
-      pem_key_cert_pairs.size(), options.force_client_auth, nullptr);
+      pem_key_cert_pairs.size(),
+      options.force_client_auth
+          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+          : options.client_certificate_request,
+      nullptr);
   return std::shared_ptr<ServerCredentials>(
       new SecureServerCredentials(c_creds));
 }
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 8d769e5f6a..aeef8a79e9 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -911,9 +911,12 @@ grpcsharp_ssl_server_credentials_create(
       key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i];
     }
   }
-  creds = grpc_ssl_server_credentials_create(pem_root_certs, key_cert_pairs,
-                                             num_key_cert_pairs,
-                                             force_client_auth, NULL);
+  creds = grpc_ssl_server_credentials_create_ex(
+      pem_root_certs, key_cert_pairs, num_key_cert_pairs,
+      force_client_auth
+          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+          : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+      NULL);
   gpr_free(key_cert_pairs);
   return creds;
 }
diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc
index 5285d53df4..cff821aafc 100644
--- a/src/node/ext/server_credentials.cc
+++ b/src/node/ext/server_credentials.cc
@@ -145,9 +145,13 @@ NAN_METHOD(ServerCredentials::CreateSsl) {
     return Nan::ThrowTypeError(
         "createSsl's second argument must be a list of objects");
   }
-  int force_client_auth = 0;
+
+  grpc_ssl_client_certificate_request_type client_certificate_request;
   if (info[2]->IsBoolean()) {
-    force_client_auth = (int)Nan::To<bool>(info[2]).FromJust();
+    client_certificate_request =
+        Nan::To<bool>(info[2]).FromJust()
+            ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+            : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
   } else if (!(info[2]->IsUndefined() || info[2]->IsNull())) {
     return Nan::ThrowTypeError(
         "createSsl's third argument must be a boolean if provided");
@@ -180,8 +184,9 @@ NAN_METHOD(ServerCredentials::CreateSsl) {
     key_cert_pairs[i].private_key = ::node::Buffer::Data(maybe_key);
     key_cert_pairs[i].cert_chain = ::node::Buffer::Data(maybe_cert);
   }
-  grpc_server_credentials *creds = grpc_ssl_server_credentials_create(
-      root_certs, key_cert_pairs, key_cert_pair_count, force_client_auth, NULL);
+  grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex(
+      root_certs, key_cert_pairs, key_cert_pair_count,
+      client_certificate_request, NULL);
   delete key_cert_pairs;
   if (creds == NULL) {
     info.GetReturnValue().SetNull();
diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c
index 79188246bc..f3951b31fe 100644
--- a/src/php/ext/grpc/server_credentials.c
+++ b/src/php/ext/grpc/server_credentials.c
@@ -115,10 +115,11 @@ PHP_METHOD(ServerCredentials, createSsl) {
                          "createSsl expects 3 strings", 1 TSRMLS_CC);
     return;
   }
-  /* TODO: add a force_client_auth field in ServerCredentials and pass it as
-   * the last parameter. */
-  grpc_server_credentials *creds = grpc_ssl_server_credentials_create(
-      pem_root_certs, &pem_key_cert_pair, 1, 0, NULL);
+  /* TODO: add a client_certificate_request field in ServerCredentials and pass
+   * it as the last parameter. */
+  grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex(
+      pem_root_certs, &pem_key_cert_pair, 1,
+      GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, NULL);
   zval *creds_object = grpc_php_wrap_server_credentials(creds);
   RETURN_DESTROY_ZVAL(creds_object);
 }
diff --git a/src/proto/grpc/binary_log/v1alpha/log.proto b/src/proto/grpc/binary_log/v1alpha/log.proto
index 6cc473be74..83166cd410 100644
--- a/src/proto/grpc/binary_log/v1alpha/log.proto
+++ b/src/proto/grpc/binary_log/v1alpha/log.proto
@@ -105,4 +105,4 @@ message Message {
   // The contents of the message. May be a prefix instead of the complete
   // message.
   bytes data = 5;
-}
\ No newline at end of file
+}
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
index 842635f56b..94d13b5999 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
@@ -302,6 +302,8 @@ def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
         (<SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
   credentials.c_credentials = grpc_ssl_server_credentials_create(
       c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
-      credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL)
+      credentials.c_ssl_pem_key_cert_pairs_count,
+      GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY if force_client_auth else GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+      NULL)
   return credentials
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 7696f8c7f7..3d158a7707 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -105,6 +105,13 @@ cdef extern from "grpc/_cython/loader.h":
     GRPC_SSL_ROOTS_OVERRIDE_FAILED_PERMANENTLY
     GRPC_SSL_ROOTS_OVERRIDE_FAILED
 
+  ctypedef enum grpc_ssl_client_certificate_request_type:
+    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+    GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY
+    GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY
+    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY
+    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+
   struct grpc_byte_buffer_reader:
     # We don't care about the internals
     pass
diff --git a/src/python/grpcio/grpc/_cython/imports.generated.c b/src/python/grpcio/grpc/_cython/imports.generated.c
index 8bd6ae6372..9567fcd5a4 100644
--- a/src/python/grpcio/grpc/_cython/imports.generated.c
+++ b/src/python/grpcio/grpc/_cython/imports.generated.c
@@ -152,6 +152,7 @@ grpc_metadata_credentials_create_from_plugin_type grpc_metadata_credentials_crea
 grpc_secure_channel_create_type grpc_secure_channel_create_import;
 grpc_server_credentials_release_type grpc_server_credentials_release_import;
 grpc_ssl_server_credentials_create_type grpc_ssl_server_credentials_create_import;
+grpc_ssl_server_credentials_create_ex_type grpc_ssl_server_credentials_create_ex_import;
 grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 grpc_call_set_credentials_type grpc_call_set_credentials_import;
 grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import;
@@ -418,6 +419,7 @@ void pygrpc_load_imports(HMODULE library) {
   grpc_secure_channel_create_import = (grpc_secure_channel_create_type) GetProcAddress(library, "grpc_secure_channel_create");
   grpc_server_credentials_release_import = (grpc_server_credentials_release_type) GetProcAddress(library, "grpc_server_credentials_release");
   grpc_ssl_server_credentials_create_import = (grpc_ssl_server_credentials_create_type) GetProcAddress(library, "grpc_ssl_server_credentials_create");
+  grpc_ssl_server_credentials_create_ex_import = (grpc_ssl_server_credentials_create_ex_type) GetProcAddress(library, "grpc_ssl_server_credentials_create_ex");
   grpc_server_add_secure_http2_port_import = (grpc_server_add_secure_http2_port_type) GetProcAddress(library, "grpc_server_add_secure_http2_port");
   grpc_call_set_credentials_import = (grpc_call_set_credentials_type) GetProcAddress(library, "grpc_call_set_credentials");
   grpc_server_credentials_set_auth_metadata_processor_import = (grpc_server_credentials_set_auth_metadata_processor_type) GetProcAddress(library, "grpc_server_credentials_set_auth_metadata_processor");
diff --git a/src/python/grpcio/grpc/_cython/imports.generated.h b/src/python/grpcio/grpc/_cython/imports.generated.h
index 272e85b485..097c258263 100644
--- a/src/python/grpcio/grpc/_cython/imports.generated.h
+++ b/src/python/grpcio/grpc/_cython/imports.generated.h
@@ -406,6 +406,9 @@ extern grpc_server_credentials_release_type grpc_server_credentials_release_impo
 typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_type)(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs, int force_client_auth, void *reserved);
 extern grpc_ssl_server_credentials_create_type grpc_ssl_server_credentials_create_import;
 #define grpc_ssl_server_credentials_create grpc_ssl_server_credentials_create_import
+typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_ex_type)(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs, grpc_ssl_client_certificate_request_type client_certificate_request, void *reserved);
+extern grpc_ssl_server_credentials_create_ex_type grpc_ssl_server_credentials_create_ex_import;
+#define grpc_ssl_server_credentials_create_ex grpc_ssl_server_credentials_create_ex_import
 typedef int(*grpc_server_add_secure_http2_port_type)(grpc_server *server, const char *addr, grpc_server_credentials *creds);
 extern grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 #define grpc_server_add_secure_http2_port grpc_server_add_secure_http2_port_import
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 56db4ec686..d4657342d1 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -152,6 +152,7 @@ grpc_metadata_credentials_create_from_plugin_type grpc_metadata_credentials_crea
 grpc_secure_channel_create_type grpc_secure_channel_create_import;
 grpc_server_credentials_release_type grpc_server_credentials_release_import;
 grpc_ssl_server_credentials_create_type grpc_ssl_server_credentials_create_import;
+grpc_ssl_server_credentials_create_ex_type grpc_ssl_server_credentials_create_ex_import;
 grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 grpc_call_set_credentials_type grpc_call_set_credentials_import;
 grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import;
@@ -414,6 +415,7 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_secure_channel_create_import = (grpc_secure_channel_create_type) GetProcAddress(library, "grpc_secure_channel_create");
   grpc_server_credentials_release_import = (grpc_server_credentials_release_type) GetProcAddress(library, "grpc_server_credentials_release");
   grpc_ssl_server_credentials_create_import = (grpc_ssl_server_credentials_create_type) GetProcAddress(library, "grpc_ssl_server_credentials_create");
+  grpc_ssl_server_credentials_create_ex_import = (grpc_ssl_server_credentials_create_ex_type) GetProcAddress(library, "grpc_ssl_server_credentials_create_ex");
   grpc_server_add_secure_http2_port_import = (grpc_server_add_secure_http2_port_type) GetProcAddress(library, "grpc_server_add_secure_http2_port");
   grpc_call_set_credentials_import = (grpc_call_set_credentials_type) GetProcAddress(library, "grpc_call_set_credentials");
   grpc_server_credentials_set_auth_metadata_processor_import = (grpc_server_credentials_set_auth_metadata_processor_type) GetProcAddress(library, "grpc_server_credentials_set_auth_metadata_processor");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index c526f434c6..4b02087b72 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -406,6 +406,9 @@ extern grpc_server_credentials_release_type grpc_server_credentials_release_impo
 typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_type)(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs, int force_client_auth, void *reserved);
 extern grpc_ssl_server_credentials_create_type grpc_ssl_server_credentials_create_import;
 #define grpc_ssl_server_credentials_create grpc_ssl_server_credentials_create_import
+typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_ex_type)(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs, grpc_ssl_client_certificate_request_type client_certificate_request, void *reserved);
+extern grpc_ssl_server_credentials_create_ex_type grpc_ssl_server_credentials_create_ex_import;
+#define grpc_ssl_server_credentials_create_ex grpc_ssl_server_credentials_create_ex_import
 typedef int(*grpc_server_add_secure_http2_port_type)(grpc_server *server, const char *addr, grpc_server_credentials *creds);
 extern grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 #define grpc_server_add_secure_http2_port grpc_server_add_secure_http2_port_import
diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c
index 33b8372850..b2d7280a30 100644
--- a/src/ruby/ext/grpc/rb_server_credentials.c
+++ b/src/ruby/ext/grpc/rb_server_credentials.c
@@ -90,9 +90,12 @@ static void grpc_rb_server_credentials_mark(void *p) {
 
 static const rb_data_type_t grpc_rb_server_credentials_data_type = {
     "grpc_server_credentials",
-    {grpc_rb_server_credentials_mark, grpc_rb_server_credentials_free,
-     GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}},
-    NULL, NULL,
+    {grpc_rb_server_credentials_mark,
+     grpc_rb_server_credentials_free,
+     GRPC_RB_MEMSIZE_UNAVAILABLE,
+     {NULL, NULL}},
+    NULL,
+    NULL,
 #ifdef RUBY_TYPED_FREE_IMMEDIATELY
     RUBY_TYPED_FREE_IMMEDIATELY
 #endif
@@ -219,7 +222,9 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
     }
   }
 
-  auth_client = TYPE(force_client_auth) == T_TRUE;
+  auth_client = TYPE(force_client_auth) == T_TRUE
+                    ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+                    : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
   key_cert_pairs = ALLOC_N(grpc_ssl_pem_key_cert_pair, num_key_certs);
   for (i = 0; i < num_key_certs; i++) {
     key_cert = rb_ary_entry(pem_key_certs, i);
@@ -233,13 +238,12 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
                        &grpc_rb_server_credentials_data_type, wrapper);
 
   if (pem_root_certs == Qnil) {
-    creds = grpc_ssl_server_credentials_create(NULL, key_cert_pairs,
-                                               num_key_certs,
-                                               auth_client, NULL);
+    creds = grpc_ssl_server_credentials_create_ex(
+        NULL, key_cert_pairs, num_key_certs, auth_client, NULL);
   } else {
-    creds = grpc_ssl_server_credentials_create(RSTRING_PTR(pem_root_certs),
-                                               key_cert_pairs, num_key_certs,
-                                               auth_client, NULL);
+    creds = grpc_ssl_server_credentials_create_ex(RSTRING_PTR(pem_root_certs),
+                                                  key_cert_pairs, num_key_certs,
+                                                  auth_client, NULL);
   }
   xfree(key_cert_pairs);
   if (creds == NULL) {
diff --git a/test/core/end2end/data/client_certs.c b/test/core/end2end/data/client_certs.c
new file mode 100644
index 0000000000..9cdb704335
--- /dev/null
+++ b/test/core/end2end/data/client_certs.c
@@ -0,0 +1,343 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+const char test_self_signed_client_cert[] = {
+    0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43,
+    0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d,
+    0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6f, 0x44, 0x43, 0x43,
+    0x41, 0x67, 0x6d, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x4a,
+    0x41, 0x4e, 0x49, 0x7a, 0x32, 0x2f, 0x7a, 0x6f, 0x52, 0x69, 0x61, 0x70,
+    0x4d, 0x41, 0x30, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, 0x62, 0x33,
+    0x44, 0x51, 0x45, 0x42, 0x42, 0x51, 0x55, 0x41, 0x4d, 0x47, 0x6b, 0x78,
+    0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x0a, 0x42, 0x41, 0x59,
+    0x54, 0x41, 0x6b, 0x46, 0x56, 0x4d, 0x52, 0x4d, 0x77, 0x45, 0x51, 0x59,
+    0x44, 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, 0x70, 0x54, 0x62, 0x32, 0x31,
+    0x6c, 0x4c, 0x56, 0x4e, 0x30, 0x59, 0x58, 0x52, 0x6c, 0x4d, 0x53, 0x45,
+    0x77, 0x48, 0x77, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x68,
+    0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x63, 0x6d, 0x35, 0x6c, 0x64, 0x43, 0x42,
+    0x58, 0x0a, 0x61, 0x57, 0x52, 0x6e, 0x61, 0x58, 0x52, 0x7a, 0x49, 0x46,
+    0x42, 0x30, 0x65, 0x53, 0x42, 0x4d, 0x64, 0x47, 0x51, 0x78, 0x49, 0x6a,
+    0x41, 0x67, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x47, 0x57,
+    0x4a, 0x68, 0x5a, 0x47, 0x4e, 0x73, 0x61, 0x57, 0x56, 0x75, 0x64, 0x43,
+    0x35, 0x30, 0x5a, 0x58, 0x4e, 0x30, 0x4c, 0x6d, 0x64, 0x76, 0x62, 0x32,
+    0x64, 0x73, 0x5a, 0x53, 0x35, 0x6a, 0x0a, 0x62, 0x32, 0x30, 0x77, 0x48,
+    0x68, 0x63, 0x4e, 0x4d, 0x54, 0x51, 0x77, 0x4e, 0x7a, 0x49, 0x34, 0x4d,
+    0x6a, 0x41, 0x77, 0x4f, 0x44, 0x49, 0x31, 0x57, 0x68, 0x63, 0x4e, 0x4d,
+    0x6a, 0x51, 0x77, 0x4e, 0x7a, 0x49, 0x31, 0x4d, 0x6a, 0x41, 0x77, 0x4f,
+    0x44, 0x49, 0x31, 0x57, 0x6a, 0x42, 0x70, 0x4d, 0x51, 0x73, 0x77, 0x43,
+    0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x42, 0x0a,
+    0x56, 0x54, 0x45, 0x54, 0x4d, 0x42, 0x45, 0x47, 0x41, 0x31, 0x55, 0x45,
+    0x43, 0x41, 0x77, 0x4b, 0x55, 0x32, 0x39, 0x74, 0x5a, 0x53, 0x31, 0x54,
+    0x64, 0x47, 0x46, 0x30, 0x5a, 0x54, 0x45, 0x68, 0x4d, 0x42, 0x38, 0x47,
+    0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77, 0x59, 0x53, 0x57, 0x35, 0x30,
+    0x5a, 0x58, 0x4a, 0x75, 0x5a, 0x58, 0x51, 0x67, 0x56, 0x32, 0x6c, 0x6b,
+    0x5a, 0x32, 0x6c, 0x30, 0x0a, 0x63, 0x79, 0x42, 0x51, 0x64, 0x48, 0x6b,
+    0x67, 0x54, 0x48, 0x52, 0x6b, 0x4d, 0x53, 0x49, 0x77, 0x49, 0x41, 0x59,
+    0x44, 0x56, 0x51, 0x51, 0x44, 0x44, 0x42, 0x6c, 0x69, 0x59, 0x57, 0x52,
+    0x6a, 0x62, 0x47, 0x6c, 0x6c, 0x62, 0x6e, 0x51, 0x75, 0x64, 0x47, 0x56,
+    0x7a, 0x64, 0x43, 0x35, 0x6e, 0x62, 0x32, 0x39, 0x6e, 0x62, 0x47, 0x55,
+    0x75, 0x59, 0x32, 0x39, 0x74, 0x4d, 0x49, 0x47, 0x66, 0x0a, 0x4d, 0x41,
+    0x30, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, 0x62, 0x33, 0x44, 0x51,
+    0x45, 0x42, 0x41, 0x51, 0x55, 0x41, 0x41, 0x34, 0x47, 0x4e, 0x41, 0x44,
+    0x43, 0x42, 0x69, 0x51, 0x4b, 0x42, 0x67, 0x51, 0x43, 0x79, 0x58, 0x32,
+    0x4a, 0x78, 0x5a, 0x2b, 0x4a, 0x35, 0x49, 0x2b, 0x64, 0x6c, 0x68, 0x52,
+    0x4f, 0x56, 0x74, 0x71, 0x6c, 0x4d, 0x51, 0x6e, 0x34, 0x37, 0x42, 0x42,
+    0x63, 0x72, 0x0a, 0x6c, 0x32, 0x47, 0x43, 0x6b, 0x76, 0x39, 0x4f, 0x31,
+    0x44, 0x31, 0x72, 0x4c, 0x39, 0x34, 0x4b, 0x57, 0x59, 0x62, 0x59, 0x31,
+    0x34, 0x48, 0x58, 0x68, 0x69, 0x2f, 0x6e, 0x61, 0x63, 0x42, 0x41, 0x51,
+    0x74, 0x43, 0x45, 0x51, 0x77, 0x58, 0x78, 0x70, 0x35, 0x44, 0x4b, 0x65,
+    0x6d, 0x47, 0x4f, 0x55, 0x6a, 0x75, 0x36, 0x35, 0x78, 0x4d, 0x39, 0x46,
+    0x39, 0x36, 0x2f, 0x33, 0x37, 0x34, 0x47, 0x0a, 0x4d, 0x76, 0x6e, 0x52,
+    0x4a, 0x64, 0x6f, 0x35, 0x32, 0x67, 0x4f, 0x73, 0x34, 0x48, 0x4f, 0x30,
+    0x63, 0x7a, 0x42, 0x70, 0x66, 0x56, 0x4e, 0x64, 0x58, 0x65, 0x65, 0x6f,
+    0x44, 0x2f, 0x52, 0x59, 0x67, 0x77, 0x74, 0x74, 0x66, 0x64, 0x4a, 0x72,
+    0x7a, 0x2f, 0x34, 0x61, 0x61, 0x74, 0x73, 0x53, 0x32, 0x51, 0x6b, 0x32,
+    0x79, 0x4d, 0x59, 0x70, 0x71, 0x5a, 0x6d, 0x71, 0x45, 0x4d, 0x73, 0x62,
+    0x0a, 0x72, 0x68, 0x39, 0x57, 0x32, 0x32, 0x4c, 0x70, 0x33, 0x72, 0x43,
+    0x42, 0x76, 0x77, 0x49, 0x44, 0x41, 0x51, 0x41, 0x42, 0x6f, 0x31, 0x41,
+    0x77, 0x54, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34,
+    0x45, 0x46, 0x67, 0x51, 0x55, 0x35, 0x32, 0x33, 0x41, 0x4a, 0x4d, 0x52,
+    0x38, 0x44, 0x73, 0x39, 0x56, 0x38, 0x66, 0x68, 0x66, 0x37, 0x67, 0x75,
+    0x31, 0x69, 0x30, 0x4d, 0x4d, 0x0a, 0x55, 0x71, 0x41, 0x77, 0x48, 0x77,
+    0x59, 0x44, 0x56, 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, 0x46, 0x6f,
+    0x41, 0x55, 0x35, 0x32, 0x33, 0x41, 0x4a, 0x4d, 0x52, 0x38, 0x44, 0x73,
+    0x39, 0x56, 0x38, 0x66, 0x68, 0x66, 0x37, 0x67, 0x75, 0x31, 0x69, 0x30,
+    0x4d, 0x4d, 0x55, 0x71, 0x41, 0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x52,
+    0x30, 0x54, 0x42, 0x41, 0x55, 0x77, 0x41, 0x77, 0x45, 0x42, 0x0a, 0x2f,
+    0x7a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39,
+    0x77, 0x30, 0x42, 0x41, 0x51, 0x55, 0x46, 0x41, 0x41, 0x4f, 0x42, 0x67,
+    0x51, 0x43, 0x49, 0x2f, 0x74, 0x76, 0x53, 0x42, 0x59, 0x48, 0x31, 0x69,
+    0x79, 0x66, 0x4c, 0x61, 0x43, 0x54, 0x42, 0x4b, 0x77, 0x70, 0x64, 0x6a,
+    0x33, 0x36, 0x2b, 0x4d, 0x6b, 0x52, 0x39, 0x45, 0x65, 0x4a, 0x4a, 0x6d,
+    0x49, 0x6d, 0x78, 0x0a, 0x58, 0x2b, 0x62, 0x6a, 0x68, 0x4b, 0x57, 0x58,
+    0x77, 0x73, 0x42, 0x58, 0x34, 0x50, 0x44, 0x4d, 0x57, 0x76, 0x64, 0x75,
+    0x73, 0x72, 0x2b, 0x2b, 0x51, 0x47, 0x55, 0x59, 0x74, 0x79, 0x6f, 0x79,
+    0x61, 0x2b, 0x68, 0x66, 0x59, 0x4d, 0x58, 0x52, 0x68, 0x58, 0x75, 0x61,
+    0x33, 0x39, 0x6d, 0x44, 0x35, 0x34, 0x78, 0x67, 0x6c, 0x6f, 0x51, 0x4e,
+    0x75, 0x75, 0x39, 0x52, 0x45, 0x44, 0x77, 0x58, 0x0a, 0x46, 0x66, 0x74,
+    0x6f, 0x2b, 0x61, 0x4f, 0x77, 0x33, 0x42, 0x63, 0x59, 0x64, 0x75, 0x63,
+    0x7a, 0x36, 0x6f, 0x66, 0x78, 0x69, 0x63, 0x46, 0x4b, 0x2f, 0x59, 0x32,
+    0x56, 0x65, 0x58, 0x44, 0x75, 0x72, 0x53, 0x4d, 0x70, 0x52, 0x76, 0x35,
+    0x54, 0x66, 0x47, 0x66, 0x32, 0x51, 0x72, 0x36, 0x65, 0x4f, 0x4f, 0x64,
+    0x61, 0x52, 0x68, 0x6a, 0x36, 0x65, 0x64, 0x37, 0x42, 0x69, 0x62, 0x48,
+    0x6b, 0x0a, 0x58, 0x31, 0x56, 0x47, 0x5a, 0x41, 0x3d, 0x3d, 0x0a, 0x2d,
+    0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54,
+    0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+    0x0a, 0x00};
+
+const char test_self_signed_client_key[] = {
+    0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50,
+    0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+    0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x64, 0x77, 0x49, 0x42,
+    0x41, 0x44, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
+    0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43,
+    0x41, 0x6d, 0x45, 0x77, 0x67, 0x67, 0x4a, 0x64, 0x41, 0x67, 0x45, 0x41,
+    0x41, 0x6f, 0x47, 0x42, 0x41, 0x4c, 0x4a, 0x66, 0x59, 0x6e, 0x46, 0x6e,
+    0x34, 0x6e, 0x6b, 0x6a, 0x35, 0x32, 0x57, 0x46, 0x0a, 0x45, 0x35, 0x57,
+    0x32, 0x71, 0x55, 0x78, 0x43, 0x66, 0x6a, 0x73, 0x45, 0x46, 0x79, 0x75,
+    0x58, 0x59, 0x59, 0x4b, 0x53, 0x2f, 0x30, 0x37, 0x55, 0x50, 0x57, 0x73,
+    0x76, 0x33, 0x67, 0x70, 0x5a, 0x68, 0x74, 0x6a, 0x58, 0x67, 0x64, 0x65,
+    0x47, 0x4c, 0x2b, 0x64, 0x70, 0x77, 0x45, 0x42, 0x43, 0x30, 0x49, 0x52,
+    0x44, 0x42, 0x66, 0x47, 0x6e, 0x6b, 0x4d, 0x70, 0x36, 0x59, 0x59, 0x35,
+    0x53, 0x0a, 0x4f, 0x37, 0x72, 0x6e, 0x45, 0x7a, 0x30, 0x58, 0x33, 0x72,
+    0x2f, 0x66, 0x76, 0x67, 0x59, 0x79, 0x2b, 0x64, 0x45, 0x6c, 0x32, 0x6a,
+    0x6e, 0x61, 0x41, 0x36, 0x7a, 0x67, 0x63, 0x37, 0x52, 0x7a, 0x4d, 0x47,
+    0x6c, 0x39, 0x55, 0x31, 0x31, 0x64, 0x35, 0x36, 0x67, 0x50, 0x39, 0x46,
+    0x69, 0x44, 0x43, 0x32, 0x31, 0x39, 0x30, 0x6d, 0x76, 0x50, 0x2f, 0x68,
+    0x70, 0x71, 0x32, 0x78, 0x4c, 0x5a, 0x0a, 0x43, 0x54, 0x62, 0x49, 0x78,
+    0x69, 0x6d, 0x70, 0x6d, 0x61, 0x6f, 0x51, 0x79, 0x78, 0x75, 0x75, 0x48,
+    0x31, 0x62, 0x62, 0x59, 0x75, 0x6e, 0x65, 0x73, 0x49, 0x47, 0x2f, 0x41,
+    0x67, 0x4d, 0x42, 0x41, 0x41, 0x45, 0x43, 0x67, 0x59, 0x41, 0x64, 0x71,
+    0x4a, 0x43, 0x45, 0x7a, 0x4d, 0x49, 0x79, 0x5a, 0x45, 0x37, 0x6f, 0x61,
+    0x57, 0x30, 0x74, 0x4f, 0x70, 0x63, 0x42, 0x30, 0x42, 0x69, 0x50, 0x0a,
+    0x46, 0x59, 0x6f, 0x49, 0x76, 0x48, 0x34, 0x42, 0x4b, 0x52, 0x48, 0x38,
+    0x65, 0x48, 0x76, 0x52, 0x34, 0x37, 0x36, 0x6d, 0x74, 0x2b, 0x59, 0x64,
+    0x44, 0x68, 0x42, 0x50, 0x31, 0x73, 0x63, 0x47, 0x55, 0x6d, 0x59, 0x65,
+    0x43, 0x54, 0x34, 0x45, 0x6a, 0x2b, 0x52, 0x67, 0x48, 0x76, 0x32, 0x4c,
+    0x50, 0x54, 0x67, 0x56, 0x59, 0x77, 0x54, 0x39, 0x65, 0x63, 0x69, 0x50,
+    0x32, 0x2b, 0x45, 0x2f, 0x0a, 0x43, 0x42, 0x43, 0x4e, 0x52, 0x65, 0x6c,
+    0x30, 0x53, 0x77, 0x39, 0x4a, 0x65, 0x70, 0x77, 0x57, 0x30, 0x72, 0x2b,
+    0x6a, 0x57, 0x4a, 0x74, 0x44, 0x59, 0x31, 0x70, 0x70, 0x36, 0x59, 0x58,
+    0x41, 0x67, 0x4e, 0x52, 0x47, 0x58, 0x32, 0x55, 0x66, 0x6c, 0x76, 0x55,
+    0x73, 0x54, 0x2b, 0x6f, 0x39, 0x6c, 0x5a, 0x76, 0x61, 0x67, 0x66, 0x39,
+    0x6d, 0x6f, 0x4c, 0x54, 0x4d, 0x79, 0x47, 0x76, 0x55, 0x0a, 0x75, 0x4c,
+    0x46, 0x6e, 0x73, 0x79, 0x66, 0x4c, 0x69, 0x6d, 0x31, 0x42, 0x34, 0x76,
+    0x58, 0x76, 0x57, 0x51, 0x4a, 0x42, 0x41, 0x4e, 0x6f, 0x75, 0x5a, 0x6c,
+    0x6c, 0x58, 0x47, 0x5a, 0x6f, 0x53, 0x72, 0x5a, 0x4c, 0x74, 0x52, 0x33,
+    0x56, 0x67, 0x56, 0x34, 0x74, 0x7a, 0x52, 0x51, 0x76, 0x4a, 0x78, 0x75,
+    0x38, 0x34, 0x6b, 0x4c, 0x65, 0x49, 0x6b, 0x36, 0x34, 0x4f, 0x76, 0x34,
+    0x37, 0x58, 0x0a, 0x70, 0x48, 0x56, 0x42, 0x4d, 0x54, 0x52, 0x42, 0x66,
+    0x7a, 0x50, 0x45, 0x68, 0x62, 0x42, 0x6f, 0x64, 0x6a, 0x72, 0x31, 0x6d,
+    0x35, 0x4f, 0x4c, 0x61, 0x56, 0x4c, 0x71, 0x6b, 0x46, 0x63, 0x58, 0x66,
+    0x74, 0x7a, 0x52, 0x43, 0x72, 0x62, 0x57, 0x6f, 0x4b, 0x73, 0x43, 0x51,
+    0x51, 0x44, 0x52, 0x53, 0x6f, 0x4c, 0x4c, 0x58, 0x4f, 0x69, 0x4c, 0x72,
+    0x74, 0x4a, 0x33, 0x44, 0x4c, 0x4a, 0x43, 0x0a, 0x72, 0x58, 0x37, 0x59,
+    0x38, 0x77, 0x72, 0x48, 0x5a, 0x72, 0x71, 0x6b, 0x35, 0x62, 0x4d, 0x64,
+    0x5a, 0x4c, 0x47, 0x61, 0x2f, 0x55, 0x58, 0x38, 0x52, 0x61, 0x6e, 0x68,
+    0x56, 0x77, 0x33, 0x2b, 0x58, 0x70, 0x2b, 0x75, 0x72, 0x64, 0x31, 0x37,
+    0x31, 0x31, 0x75, 0x6d, 0x65, 0x4e, 0x4a, 0x66, 0x7a, 0x75, 0x2f, 0x4d,
+    0x43, 0x6b, 0x34, 0x61, 0x31, 0x4b, 0x6b, 0x47, 0x2f, 0x43, 0x55, 0x30,
+    0x0a, 0x72, 0x71, 0x73, 0x39, 0x41, 0x6b, 0x41, 0x34, 0x63, 0x53, 0x78,
+    0x31, 0x44, 0x44, 0x31, 0x4a, 0x53, 0x47, 0x2b, 0x79, 0x78, 0x4d, 0x4e,
+    0x70, 0x73, 0x41, 0x53, 0x31, 0x78, 0x4a, 0x6f, 0x6d, 0x46, 0x49, 0x72,
+    0x73, 0x4d, 0x39, 0x76, 0x73, 0x50, 0x74, 0x37, 0x46, 0x64, 0x6e, 0x64,
+    0x44, 0x77, 0x72, 0x46, 0x2b, 0x79, 0x2b, 0x43, 0x6f, 0x76, 0x68, 0x44,
+    0x6b, 0x47, 0x59, 0x44, 0x6b, 0x0a, 0x52, 0x41, 0x48, 0x68, 0x2b, 0x73,
+    0x76, 0x47, 0x66, 0x5a, 0x67, 0x2f, 0x70, 0x51, 0x4b, 0x32, 0x4a, 0x52,
+    0x50, 0x69, 0x6d, 0x41, 0x6d, 0x48, 0x68, 0x7a, 0x71, 0x46, 0x41, 0x6b,
+    0x45, 0x41, 0x75, 0x36, 0x59, 0x61, 0x37, 0x30, 0x73, 0x32, 0x46, 0x55,
+    0x65, 0x42, 0x33, 0x4d, 0x75, 0x39, 0x61, 0x4a, 0x73, 0x32, 0x43, 0x44,
+    0x36, 0x68, 0x67, 0x33, 0x64, 0x51, 0x45, 0x56, 0x6b, 0x42, 0x0a, 0x35,
+    0x33, 0x44, 0x49, 0x37, 0x54, 0x58, 0x34, 0x38, 0x64, 0x39, 0x6b, 0x47,
+    0x57, 0x35, 0x38, 0x56, 0x58, 0x31, 0x78, 0x6e, 0x71, 0x53, 0x30, 0x32,
+    0x4c, 0x79, 0x57, 0x71, 0x41, 0x50, 0x63, 0x57, 0x35, 0x71, 0x6d, 0x31,
+    0x6b, 0x4c, 0x48, 0x46, 0x4c, 0x64, 0x6e, 0x64, 0x61, 0x50, 0x4e, 0x6d,
+    0x42, 0x61, 0x6a, 0x34, 0x51, 0x4a, 0x42, 0x41, 0x4a, 0x75, 0x67, 0x6c,
+    0x33, 0x36, 0x37, 0x0a, 0x39, 0x64, 0x39, 0x74, 0x2f, 0x51, 0x4c, 0x54,
+    0x53, 0x75, 0x55, 0x4c, 0x4c, 0x61, 0x6f, 0x59, 0x76, 0x32, 0x76, 0x4a,
+    0x54, 0x33, 0x73, 0x31, 0x79, 0x39, 0x48, 0x4e, 0x38, 0x39, 0x45, 0x6f,
+    0x61, 0x44, 0x44, 0x45, 0x6b, 0x50, 0x56, 0x66, 0x51, 0x75, 0x36, 0x47,
+    0x56, 0x45, 0x58, 0x67, 0x49, 0x42, 0x74, 0x69, 0x6d, 0x31, 0x73, 0x49,
+    0x2f, 0x56, 0x50, 0x53, 0x7a, 0x49, 0x38, 0x48, 0x0a, 0x61, 0x58, 0x76,
+    0x61, 0x54, 0x55, 0x77, 0x62, 0x6c, 0x46, 0x57, 0x53, 0x4d, 0x37, 0x30,
+    0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50,
+    0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+    0x2d, 0x2d, 0x2d, 0x0a, 0x00};
+
+const char test_signed_client_cert[] = {
+    0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43,
+    0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d,
+    0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x48, 0x7a, 0x43, 0x43,
+    0x41, 0x59, 0x67, 0x43, 0x41, 0x51, 0x45, 0x77, 0x44, 0x51, 0x59, 0x4a,
+    0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45, 0x46,
+    0x42, 0x51, 0x41, 0x77, 0x56, 0x6a, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47,
+    0x41, 0x31, 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x51, 0x56, 0x55, 0x78,
+    0x45, 0x7a, 0x41, 0x52, 0x42, 0x67, 0x4e, 0x56, 0x0a, 0x42, 0x41, 0x67,
+    0x4d, 0x43, 0x6c, 0x4e, 0x76, 0x62, 0x57, 0x55, 0x74, 0x55, 0x33, 0x52,
+    0x68, 0x64, 0x47, 0x55, 0x78, 0x49, 0x54, 0x41, 0x66, 0x42, 0x67, 0x4e,
+    0x56, 0x42, 0x41, 0x6f, 0x4d, 0x47, 0x45, 0x6c, 0x75, 0x64, 0x47, 0x56,
+    0x79, 0x62, 0x6d, 0x56, 0x30, 0x49, 0x46, 0x64, 0x70, 0x5a, 0x47, 0x64,
+    0x70, 0x64, 0x48, 0x4d, 0x67, 0x55, 0x48, 0x52, 0x35, 0x49, 0x45, 0x78,
+    0x30, 0x0a, 0x5a, 0x44, 0x45, 0x50, 0x4d, 0x41, 0x30, 0x47, 0x41, 0x31,
+    0x55, 0x45, 0x41, 0x77, 0x77, 0x47, 0x64, 0x47, 0x56, 0x7a, 0x64, 0x47,
+    0x4e, 0x68, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x45, 0x30, 0x4d, 0x44,
+    0x63, 0x78, 0x4e, 0x7a, 0x49, 0x7a, 0x4e, 0x54, 0x59, 0x77, 0x4d, 0x6c,
+    0x6f, 0x58, 0x44, 0x54, 0x49, 0x30, 0x4d, 0x44, 0x63, 0x78, 0x4e, 0x44,
+    0x49, 0x7a, 0x4e, 0x54, 0x59, 0x77, 0x0a, 0x4d, 0x6c, 0x6f, 0x77, 0x57,
+    0x6a, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42,
+    0x68, 0x4d, 0x43, 0x51, 0x56, 0x55, 0x78, 0x45, 0x7a, 0x41, 0x52, 0x42,
+    0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x43, 0x6c, 0x4e, 0x76, 0x62,
+    0x57, 0x55, 0x74, 0x55, 0x33, 0x52, 0x68, 0x64, 0x47, 0x55, 0x78, 0x49,
+    0x54, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x0a,
+    0x47, 0x45, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x79, 0x62, 0x6d, 0x56, 0x30,
+    0x49, 0x46, 0x64, 0x70, 0x5a, 0x47, 0x64, 0x70, 0x64, 0x48, 0x4d, 0x67,
+    0x55, 0x48, 0x52, 0x35, 0x49, 0x45, 0x78, 0x30, 0x5a, 0x44, 0x45, 0x54,
+    0x4d, 0x42, 0x45, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x4b,
+    0x64, 0x47, 0x56, 0x7a, 0x64, 0x47, 0x4e, 0x73, 0x61, 0x57, 0x56, 0x75,
+    0x64, 0x44, 0x43, 0x42, 0x0a, 0x6e, 0x7a, 0x41, 0x4e, 0x42, 0x67, 0x6b,
+    0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45,
+    0x46, 0x41, 0x41, 0x4f, 0x42, 0x6a, 0x51, 0x41, 0x77, 0x67, 0x59, 0x6b,
+    0x43, 0x67, 0x59, 0x45, 0x41, 0x37, 0x46, 0x52, 0x48, 0x32, 0x36, 0x47,
+    0x2b, 0x46, 0x74, 0x35, 0x56, 0x51, 0x67, 0x79, 0x7a, 0x6c, 0x5a, 0x73,
+    0x66, 0x53, 0x6e, 0x48, 0x53, 0x5a, 0x36, 0x47, 0x58, 0x0a, 0x62, 0x37,
+    0x71, 0x78, 0x6d, 0x6b, 0x32, 0x50, 0x4f, 0x38, 0x54, 0x59, 0x71, 0x4b,
+    0x5a, 0x6d, 0x6b, 0x66, 0x4d, 0x77, 0x6b, 0x65, 0x36, 0x52, 0x55, 0x66,
+    0x51, 0x56, 0x2b, 0x53, 0x2b, 0x47, 0x7a, 0x52, 0x76, 0x7a, 0x35, 0x4c,
+    0x6c, 0x53, 0x33, 0x31, 0x55, 0x31, 0x51, 0x43, 0x70, 0x33, 0x63, 0x67,
+    0x77, 0x6b, 0x49, 0x49, 0x41, 0x51, 0x61, 0x31, 0x45, 0x32, 0x68, 0x43,
+    0x45, 0x7a, 0x0a, 0x57, 0x33, 0x31, 0x69, 0x76, 0x62, 0x4d, 0x42, 0x79,
+    0x52, 0x4b, 0x39, 0x74, 0x46, 0x70, 0x79, 0x6e, 0x34, 0x55, 0x76, 0x38,
+    0x4b, 0x50, 0x31, 0x34, 0x4f, 0x62, 0x4b, 0x6a, 0x54, 0x51, 0x71, 0x78,
+    0x55, 0x5a, 0x70, 0x35, 0x35, 0x38, 0x44, 0x67, 0x4f, 0x48, 0x67, 0x35,
+    0x62, 0x35, 0x6d, 0x47, 0x52, 0x4d, 0x30, 0x70, 0x79, 0x56, 0x31, 0x65,
+    0x71, 0x52, 0x4b, 0x36, 0x50, 0x57, 0x77, 0x0a, 0x52, 0x2f, 0x62, 0x6a,
+    0x67, 0x6c, 0x6c, 0x69, 0x36, 0x70, 0x6d, 0x6e, 0x72, 0x2b, 0x30, 0x43,
+    0x41, 0x77, 0x45, 0x41, 0x41, 0x54, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71,
+    0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x55, 0x46,
+    0x41, 0x41, 0x4f, 0x42, 0x67, 0x51, 0x41, 0x53, 0x74, 0x53, 0x6d, 0x35,
+    0x50, 0x4d, 0x37, 0x75, 0x62, 0x52, 0x4f, 0x69, 0x4b, 0x4b, 0x36, 0x2f,
+    0x0a, 0x54, 0x32, 0x46, 0x6b, 0x4b, 0x6c, 0x68, 0x69, 0x54, 0x4f, 0x78,
+    0x2b, 0x52, 0x79, 0x65, 0x6e, 0x6d, 0x33, 0x45, 0x69, 0x6f, 0x35, 0x39,
+    0x65, 0x6d, 0x71, 0x2b, 0x6a, 0x58, 0x6c, 0x2b, 0x31, 0x6e, 0x68, 0x50,
+    0x79, 0x53, 0x58, 0x35, 0x47, 0x32, 0x50, 0x51, 0x7a, 0x53, 0x52, 0x35,
+    0x76, 0x64, 0x31, 0x64, 0x49, 0x68, 0x77, 0x67, 0x5a, 0x53, 0x52, 0x34,
+    0x47, 0x79, 0x74, 0x74, 0x6b, 0x0a, 0x74, 0x52, 0x5a, 0x35, 0x37, 0x6b,
+    0x2f, 0x4e, 0x49, 0x31, 0x62, 0x72, 0x55, 0x57, 0x38, 0x6a, 0x6f, 0x69,
+    0x45, 0x4f, 0x4d, 0x4a, 0x41, 0x2f, 0x4d, 0x72, 0x37, 0x48, 0x37, 0x61,
+    0x73, 0x78, 0x37, 0x77, 0x49, 0x52, 0x59, 0x44, 0x45, 0x39, 0x31, 0x46,
+    0x73, 0x38, 0x47, 0x6b, 0x4b, 0x57, 0x64, 0x35, 0x4c, 0x68, 0x6f, 0x50,
+    0x41, 0x51, 0x6a, 0x2b, 0x71, 0x64, 0x47, 0x33, 0x35, 0x43, 0x0a, 0x4f,
+    0x4f, 0x2b, 0x73, 0x76, 0x64, 0x6b, 0x6d, 0x71, 0x48, 0x30, 0x4b, 0x5a,
+    0x6f, 0x33, 0x32, 0x30, 0x5a, 0x55, 0x71, 0x64, 0x6c, 0x32, 0x6f, 0x6f,
+    0x51, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44,
+    0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45,
+    0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00};
+
+const char test_signed_client_key[] = {
+    0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50,
+    0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+    0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x65, 0x51, 0x49, 0x42,
+    0x41, 0x44, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
+    0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43,
+    0x41, 0x6d, 0x4d, 0x77, 0x67, 0x67, 0x4a, 0x66, 0x41, 0x67, 0x45, 0x41,
+    0x41, 0x6f, 0x47, 0x42, 0x41, 0x4f, 0x78, 0x55, 0x52, 0x39, 0x75, 0x68,
+    0x76, 0x68, 0x62, 0x65, 0x56, 0x55, 0x49, 0x4d, 0x0a, 0x73, 0x35, 0x57,
+    0x62, 0x48, 0x30, 0x70, 0x78, 0x30, 0x6d, 0x65, 0x68, 0x6c, 0x32, 0x2b,
+    0x36, 0x73, 0x5a, 0x70, 0x4e, 0x6a, 0x7a, 0x76, 0x45, 0x32, 0x4b, 0x69,
+    0x6d, 0x5a, 0x70, 0x48, 0x7a, 0x4d, 0x4a, 0x48, 0x75, 0x6b, 0x56, 0x48,
+    0x30, 0x46, 0x66, 0x6b, 0x76, 0x68, 0x73, 0x30, 0x62, 0x38, 0x2b, 0x53,
+    0x35, 0x55, 0x74, 0x39, 0x56, 0x4e, 0x55, 0x41, 0x71, 0x64, 0x33, 0x49,
+    0x4d, 0x0a, 0x4a, 0x43, 0x43, 0x41, 0x45, 0x47, 0x74, 0x52, 0x4e, 0x6f,
+    0x51, 0x68, 0x4d, 0x31, 0x74, 0x39, 0x59, 0x72, 0x32, 0x7a, 0x41, 0x63,
+    0x6b, 0x53, 0x76, 0x62, 0x52, 0x61, 0x63, 0x70, 0x2b, 0x46, 0x4c, 0x2f,
+    0x43, 0x6a, 0x39, 0x65, 0x44, 0x6d, 0x79, 0x6f, 0x30, 0x30, 0x4b, 0x73,
+    0x56, 0x47, 0x61, 0x65, 0x65, 0x66, 0x41, 0x34, 0x44, 0x68, 0x34, 0x4f,
+    0x57, 0x2b, 0x5a, 0x68, 0x6b, 0x54, 0x0a, 0x4e, 0x4b, 0x63, 0x6c, 0x64,
+    0x58, 0x71, 0x6b, 0x53, 0x75, 0x6a, 0x31, 0x73, 0x45, 0x66, 0x32, 0x34,
+    0x34, 0x4a, 0x5a, 0x59, 0x75, 0x71, 0x5a, 0x70, 0x36, 0x2f, 0x74, 0x41,
+    0x67, 0x4d, 0x42, 0x41, 0x41, 0x45, 0x43, 0x67, 0x59, 0x45, 0x41, 0x69,
+    0x32, 0x4e, 0x53, 0x56, 0x71, 0x70, 0x5a, 0x4d, 0x61, 0x66, 0x45, 0x35,
+    0x59, 0x59, 0x55, 0x54, 0x63, 0x4d, 0x47, 0x65, 0x36, 0x51, 0x53, 0x0a,
+    0x6b, 0x32, 0x6a, 0x74, 0x70, 0x73, 0x71, 0x59, 0x67, 0x67, 0x67, 0x49,
+    0x32, 0x52, 0x6e, 0x4c, 0x4a, 0x2f, 0x32, 0x74, 0x4e, 0x5a, 0x77, 0x59,
+    0x49, 0x35, 0x70, 0x77, 0x50, 0x38, 0x51, 0x56, 0x53, 0x62, 0x6e, 0x4d,
+    0x61, 0x69, 0x46, 0x34, 0x67, 0x6f, 0x6b, 0x44, 0x35, 0x68, 0x47, 0x64,
+    0x72, 0x4e, 0x44, 0x66, 0x54, 0x6e, 0x62, 0x32, 0x76, 0x2b, 0x79, 0x49,
+    0x77, 0x59, 0x45, 0x48, 0x0a, 0x30, 0x77, 0x38, 0x2b, 0x6f, 0x47, 0x37,
+    0x5a, 0x38, 0x31, 0x4b, 0x6f, 0x64, 0x73, 0x69, 0x5a, 0x53, 0x49, 0x44,
+    0x4a, 0x66, 0x54, 0x47, 0x73, 0x41, 0x5a, 0x68, 0x56, 0x4e, 0x77, 0x4f,
+    0x7a, 0x39, 0x79, 0x30, 0x56, 0x44, 0x38, 0x42, 0x42, 0x5a, 0x5a, 0x31,
+    0x2f, 0x32, 0x37, 0x34, 0x5a, 0x68, 0x35, 0x32, 0x41, 0x55, 0x4b, 0x4c,
+    0x6a, 0x5a, 0x53, 0x2f, 0x5a, 0x77, 0x49, 0x62, 0x53, 0x0a, 0x57, 0x32,
+    0x79, 0x77, 0x79, 0x61, 0x38, 0x35, 0x35, 0x64, 0x50, 0x6e, 0x48, 0x2f,
+    0x77, 0x6a, 0x2b, 0x30, 0x45, 0x43, 0x51, 0x51, 0x44, 0x39, 0x58, 0x38,
+    0x44, 0x39, 0x32, 0x30, 0x6b, 0x42, 0x79, 0x54, 0x4e, 0x48, 0x68, 0x42,
+    0x47, 0x31, 0x38, 0x62, 0x69, 0x41, 0x45, 0x5a, 0x34, 0x70, 0x78, 0x73,
+    0x39, 0x66, 0x30, 0x4f, 0x41, 0x47, 0x38, 0x33, 0x33, 0x33, 0x65, 0x56,
+    0x63, 0x49, 0x0a, 0x77, 0x32, 0x6c, 0x4a, 0x44, 0x4c, 0x73, 0x59, 0x44,
+    0x5a, 0x72, 0x43, 0x42, 0x32, 0x6f, 0x63, 0x67, 0x41, 0x33, 0x6c, 0x55,
+    0x64, 0x6f, 0x7a, 0x6c, 0x7a, 0x50, 0x43, 0x37, 0x59, 0x44, 0x59, 0x77,
+    0x38, 0x72, 0x65, 0x67, 0x30, 0x74, 0x6b, 0x69, 0x52, 0x59, 0x35, 0x41,
+    0x6b, 0x45, 0x41, 0x37, 0x73, 0x64, 0x4e, 0x7a, 0x4f, 0x65, 0x51, 0x73,
+    0x51, 0x52, 0x6e, 0x37, 0x2b, 0x2b, 0x35, 0x0a, 0x30, 0x62, 0x50, 0x39,
+    0x44, 0x74, 0x54, 0x2f, 0x69, 0x4f, 0x4e, 0x31, 0x67, 0x62, 0x66, 0x78,
+    0x52, 0x7a, 0x43, 0x66, 0x43, 0x66, 0x58, 0x64, 0x6f, 0x4f, 0x74, 0x66,
+    0x51, 0x57, 0x49, 0x7a, 0x54, 0x65, 0x50, 0x57, 0x74, 0x55, 0x52, 0x74,
+    0x39, 0x58, 0x2f, 0x35, 0x44, 0x39, 0x4e, 0x6f, 0x66, 0x49, 0x30, 0x52,
+    0x67, 0x35, 0x57, 0x32, 0x6f, 0x47, 0x79, 0x2f, 0x4d, 0x4c, 0x65, 0x35,
+    0x0a, 0x2f, 0x73, 0x58, 0x48, 0x56, 0x51, 0x4a, 0x42, 0x41, 0x49, 0x75,
+    0x70, 0x35, 0x58, 0x72, 0x4a, 0x44, 0x6b, 0x51, 0x79, 0x77, 0x4e, 0x5a,
+    0x79, 0x41, 0x55, 0x55, 0x32, 0x65, 0x63, 0x6e, 0x32, 0x62, 0x43, 0x57,
+    0x42, 0x46, 0x6a, 0x77, 0x74, 0x71, 0x64, 0x2b, 0x4c, 0x42, 0x6d, 0x75,
+    0x4d, 0x63, 0x69, 0x49, 0x39, 0x66, 0x4f, 0x4b, 0x73, 0x5a, 0x74, 0x45,
+    0x4b, 0x5a, 0x72, 0x7a, 0x2f, 0x0a, 0x55, 0x30, 0x6c, 0x6b, 0x65, 0x4d,
+    0x52, 0x6f, 0x53, 0x77, 0x76, 0x58, 0x45, 0x38, 0x77, 0x6d, 0x47, 0x4c,
+    0x6a, 0x6a, 0x72, 0x41, 0x62, 0x64, 0x66, 0x6f, 0x68, 0x72, 0x58, 0x46,
+    0x6b, 0x43, 0x51, 0x51, 0x44, 0x5a, 0x45, 0x78, 0x2f, 0x4c, 0x74, 0x49,
+    0x6c, 0x36, 0x4a, 0x49, 0x4e, 0x4a, 0x51, 0x69, 0x73, 0x77, 0x56, 0x65,
+    0x30, 0x74, 0x57, 0x72, 0x36, 0x6b, 0x2b, 0x41, 0x53, 0x50, 0x0a, 0x31,
+    0x57, 0x58, 0x6f, 0x54, 0x6d, 0x2b, 0x48, 0x59, 0x70, 0x6f, 0x46, 0x2f,
+    0x58, 0x55, 0x76, 0x76, 0x39, 0x4c, 0x63, 0x63, 0x4e, 0x46, 0x31, 0x49,
+    0x61, 0x7a, 0x46, 0x6a, 0x33, 0x34, 0x68, 0x77, 0x52, 0x51, 0x77, 0x68,
+    0x78, 0x37, 0x77, 0x2f, 0x56, 0x35, 0x32, 0x49, 0x65, 0x62, 0x2b, 0x70,
+    0x30, 0x6a, 0x55, 0x4d, 0x59, 0x47, 0x78, 0x41, 0x6b, 0x45, 0x41, 0x6a,
+    0x44, 0x68, 0x64, 0x0a, 0x39, 0x70, 0x42, 0x4f, 0x31, 0x66, 0x4b, 0x58,
+    0x57, 0x69, 0x58, 0x7a, 0x69, 0x39, 0x5a, 0x4b, 0x66, 0x6f, 0x79, 0x54,
+    0x4e, 0x63, 0x55, 0x71, 0x33, 0x65, 0x42, 0x53, 0x56, 0x4b, 0x77, 0x50,
+    0x47, 0x32, 0x6e, 0x49, 0x74, 0x67, 0x35, 0x79, 0x63, 0x58, 0x65, 0x6e,
+    0x67, 0x6a, 0x54, 0x35, 0x73, 0x67, 0x63, 0x57, 0x44, 0x6e, 0x63, 0x69,
+    0x49, 0x7a, 0x57, 0x37, 0x42, 0x49, 0x56, 0x49, 0x0a, 0x4a, 0x69, 0x71,
+    0x4f, 0x73, 0x7a, 0x71, 0x39, 0x47, 0x57, 0x45, 0x53, 0x45, 0x72, 0x41,
+    0x61, 0x74, 0x67, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45,
+    0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+    0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00};
diff --git a/test/core/end2end/data/ssl_test_data.h b/test/core/end2end/data/ssl_test_data.h
index 675249dbd5..4a64af1e27 100644
--- a/test/core/end2end/data/ssl_test_data.h
+++ b/test/core/end2end/data/ssl_test_data.h
@@ -37,5 +37,9 @@
 extern const char test_root_cert[];
 extern const char test_server1_cert[];
 extern const char test_server1_key[];
+extern const char test_self_signed_client_cert[];
+extern const char test_self_signed_client_key[];
+extern const char test_signed_client_cert[];
+extern const char test_signed_client_key[];
 
 #endif /* GRPC_TEST_CORE_END2END_DATA_SSL_TEST_DATA_H */
diff --git a/test/core/end2end/fixtures/h2_ssl_cert.c b/test/core/end2end/fixtures/h2_ssl_cert.c
new file mode 100644
index 0000000000..cd031ca482
--- /dev/null
+++ b/test/core/end2end/fixtures/h2_ssl_cert.c
@@ -0,0 +1,376 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/support/tmpfile.h"
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+extern void simple_request(grpc_end2end_test_config config);
+
+typedef struct fullstack_secure_fixture_data {
+  char *localaddr;
+} fullstack_secure_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
+    grpc_channel_args *client_args, grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  int port = grpc_pick_unused_port_or_die();
+  fullstack_secure_fixture_data *ffd =
+      gpr_malloc(sizeof(fullstack_secure_fixture_data));
+  memset(&f, 0, sizeof(f));
+
+  gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+  f.fixture_data = ffd;
+  f.cq = grpc_completion_queue_create(NULL);
+
+  return f;
+}
+
+static void process_auth_failure(void *state, grpc_auth_context *ctx,
+                                 const grpc_metadata *md, size_t md_count,
+                                 grpc_process_auth_metadata_done_cb cb,
+                                 void *user_data) {
+  GPR_ASSERT(state == NULL);
+  cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL);
+}
+
+static void chttp2_init_client_secure_fullstack(
+    grpc_end2end_test_fixture *f, grpc_channel_args *client_args,
+    grpc_channel_credentials *creds) {
+  fullstack_secure_fixture_data *ffd = f->fixture_data;
+  f->client =
+      grpc_secure_channel_create(creds, ffd->localaddr, client_args, NULL);
+  GPR_ASSERT(f->client != NULL);
+  grpc_channel_credentials_release(creds);
+}
+
+static void chttp2_init_server_secure_fullstack(
+    grpc_end2end_test_fixture *f, grpc_channel_args *server_args,
+    grpc_server_credentials *server_creds) {
+  fullstack_secure_fixture_data *ffd = f->fixture_data;
+  if (f->server) {
+    grpc_server_destroy(f->server);
+  }
+  f->server = grpc_server_create(server_args, NULL);
+  grpc_server_register_completion_queue(f->server, f->cq, NULL);
+  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr,
+                                               server_creds));
+  grpc_server_credentials_release(server_creds);
+  grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) {
+  fullstack_secure_fixture_data *ffd = f->fixture_data;
+  gpr_free(ffd->localaddr);
+  gpr_free(ffd);
+}
+
+static int fail_server_auth_check(grpc_channel_args *server_args) {
+  size_t i;
+  if (server_args == NULL) return 0;
+  for (i = 0; i < server_args->num_args; i++) {
+    if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) ==
+        0) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+#define SERVER_INIT_NAME(REQUEST_TYPE) \
+  chttp2_init_server_simple_ssl_secure_fullstack_##REQUEST_TYPE
+
+#define SERVER_INIT(REQUEST_TYPE)                                           \
+  static void SERVER_INIT_NAME(REQUEST_TYPE)(                               \
+      grpc_end2end_test_fixture * f, grpc_channel_args * server_args) {     \
+    grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key,       \
+                                                    test_server1_cert};     \
+    grpc_server_credentials *ssl_creds =                                    \
+        grpc_ssl_server_credentials_create_ex(                              \
+            test_root_cert, &pem_cert_key_pair, 1, REQUEST_TYPE, NULL);     \
+    if (fail_server_auth_check(server_args)) {                              \
+      grpc_auth_metadata_processor processor = {process_auth_failure, NULL, \
+                                                NULL};                      \
+      grpc_server_credentials_set_auth_metadata_processor(ssl_creds,        \
+                                                          processor);       \
+    }                                                                       \
+    chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);         \
+  }
+
+SERVER_INIT(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+SERVER_INIT(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY);
+SERVER_INIT(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY);
+SERVER_INIT(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY);
+SERVER_INIT(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
+
+#define CLIENT_INIT_NAME(cert_type) \
+  chttp2_init_client_simple_ssl_secure_fullstack_##cert_type
+
+typedef enum { NONE, SELF_SIGNED, SIGNED, BAD_CERT_PAIR } certtype;
+
+#define CLIENT_INIT(cert_type)                                               \
+  static void CLIENT_INIT_NAME(cert_type)(grpc_end2end_test_fixture * f,     \
+                                          grpc_channel_args * client_args) { \
+    grpc_channel_credentials *ssl_creds = NULL;                              \
+    grpc_ssl_pem_key_cert_pair self_signed_client_key_cert_pair = {          \
+        test_self_signed_client_key, test_self_signed_client_cert};          \
+    grpc_ssl_pem_key_cert_pair signed_client_key_cert_pair = {               \
+        test_signed_client_key, test_signed_client_cert};                    \
+    grpc_ssl_pem_key_cert_pair bad_client_key_cert_pair = {                  \
+        test_self_signed_client_key, test_signed_client_cert};               \
+    grpc_ssl_pem_key_cert_pair *key_cert_pair = NULL;                        \
+    switch (cert_type) {                                                     \
+      case SELF_SIGNED:                                                      \
+        key_cert_pair = &self_signed_client_key_cert_pair;                   \
+        break;                                                               \
+      case SIGNED:                                                           \
+        key_cert_pair = &signed_client_key_cert_pair;                        \
+        break;                                                               \
+      case BAD_CERT_PAIR:                                                    \
+        key_cert_pair = &bad_client_key_cert_pair;                           \
+        break;                                                               \
+      default:                                                               \
+        break;                                                               \
+    }                                                                        \
+    ssl_creds =                                                              \
+        grpc_ssl_credentials_create(test_root_cert, key_cert_pair, NULL);    \
+    grpc_arg ssl_name_override = {GRPC_ARG_STRING,                           \
+                                  GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,         \
+                                  {"foo.test.google.fr"}};                   \
+    grpc_channel_args *new_client_args =                                     \
+        grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);  \
+    chttp2_init_client_secure_fullstack(f, new_client_args, ssl_creds);      \
+    grpc_channel_args_destroy(new_client_args);                              \
+  }
+
+CLIENT_INIT(NONE);
+CLIENT_INIT(SELF_SIGNED);
+CLIENT_INIT(SIGNED);
+CLIENT_INIT(BAD_CERT_PAIR);
+
+#define TEST_NAME(enum_name, cert_type, result) \
+  "chttp2/ssl_" #enum_name "_" #cert_type "_" #result "_"
+
+typedef enum { SUCCESS, FAIL } test_result;
+
+#define SSL_TEST(request_type, cert_type, result)                         \
+  {                                                                       \
+    {TEST_NAME(request_type, cert_type, result),                          \
+     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |                           \
+         FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS,                      \
+     chttp2_create_fixture_secure_fullstack, CLIENT_INIT_NAME(cert_type), \
+     SERVER_INIT_NAME(request_type), chttp2_tear_down_secure_fullstack},  \
+        result                                                            \
+  }
+
+/* All test configurations */
+typedef struct grpc_end2end_test_config_wrapper {
+  grpc_end2end_test_config config;
+  test_result result;
+} grpc_end2end_test_config_wrapper;
+
+static grpc_end2end_test_config_wrapper configs[] = {
+    SSL_TEST(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, NONE, SUCCESS),
+    SSL_TEST(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, SELF_SIGNED, SUCCESS),
+    SSL_TEST(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, SIGNED, SUCCESS),
+    SSL_TEST(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, BAD_CERT_PAIR, FAIL),
+
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, NONE,
+             SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, SELF_SIGNED,
+             SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, SIGNED,
+             SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, BAD_CERT_PAIR,
+             FAIL),
+
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, NONE, SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, SELF_SIGNED, FAIL),
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, SIGNED, SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, BAD_CERT_PAIR,
+             FAIL),
+
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+             NONE, FAIL),
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+             SELF_SIGNED, SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+             SIGNED, SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+             BAD_CERT_PAIR, FAIL),
+
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY, NONE,
+             FAIL),
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY,
+             SELF_SIGNED, FAIL),
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY, SIGNED,
+             SUCCESS),
+    SSL_TEST(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY,
+             BAD_CERT_PAIR, FAIL),
+};
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+static void simple_request_body(grpc_end2end_test_fixture f,
+                                test_result expected_result) {
+  grpc_call *c;
+  gpr_timespec deadline = five_seconds_time();
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_call_error error;
+
+  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               "/foo", "foo.test.google.fr:1234", deadline,
+                               NULL);
+  GPR_ASSERT(c);
+
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  cq_expect_completion(cqv, tag(1), expected_result == SUCCESS);
+  cq_verify(cqv);
+
+  grpc_call_destroy(c);
+  cq_verifier_destroy(cqv);
+}
+
+int main(int argc, char **argv) {
+  size_t i;
+  FILE *roots_file;
+  size_t roots_size = strlen(test_root_cert);
+  char *roots_filename;
+
+  grpc_test_init(argc, argv);
+  grpc_end2end_tests_pre_init();
+
+  /* Set the SSL roots env var. */
+  roots_file =
+      gpr_tmpfile("chttp2_simple_ssl_cert_fullstack_test", &roots_filename);
+  GPR_ASSERT(roots_filename != NULL);
+  GPR_ASSERT(roots_file != NULL);
+  GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
+  fclose(roots_file);
+  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+
+  grpc_init();
+
+  for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+    grpc_end2end_test_fixture f =
+        begin_test(configs[i].config, "SSL_CERT_tests", NULL, NULL);
+
+    simple_request_body(f, configs[i].result);
+    end_test(&f);
+    configs[i].config.tear_down_data(&f);
+  }
+
+  grpc_shutdown();
+
+  /* Cleanup. */
+  remove(roots_filename);
+  gpr_free(roots_filename);
+
+  return 0;
+}
diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py
index 9a940a4ab5..cffe5995bc 100755
--- a/test/core/end2end/gen_build_yaml.py
+++ b/test/core/end2end/gen_build_yaml.py
@@ -65,6 +65,7 @@ END2END_FIXTURES = {
     'h2_sockpair+trace': socketpair_unsecure_fixture_options._replace(
         ci_mac=False, tracing=True),
     'h2_ssl': default_secure_fixture_options,
+    'h2_ssl_cert': default_secure_fixture_options,
     'h2_ssl_proxy': default_secure_fixture_options._replace(includes_proxy=True,
                                                             ci_mac=False),
     'h2_uds': uds_fixture_options,
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index 579faa4441..0eede6c23b 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -37,6 +37,7 @@
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
+#include <grpc/grpc_security_constants.h>
 #include <grpc/impl/codegen/alloc.h>
 #include <grpc/impl/codegen/atm.h>
 #include <grpc/impl/codegen/byte_buffer.h>
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index de6fd0de49..034d9c6e6f 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -786,6 +786,7 @@ include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_win32.h \
 include/grpc/impl/codegen/time.h \
 include/grpc/grpc_security.h \
+include/grpc/grpc_security_constants.h \
 include/grpc/census.h \
 include/grpc/support/alloc.h \
 include/grpc/support/atm.h \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 4b3c8ab4bf..4414758985 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -786,6 +786,7 @@ include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_win32.h \
 include/grpc/impl/codegen/time.h \
 include/grpc/grpc_security.h \
+include/grpc/grpc_security_constants.h \
 include/grpc/census.h \
 src/core/lib/channel/channel_args.h \
 src/core/lib/channel/channel_stack.h \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 7978f12d53..ef4df1bd18 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -3681,6 +3681,23 @@
     "third_party": false, 
     "type": "target"
   }, 
+  {
+    "deps": [
+      "end2end_tests", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "src": [
+      "test/core/end2end/fixtures/h2_ssl_cert.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
   {
     "deps": [
       "end2end_tests", 
@@ -4097,6 +4114,7 @@
     "language": "c", 
     "name": "grpc_test_util", 
     "src": [
+      "test/core/end2end/data/client_certs.c", 
       "test/core/end2end/data/server1_cert.c", 
       "test/core/end2end/data/server1_key.c", 
       "test/core/end2end/data/ssl_test_data.h", 
@@ -6163,6 +6181,7 @@
     ], 
     "headers": [
       "include/grpc/grpc_security.h", 
+      "include/grpc/grpc_security_constants.h", 
       "src/core/lib/security/auth_filters.h", 
       "src/core/lib/security/b64.h", 
       "src/core/lib/security/credentials.h", 
@@ -6182,6 +6201,7 @@
     "name": "grpc_secure", 
     "src": [
       "include/grpc/grpc_security.h", 
+      "include/grpc/grpc_security_constants.h", 
       "src/core/lib/http/httpcli_security_connector.c", 
       "src/core/lib/security/auth_filters.h", 
       "src/core/lib/security/b64.c", 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index f4a76dedb1..54fde59855 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -13428,6 +13428,842 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "bad_hostname"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "binary_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "call_creds"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_accept"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_client_done"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_before_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_in_a_vacuum"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_with_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "compressed_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "connectivity"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "default_host"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "disappearing_server"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "empty_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_causes_close"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "graceful_server_shutdown"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "high_initial_seqno"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "hpack_size"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "idempotent_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "invoke_large_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "large_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_concurrent_streams"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_message_length"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "negative_deadline"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_op"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "registered_call"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_flags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "server_finishes_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_calls"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_tags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_delayed_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "trailing_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "bad_hostname"
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index d26c1f8dfc..2ffa186565 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -1283,6 +1283,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_ssl_test", "vcxproj\test
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_ssl_cert_test", "vcxproj\test/end2end/fixtures\h2_ssl_cert_test\h2_ssl_cert_test.vcxproj", "{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{1F1F9084-2A93-B80E-364F-5754894AFAB4} = {1F1F9084-2A93-B80E-364F-5754894AFAB4}
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_ssl_proxy_test", "vcxproj\test/end2end/fixtures\h2_ssl_proxy_test\h2_ssl_proxy_test.vcxproj", "{A9092608-E45E-AC96-6533-A6E7DD98211D}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -3339,6 +3351,22 @@ Global
 		{EA78D290-4098-FF04-C647-013F6B81E4E7}.Release-DLL|Win32.Build.0 = Release|Win32
 		{EA78D290-4098-FF04-C647-013F6B81E4E7}.Release-DLL|x64.ActiveCfg = Release|x64
 		{EA78D290-4098-FF04-C647-013F6B81E4E7}.Release-DLL|x64.Build.0 = Release|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug|x64.ActiveCfg = Debug|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release|Win32.ActiveCfg = Release|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release|x64.ActiveCfg = Release|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug|Win32.Build.0 = Debug|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug|x64.Build.0 = Debug|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release|Win32.Build.0 = Release|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release|x64.Build.0 = Release|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Debug-DLL|x64.Build.0 = Debug|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release-DLL|Win32.Build.0 = Release|Win32
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release-DLL|x64.ActiveCfg = Release|x64
+		{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}.Release-DLL|x64.Build.0 = Release|x64
 		{A9092608-E45E-AC96-6533-A6E7DD98211D}.Debug|Win32.ActiveCfg = Debug|Win32
 		{A9092608-E45E-AC96-6533-A6E7DD98211D}.Debug|x64.ActiveCfg = Debug|x64
 		{A9092608-E45E-AC96-6533-A6E7DD98211D}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 32540da499..2e4d151f95 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -293,6 +293,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_win32.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\census.h" />
   </ItemGroup>
   <ItemGroup>
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 09b94cffe9..63569df0ab 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -576,6 +576,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\census.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
index d1f67ee44e..79e00e78ff 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
@@ -161,6 +161,8 @@
     <ClInclude Include="$(SolutionDir)\..\test\core\util\slice_splitter.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\data\client_certs.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\data\server1_cert.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\data\server1_key.c">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
index 2fee6aab62..e7c64c44f4 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\data\client_certs.c">
+      <Filter>test\core\end2end\data</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\data\server1_cert.c">
       <Filter>test\core\end2end\data</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj
new file mode 100644
index 0000000000..d64c317810
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B3B7D225-3C04-72F9-4C2C-1C3F3136FE58}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>h2_ssl_cert_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>h2_ssl_cert_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_ssl_cert.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_tests\end2end_tests.vcxproj">
+      <Project>{1F1F9084-2A93-B80E-364F-5754894AFAB4}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj.filters
new file mode 100644
index 0000000000..532b0ae2b3
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_ssl_cert_test/h2_ssl_cert_test.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_ssl_cert.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{2ad9c3be-3600-2475-3705-8927bd57651b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{5d5ee434-b892-585d-97ca-ae595eecbd0b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end">
+      <UniqueIdentifier>{903c738d-3c85-534d-d26e-01138f2e96c6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end\fixtures">
+      <UniqueIdentifier>{f5bca83d-8278-22b4-7999-c50cea11b90b}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
-- 
GitLab