diff --git a/rtc_base/opensslidentity.cc b/rtc_base/opensslidentity.cc index 8eb1c42d24..85ef176de0 100644 --- a/rtc_base/opensslidentity.cc +++ b/rtc_base/opensslidentity.cc @@ -16,17 +16,18 @@ #include "rtc_base/win32.h" // NOLINT #include +#include +#include #include #include -#include #include -#include #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/openssl.h" #include "rtc_base/openssldigest.h" +#include "rtc_base/ptr_util.h" namespace rtc { @@ -143,7 +144,7 @@ static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) { RTC_LOG(LS_INFO) << "Returning certificate"; return x509; - error: +error: BN_free(serial_number); X509_NAME_free(name); X509_free(x509); @@ -218,8 +219,8 @@ std::string OpenSSLKeyPair::PrivateKeyToPEMString() const { RTC_NOTREACHED(); return ""; } - if (!PEM_write_bio_PrivateKey( - temp_memory_bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr)) { + if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0, + nullptr, nullptr)) { RTC_LOG_F(LS_ERROR) << "Failed to write private key"; BIO_free(temp_memory_bio); RTC_NOTREACHED(); @@ -279,8 +280,13 @@ static void PrintCert(X509* x509) { } #endif +OpenSSLCertificate::OpenSSLCertificate(X509* x509) : x509_(x509) { + AddReference(); +} + OpenSSLCertificate* OpenSSLCertificate::Generate( - OpenSSLKeyPair* key_pair, const SSLIdentityParams& params) { + OpenSSLKeyPair* key_pair, + const SSLIdentityParams& params) { SSLIdentityParams actual_params(params); if (actual_params.common_name.empty()) { // Use a random string, arbitrarily 8chars long. @@ -363,9 +369,6 @@ bool OpenSSLCertificate::GetSignatureDigestAlgorithm( } std::unique_ptr OpenSSLCertificate::GetChain() const { - // Chains are not yet supported when using OpenSSL. - // OpenSSLStreamAdapter::SSLVerifyCallback currently requires the remote - // certificate to be self-signed. return nullptr; } @@ -451,7 +454,7 @@ void OpenSSLCertificate::AddReference() const { } bool OpenSSLCertificate::operator==(const OpenSSLCertificate& other) const { - return X509_cmp(this->x509_, other.x509_) == 0; + return X509_cmp(x509_, other.x509_) == 0; } bool OpenSSLCertificate::operator!=(const OpenSSLCertificate& other) const { @@ -474,24 +477,35 @@ int64_t OpenSSLCertificate::CertificateExpirationTime() const { return ASN1TimeToSec(expire_time->data, expire_time->length, long_format); } -OpenSSLIdentity::OpenSSLIdentity(OpenSSLKeyPair* key_pair, - OpenSSLCertificate* certificate) - : key_pair_(key_pair), certificate_(certificate) { - RTC_DCHECK(key_pair != nullptr); +OpenSSLIdentity::OpenSSLIdentity( + std::unique_ptr key_pair, + std::unique_ptr certificate) + : key_pair_(std::move(key_pair)) { + RTC_DCHECK(key_pair_ != nullptr); RTC_DCHECK(certificate != nullptr); + std::vector> certs; + certs.push_back(std::move(certificate)); + cert_chain_.reset(new SSLCertChain(std::move(certs))); +} + +OpenSSLIdentity::OpenSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr cert_chain) + : key_pair_(std::move(key_pair)), cert_chain_(std::move(cert_chain)) { + RTC_DCHECK(key_pair_ != nullptr); + RTC_DCHECK(cert_chain_ != nullptr); } OpenSSLIdentity::~OpenSSLIdentity() = default; OpenSSLIdentity* OpenSSLIdentity::GenerateInternal( const SSLIdentityParams& params) { - OpenSSLKeyPair* key_pair = OpenSSLKeyPair::Generate(params.key_params); + std::unique_ptr key_pair( + OpenSSLKeyPair::Generate(params.key_params)); if (key_pair) { - OpenSSLCertificate* certificate = - OpenSSLCertificate::Generate(key_pair, params); - if (certificate) - return new OpenSSLIdentity(key_pair, certificate); - delete key_pair; + std::unique_ptr certificate( + OpenSSLCertificate::Generate(key_pair.get(), params)); + if (certificate != nullptr) + return new OpenSSLIdentity(std::move(key_pair), std::move(certificate)); } RTC_LOG(LS_INFO) << "Identity generation failed"; return nullptr; @@ -517,9 +531,8 @@ OpenSSLIdentity* OpenSSLIdentity::GenerateForTest( return GenerateInternal(params); } -SSLIdentity* OpenSSLIdentity::FromPEMStrings( - const std::string& private_key, - const std::string& certificate) { +SSLIdentity* OpenSSLIdentity::FromPEMStrings(const std::string& private_key, + const std::string& certificate) { std::unique_ptr cert( OpenSSLCertificate::FromPEMString(certificate)); if (!cert) { @@ -527,33 +540,84 @@ SSLIdentity* OpenSSLIdentity::FromPEMStrings( return nullptr; } - OpenSSLKeyPair* key_pair = - OpenSSLKeyPair::FromPrivateKeyPEMString(private_key); + std::unique_ptr key_pair( + OpenSSLKeyPair::FromPrivateKeyPEMString(private_key)); if (!key_pair) { RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string."; return nullptr; } - return new OpenSSLIdentity(key_pair, - cert.release()); + return new OpenSSLIdentity(std::move(key_pair), std::move(cert)); +} + +SSLIdentity* OpenSSLIdentity::FromPEMChainStrings( + const std::string& private_key, + const std::string& certificate_chain) { + BIO* bio = + BIO_new_mem_buf(certificate_chain.data(), certificate_chain.size()); + if (!bio) + return nullptr; + BIO_set_mem_eof_return(bio, 0); + std::vector> certs; + while (true) { + X509* x509 = + PEM_read_bio_X509(bio, nullptr, nullptr, const_cast("\0")); + if (x509 == nullptr) { + uint32_t err = ERR_peek_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + break; + } + RTC_LOG(LS_ERROR) << "Failed to parse certificate from PEM string."; + BIO_free(bio); + return nullptr; + } + certs.emplace_back(new OpenSSLCertificate(x509)); + X509_free(x509); + } + BIO_free(bio); + if (certs.empty()) { + RTC_LOG(LS_ERROR) << "Found no certificates in PEM string."; + return nullptr; + } + + std::unique_ptr key_pair( + OpenSSLKeyPair::FromPrivateKeyPEMString(private_key)); + if (!key_pair) { + RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string."; + return nullptr; + } + + return new OpenSSLIdentity(std::move(key_pair), + MakeUnique(std::move(certs))); } const OpenSSLCertificate& OpenSSLIdentity::certificate() const { - return *certificate_; + return *static_cast(&cert_chain_->Get(0)); } OpenSSLIdentity* OpenSSLIdentity::GetReference() const { - return new OpenSSLIdentity(key_pair_->GetReference(), - certificate_->GetReference()); + return new OpenSSLIdentity(WrapUnique(key_pair_->GetReference()), + WrapUnique(certificate().GetReference())); } bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) { // 1 is the documented success return code. - if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 || - SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) { + const OpenSSLCertificate* cert = &certificate(); + if (SSL_CTX_use_certificate(ctx, cert->x509()) != 1 || + SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) { LogSSLErrors("Configuring key and certificate"); return false; } + // If a chain is available, use it. + for (size_t i = 1; i < cert_chain_->GetSize(); ++i) { + cert = static_cast(&cert_chain_->Get(i)); + if (SSL_CTX_add1_chain_cert(ctx, cert->x509()) != 1) { + LogSSLErrors("Configuring intermediate certificate"); + return false; + } + } + return true; } @@ -567,7 +631,7 @@ std::string OpenSSLIdentity::PublicKeyToPEMString() const { bool OpenSSLIdentity::operator==(const OpenSSLIdentity& other) const { return *this->key_pair_ == *other.key_pair_ && - *this->certificate_ == *other.certificate_; + this->certificate() == other.certificate(); } bool OpenSSLIdentity::operator!=(const OpenSSLIdentity& other) const { diff --git a/rtc_base/opensslidentity.h b/rtc_base/opensslidentity.h index e4a790e48e..a700a1d107 100644 --- a/rtc_base/opensslidentity.h +++ b/rtc_base/opensslidentity.h @@ -36,8 +36,7 @@ class OpenSSLKeyPair { static OpenSSLKeyPair* Generate(const KeyParams& key_params); // Constructs a key pair from the private key PEM string. This must not result // in missing public key parameters. Returns null on error. - static OpenSSLKeyPair* FromPrivateKeyPEMString( - const std::string& pem_string); + static OpenSSLKeyPair* FromPrivateKeyPEMString(const std::string& pem_string); virtual ~OpenSSLKeyPair(); @@ -62,9 +61,7 @@ class OpenSSLKeyPair { class OpenSSLCertificate : public SSLCertificate { public: // Caller retains ownership of the X509 object. - explicit OpenSSLCertificate(X509* x509) : x509_(x509) { - AddReference(); - } + explicit OpenSSLCertificate(X509* x509); static OpenSSLCertificate* Generate(OpenSSLKeyPair* key_pair, const SSLIdentityParams& params); @@ -103,7 +100,6 @@ class OpenSSLCertificate : public SSLCertificate { void AddReference() const; X509* x509_; - RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLCertificate); }; @@ -117,6 +113,8 @@ class OpenSSLIdentity : public SSLIdentity { static OpenSSLIdentity* GenerateForTest(const SSLIdentityParams& params); static SSLIdentity* FromPEMStrings(const std::string& private_key, const std::string& certificate); + static SSLIdentity* FromPEMChainStrings(const std::string& private_key, + const std::string& certificate_chain); ~OpenSSLIdentity() override; const OpenSSLCertificate& certificate() const override; @@ -131,17 +129,19 @@ class OpenSSLIdentity : public SSLIdentity { bool operator!=(const OpenSSLIdentity& other) const; private: - OpenSSLIdentity(OpenSSLKeyPair* key_pair, OpenSSLCertificate* certificate); + OpenSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr certificate); + OpenSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr cert_chain); static OpenSSLIdentity* GenerateInternal(const SSLIdentityParams& params); std::unique_ptr key_pair_; - std::unique_ptr certificate_; + std::unique_ptr cert_chain_; RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLIdentity); }; - } // namespace rtc #endif // RTC_BASE_OPENSSLIDENTITY_H_ diff --git a/rtc_base/opensslstreamadapter.cc b/rtc_base/opensslstreamadapter.cc index c1ce2c6def..bdc39c20be 100644 --- a/rtc_base/opensslstreamadapter.cc +++ b/rtc_base/opensslstreamadapter.cc @@ -1104,6 +1104,11 @@ bool OpenSSLStreamAdapter::VerifyPeerCertificate() { return true; } +std::unique_ptr OpenSSLStreamAdapter::GetPeerSSLCertChain() + const { + return std::unique_ptr(peer_cert_chain_->Copy()); +} + int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) { // Get our SSL structure and OpenSSLStreamAdapter from the store. SSL* ssl = reinterpret_cast( @@ -1111,10 +1116,23 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) { OpenSSLStreamAdapter* stream = reinterpret_cast(SSL_get_app_data(ssl)); +#if defined(OPENSSL_IS_BORINGSSL) + STACK_OF(X509)* chain = SSL_get_peer_full_cert_chain(ssl); + // Creates certificate. + stream->peer_certificate_.reset( + new OpenSSLCertificate(sk_X509_value(chain, 0))); + // Creates certificate chain. + std::vector> cert_chain; + for (X509* cert : chain) { + cert_chain.emplace_back(new OpenSSLCertificate(cert)); + } + stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain))); +#else // Record the peer's certificate. X509* cert = SSL_get_peer_certificate(ssl); stream->peer_certificate_.reset(new OpenSSLCertificate(cert)); X509_free(cert); +#endif // If the peer certificate digest isn't known yet, we'll wait to verify // until it's known, and for now just return a success status. diff --git a/rtc_base/opensslstreamadapter.h b/rtc_base/opensslstreamadapter.h index ccd6460d02..b43dcc7e01 100644 --- a/rtc_base/opensslstreamadapter.h +++ b/rtc_base/opensslstreamadapter.h @@ -71,6 +71,8 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter { std::unique_ptr GetPeerCertificate() const override; + std::unique_ptr GetPeerSSLCertChain() const override; + // Goes from state SSL_NONE to either SSL_CONNECTING or SSL_WAIT, depending // on whether the underlying stream is already open or not. int StartSSL() override; @@ -198,6 +200,7 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter { // The certificate that the peer presented. Initially null, until the // connection is established. std::unique_ptr peer_certificate_; + std::unique_ptr peer_cert_chain_; bool peer_certificate_verified_ = false; // The digest of the certificate that the peer must present. Buffer peer_certificate_digest_value_; diff --git a/rtc_base/sslidentity.cc b/rtc_base/sslidentity.cc index 79e2ad56b7..e035d9ec1b 100644 --- a/rtc_base/sslidentity.cc +++ b/rtc_base/sslidentity.cc @@ -265,6 +265,13 @@ SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, return OpenSSLIdentity::FromPEMStrings(private_key, certificate); } +// static +SSLIdentity* SSLIdentity::FromPEMChainStrings( + const std::string& private_key, + const std::string& certificate_chain) { + return OpenSSLIdentity::FromPEMChainStrings(private_key, certificate_chain); +} + bool operator==(const SSLIdentity& a, const SSLIdentity& b) { return static_cast(a) == static_cast(b); diff --git a/rtc_base/sslidentity.h b/rtc_base/sslidentity.h index f20c5470ef..952e2ab8c2 100644 --- a/rtc_base/sslidentity.h +++ b/rtc_base/sslidentity.h @@ -67,8 +67,8 @@ class SSLCertificate { std::unique_ptr GetUniqueReference() const; - // Provides the cert chain, or null. The chain includes a copy of each - // certificate, excluding the leaf. + // Returns null. This is deprecated. Please use + // SSLStreamAdapter::GetPeerSSLCertChain virtual std::unique_ptr GetChain() const = 0; // Returns a PEM encoded string representation of the certificate. @@ -98,7 +98,7 @@ class SSLCertificate { private: std::unique_ptr GetStats( - std::unique_ptr issuer) const; + std::unique_ptr issuer) const; }; // SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves @@ -229,6 +229,10 @@ class SSLIdentity { static SSLIdentity* FromPEMStrings(const std::string& private_key, const std::string& certificate); + // Construct an identity from a private key and a certificate chain. + static SSLIdentity* FromPEMChainStrings(const std::string& private_key, + const std::string& certificate_chain); + virtual ~SSLIdentity() {} // Returns a new SSLIdentity object instance wrapping the same diff --git a/rtc_base/sslstreamadapter.h b/rtc_base/sslstreamadapter.h index c1b2f3687d..560dd591c0 100644 --- a/rtc_base/sslstreamadapter.h +++ b/rtc_base/sslstreamadapter.h @@ -58,8 +58,9 @@ int SrtpCryptoSuiteFromName(const std::string& crypto_suite); // Get key length and salt length for given crypto suite. Returns true for // valid suites, otherwise false. -bool GetSrtpKeyAndSaltLengths(int crypto_suite, int *key_length, - int *salt_length); +bool GetSrtpKeyAndSaltLengths(int crypto_suite, + int* key_length, + int* salt_length); // Returns true if the given crypto suite id uses a GCM cipher. bool IsGcmCryptoSuite(int crypto_suite); @@ -103,7 +104,6 @@ std::vector GetSupportedDtlsSrtpCryptoSuites( // for doing this are in SSLAdapter. They should possibly be moved out // to a neutral class. - enum SSLRole { SSL_CLIENT, SSL_SERVER }; enum SSLMode { SSL_MODE_TLS, SSL_MODE_DTLS }; enum SSLProtocolVersion { @@ -202,10 +202,13 @@ class SSLStreamAdapter : public StreamAdapterInterface { SSLPeerCertificateDigestError* error = nullptr) = 0; // Retrieves the peer's X.509 certificate, if a connection has been - // established. It returns the transmitted over SSL, including the entire - // chain. + // established. virtual std::unique_ptr GetPeerCertificate() const = 0; + // Retrieves the peer's certificate chain including leaf, if a + // connection has been established. + virtual std::unique_ptr GetPeerSSLCertChain() const = 0; + // Retrieves the IANA registration id of the cipher suite used for the // connection (e.g. 0x2F for "TLS_RSA_WITH_AES_128_CBC_SHA"). virtual bool GetSslCipherSuite(int* cipher_suite); diff --git a/rtc_base/sslstreamadapter_unittest.cc b/rtc_base/sslstreamadapter_unittest.cc index 2a3af854e7..c4962e8ad7 100644 --- a/rtc_base/sslstreamadapter_unittest.cc +++ b/rtc_base/sslstreamadapter_unittest.cc @@ -64,6 +64,71 @@ static const char kCERT_PEM[] = "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" "-----END CERTIFICATE-----\n"; +static const char kIntCert1[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIEUjCCAjqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBljELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS\n" + "BgNVBAoMC0dvb2dsZSwgSW5jMQwwCgYDVQQLDANHVFAxFzAVBgNVBAMMDnRlbGVw\n" + "aG9ueS5nb29nMR0wGwYJKoZIhvcNAQkBFg5ndHBAZ29vZ2xlLmNvbTAeFw0xNzA5\n" + "MjYwNDA5MDNaFw0yMDA2MjIwNDA5MDNaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI\n" + "DAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEXMBUGA1UECgwOdGVsZXBob255\n" + "Lmdvb2cxFzAVBgNVBAMMDnRlbGVwaG9ueS5nb29nMIGfMA0GCSqGSIb3DQEBAQUA\n" + "A4GNADCBiQKBgQDJXWeeU1v1+wlqkVobzI3aN7Uh2iVQA9YCdq5suuabtiD/qoOD\n" + "NKpmQqsx7WZGGWSZTDFEBaUpvIK7Hb+nzRqk6iioPCFOFuarm6GxO1xVneImMuE6\n" + "tuWb3YZPr+ikChJbl11y5UcSbg0QsbeUc+jHl5umNvrL85Y+z8SP0rxbBwIDAQAB\n" + "o2AwXjAdBgNVHQ4EFgQU7tdZobqlN8R8V72FQnRxmqq8tKswHwYDVR0jBBgwFoAU\n" + "5GgKMUtcxkQ2dJrtNR5YOlIAPDswDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMC\n" + "AQYwDQYJKoZIhvcNAQELBQADggIBADObh9Z+z14FmP9zSenhFtq7hFnmNrSkklk8\n" + "eyYWXKfOuIriEQQBZsz76ZcnzStih8Rj+yQ0AXydk4fJ5LOwC2cUqQBar17g6Pd2\n" + "8g4SIL4azR9WvtiSvpuGlwp25b+yunaacDne6ebnf/MUiiKT5w61Xo3cEPVfl38e\n" + "/Up2l0bioid5enUTmg6LY6RxDO6tnZQkz3XD+nNSwT4ehtkqFpHYWjErj0BbkDM2\n" + "hiVc/JsYOZn3DmuOlHVHU6sKwqh3JEyvHO/d7DGzMGWHpHwv2mCTJq6l/sR95Tc2\n" + "GaQZgGDVNs9pdEouJCDm9e/PbQWRYhnat82PTkXx/6mDAAwdZlIi/pACzq8K4p7e\n" + "6hF0t8uKGnXJubHPXxlnJU6yxZ0yWmivAGjwWK4ur832gKlho4jeMDhiI/T3QPpl\n" + "iMNsIvxRhdD+GxJkQP1ezayw8s+Uc9KwKglrkBSRRDLCJUfPOvMmXLUDSTMX7kp4\n" + "/Ak1CA8dVLJIlfEjLBUuvAttlP7+7lsKNgxAjCxZkWLXIyGULzNPQwVWkGfCbrQs\n" + "XyMvSbFsSIb7blV7eLlmf9a+2RprUUkc2ALXLLCI9YQXmxm2beBfMyNmmebwBJzT\n" + "B0OR+5pFFNTJPoNlqpdrDsGrDu7JlUtk0ZLZzYyKXbgy2qXxfd4OWzXXjxpLMszZ\n" + "LDIpOAkj\n" + "-----END CERTIFICATE-----\n"; + +static const char kCACert[] = + "-----BEGIN CERTIFICATE-----\n" + "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\n" + "dGaXBy+qbVXVyGXaeEbIRjtJ6m92\n" + "-----END CERTIFICATE-----\n"; + class SSLStreamAdapterTestBase; class SSLDummyStreamBase : public rtc::StreamInterface, @@ -865,6 +930,36 @@ class SSLStreamAdapterTestDTLSFromPEMStrings : public SSLStreamAdapterTestDTLS { } }; +// Test fixture for certificate chaining. Server will push more than one +// certificate. +class SSLStreamAdapterTestDTLSCertChain : public SSLStreamAdapterTestDTLS { + public: + SSLStreamAdapterTestDTLSCertChain() : SSLStreamAdapterTestDTLS("", ""){}; + void SetUp() override { + CreateStreams(); + + client_ssl_.reset(rtc::SSLStreamAdapter::Create(client_stream_)); + server_ssl_.reset(rtc::SSLStreamAdapter::Create(server_stream_)); + + // Set up the slots + client_ssl_->SignalEvent.connect( + reinterpret_cast(this), + &SSLStreamAdapterTestBase::OnEvent); + server_ssl_->SignalEvent.connect( + reinterpret_cast(this), + &SSLStreamAdapterTestBase::OnEvent); + + if (!client_cert_pem_.empty() && !client_private_key_pem_.empty()) { + client_identity_ = rtc::SSLIdentity::FromPEMStrings( + client_private_key_pem_, client_cert_pem_); + } else { + client_identity_ = rtc::SSLIdentity::Generate("client", client_key_type_); + } + + client_ssl_->SetIdentity(client_identity_); + } +}; + // Basic tests: TLS // Test that we can make a handshake work @@ -872,6 +967,44 @@ TEST_P(SSLStreamAdapterTestTLS, TestTLSConnect) { TestHandshake(); }; +TEST_P(SSLStreamAdapterTestTLS, GetPeerCertChainWithOneCertificate) { + TestHandshake(); + std::unique_ptr cert_chain = + client_ssl_->GetPeerSSLCertChain(); + std::unique_ptr certificate = + client_ssl_->GetPeerCertificate(); + ASSERT_NE(nullptr, cert_chain); + EXPECT_EQ(1u, cert_chain->GetSize()); + EXPECT_EQ(cert_chain->Get(0).ToPEMString(), certificate->ToPEMString()); +} + +TEST_F(SSLStreamAdapterTestDTLSCertChain, TwoCertHandshake) { + server_identity_ = rtc::SSLIdentity::FromPEMChainStrings( + kRSA_PRIVATE_KEY_PEM, std::string(kCERT_PEM) + kCACert); + server_ssl_->SetIdentity(server_identity_); + TestHandshake(); + std::unique_ptr peer_cert_chain = + client_ssl_->GetPeerSSLCertChain(); + ASSERT_NE(nullptr, peer_cert_chain); + ASSERT_EQ(2u, peer_cert_chain->GetSize()); + EXPECT_EQ(kCERT_PEM, peer_cert_chain->Get(0).ToPEMString()); + EXPECT_EQ(kCACert, peer_cert_chain->Get(1).ToPEMString()); +} + +TEST_F(SSLStreamAdapterTestDTLSCertChain, ThreeCertHandshake) { + server_identity_ = rtc::SSLIdentity::FromPEMChainStrings( + kRSA_PRIVATE_KEY_PEM, std::string(kCERT_PEM) + kIntCert1 + kCACert); + server_ssl_->SetIdentity(server_identity_); + TestHandshake(); + std::unique_ptr peer_cert_chain = + client_ssl_->GetPeerSSLCertChain(); + ASSERT_NE(nullptr, peer_cert_chain); + ASSERT_EQ(3u, peer_cert_chain->GetSize()); + EXPECT_EQ(kCERT_PEM, peer_cert_chain->Get(0).ToPEMString()); + EXPECT_EQ(kIntCert1, peer_cert_chain->Get(1).ToPEMString()); + EXPECT_EQ(kCACert, peer_cert_chain->Get(2).ToPEMString()); +} + // Test that closing the connection on one side updates the other side. TEST_P(SSLStreamAdapterTestTLS, TestTLSClose) { TestHandshake();