From e29352bb34de60bd0a56d4ce46c2ce35ac2b27b4 Mon Sep 17 00:00:00 2001 From: hbos Date: Thu, 25 Aug 2016 03:52:38 -0700 Subject: [PATCH] Refactor certificate stats collection, added SSLCertificateStats. The code that extracts certificate stats from an SSLCertificate and its certificate chain is moved into SSLCertificate::GetStats. The stats collector code loops through the resulting SSLCertificateStats and creates the StatsReports for those stats. This will allow the new stats collector to reuse GetStats in a future CL. BUG=chromium:627816, chromium:629436 Review-Url: https://codereview.webrtc.org/2259283002 Cr-Commit-Position: refs/heads/master@{#13917} --- webrtc/api/statscollector.cc | 83 +++------ webrtc/api/statscollector.h | 6 +- webrtc/base/sslidentity.cc | 65 +++++++ webrtc/base/sslidentity.h | 21 +++ webrtc/base/sslidentity_unittest.cc | 265 ++++++++++++++++++++-------- 5 files changed, 300 insertions(+), 140 deletions(-) diff --git a/webrtc/api/statscollector.cc b/webrtc/api/statscollector.cc index ab191d15e5..ffe241568b 100644 --- a/webrtc/api/statscollector.cc +++ b/webrtc/api/statscollector.cc @@ -536,73 +536,34 @@ StatsReport* StatsCollector::PrepareReport( return report; } -StatsReport* StatsCollector::AddOneCertificateReport( - const rtc::SSLCertificate* cert, const StatsReport* issuer) { - RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); - - // TODO(bemasc): Move this computation to a helper class that caches these - // values to reduce CPU use in GetStats. This will require adding a fast - // SSLCertificate::Equals() method to detect certificate changes. - - std::string digest_algorithm; - if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm)) - return nullptr; - - std::unique_ptr ssl_fingerprint( - rtc::SSLFingerprint::Create(digest_algorithm, cert)); - - // SSLFingerprint::Create can fail if the algorithm returned by - // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the - // implementation of SSLCertificate::ComputeDigest. This currently happens - // with MD5- and SHA-224-signed certificates when linked to libNSS. - if (!ssl_fingerprint) - return nullptr; - - std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); - - rtc::Buffer der_buffer; - cert->ToDER(&der_buffer); - std::string der_base64; - rtc::Base64::EncodeFromArray(der_buffer.data(), der_buffer.size(), - &der_base64); - - StatsReport::Id id(StatsReport::NewTypedId( - StatsReport::kStatsReportTypeCertificate, fingerprint)); - StatsReport* report = reports_.ReplaceOrAddNew(id); - report->set_timestamp(stats_gathering_started_); - report->AddString(StatsReport::kStatsValueNameFingerprint, fingerprint); - report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm, - digest_algorithm); - report->AddString(StatsReport::kStatsValueNameDer, der_base64); - if (issuer) - report->AddId(StatsReport::kStatsValueNameIssuerId, issuer->id()); - return report; -} - StatsReport* StatsCollector::AddCertificateReports( const rtc::SSLCertificate* cert) { RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); - // Produces a chain of StatsReports representing this certificate and the rest - // of its chain, and adds those reports to |reports_|. The return value is - // the id of the leaf report. The provided cert must be non-null, so at least - // one report will always be provided and the returned string will never be - // empty. RTC_DCHECK(cert != NULL); - StatsReport* issuer = nullptr; - std::unique_ptr chain = cert->GetChain(); - if (chain) { - // This loop runs in reverse, i.e. from root to leaf, so that each - // certificate's issuer's report ID is known before the child certificate's - // report is generated. The root certificate does not have an issuer ID - // value. - for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) { - const rtc::SSLCertificate& cert_i = chain->Get(i); - issuer = AddOneCertificateReport(&cert_i, issuer); - } + std::unique_ptr first_stats = cert->GetStats(); + StatsReport* first_report = nullptr; + StatsReport* prev_report = nullptr; + for (rtc::SSLCertificateStats* stats = first_stats.get(); stats; + stats = stats->issuer.get()) { + StatsReport::Id id(StatsReport::NewTypedId( + StatsReport::kStatsReportTypeCertificate, stats->fingerprint)); + + StatsReport* report = reports_.ReplaceOrAddNew(id); + report->set_timestamp(stats_gathering_started_); + report->AddString(StatsReport::kStatsValueNameFingerprint, + stats->fingerprint); + report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm, + stats->fingerprint_algorithm); + report->AddString(StatsReport::kStatsValueNameDer, + stats->base64_certificate); + if (!first_report) + first_report = report; + else + prev_report->AddId(StatsReport::kStatsValueNameIssuerId, id); + prev_report = report; } - // Add the leaf certificate. - return AddOneCertificateReport(cert, issuer); + return first_report; } StatsReport* StatsCollector::AddConnectionInfoReport( diff --git a/webrtc/api/statscollector.h b/webrtc/api/statscollector.h index 8c359e48c4..adbac44b80 100644 --- a/webrtc/api/statscollector.h +++ b/webrtc/api/statscollector.h @@ -91,17 +91,13 @@ class StatsCollector { bool CopySelectedReports(const std::string& selector, StatsReports* reports); - // Helper method for AddCertificateReports. - StatsReport* AddOneCertificateReport( - const rtc::SSLCertificate* cert, const StatsReport* issuer); - // Helper method for creating IceCandidate report. |is_local| indicates // whether this candidate is local or remote. StatsReport* AddCandidateReport(const cricket::Candidate& candidate, bool local); // Adds a report for this certificate and every certificate in its chain, and - // returns the leaf certificate's report. + // returns the leaf certificate's report (|cert|'s report). StatsReport* AddCertificateReports(const rtc::SSLCertificate* cert); StatsReport* AddConnectionInfoReport(const std::string& content_name, diff --git a/webrtc/base/sslidentity.cc b/webrtc/base/sslidentity.cc index 5f3a73fad1..090428dd35 100644 --- a/webrtc/base/sslidentity.cc +++ b/webrtc/base/sslidentity.cc @@ -18,6 +18,7 @@ #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/base/sslconfig.h" +#include "webrtc/base/sslfingerprint.h" #if SSL_USE_OPENSSL @@ -31,6 +32,70 @@ const char kPemTypeCertificate[] = "CERTIFICATE"; const char kPemTypeRsaPrivateKey[] = "RSA PRIVATE KEY"; const char kPemTypeEcPrivateKey[] = "EC PRIVATE KEY"; +SSLCertificateStats::SSLCertificateStats( + std::string&& fingerprint, + std::string&& fingerprint_algorithm, + std::string&& base64_certificate, + std::unique_ptr&& issuer) + : fingerprint(std::move(fingerprint)), + fingerprint_algorithm(std::move(fingerprint_algorithm)), + base64_certificate(std::move(base64_certificate)), + issuer(std::move(issuer)) { +} + +SSLCertificateStats::~SSLCertificateStats() { +} + +std::unique_ptr SSLCertificate::GetStats() const { + // We have a certificate and optionally a chain of certificates. This forms a + // linked list, starting with |this|, then the first element of |chain| and + // ending with the last element of |chain|. The "issuer" of a certificate is + // the next certificate in the chain. Stats are produced for each certificate + // in the list. Here, the "issuer" is the issuer's stats. + std::unique_ptr chain = GetChain(); + std::unique_ptr issuer; + if (chain) { + // The loop runs in reverse so that the |issuer| is known before the + // |cert|'s stats. + for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) { + const SSLCertificate* cert = &chain->Get(i); + issuer = cert->GetStats(std::move(issuer)); + } + } + return GetStats(std::move(issuer)); +} + +std::unique_ptr SSLCertificate::GetStats( + std::unique_ptr issuer) const { + // TODO(bemasc): Move this computation to a helper class that caches these + // values to reduce CPU use in |StatsCollector::GetStats|. This will require + // adding a fast |SSLCertificate::Equals| to detect certificate changes. + std::string digest_algorithm; + if (!GetSignatureDigestAlgorithm(&digest_algorithm)) + return nullptr; + + // |SSLFingerprint::Create| can fail if the algorithm returned by + // |SSLCertificate::GetSignatureDigestAlgorithm| is not supported by the + // implementation of |SSLCertificate::ComputeDigest|. This currently happens + // with MD5- and SHA-224-signed certificates when linked to libNSS. + std::unique_ptr ssl_fingerprint( + SSLFingerprint::Create(digest_algorithm, this)); + if (!ssl_fingerprint) + return nullptr; + std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); + + Buffer der_buffer; + ToDER(&der_buffer); + std::string der_base64; + Base64::EncodeFromArray(der_buffer.data(), der_buffer.size(), &der_base64); + + return std::unique_ptr(new SSLCertificateStats( + std::move(fingerprint), + std::move(digest_algorithm), + std::move(der_base64), + std::move(issuer))); +} + KeyParams::KeyParams(KeyType key_type) { if (key_type == KT_ECDSA) { type_ = KT_ECDSA; diff --git a/webrtc/base/sslidentity.h b/webrtc/base/sslidentity.h index 857a27cfc7..181174b265 100644 --- a/webrtc/base/sslidentity.h +++ b/webrtc/base/sslidentity.h @@ -28,6 +28,18 @@ namespace rtc { // Forward declaration due to circular dependency with SSLCertificate. class SSLCertChain; +struct SSLCertificateStats { + SSLCertificateStats(std::string&& fingerprint, + std::string&& fingerprint_algorithm, + std::string&& base64_certificate, + std::unique_ptr&& issuer); + ~SSLCertificateStats(); + std::string fingerprint; + std::string fingerprint_algorithm; + std::string base64_certificate; + std::unique_ptr issuer; +}; + // Abstract interface overridden by SSL library specific // implementations. @@ -75,6 +87,15 @@ class SSLCertificate { // Returns the time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC), // or -1 if an expiration time could not be retrieved. virtual int64_t CertificateExpirationTime() const = 0; + + // Gets information (fingerprint, etc.) about this certificate and its chain + // (if it has a certificate chain). This is used for certificate stats, see + // https://w3c.github.io/webrtc-stats/#certificatestats-dict*. + std::unique_ptr GetStats() const; + + private: + std::unique_ptr GetStats( + std::unique_ptr issuer) const; }; // SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves diff --git a/webrtc/base/sslidentity_unittest.cc b/webrtc/base/sslidentity_unittest.cc index 399fe9d474..5c881c2371 100644 --- a/webrtc/base/sslidentity_unittest.cc +++ b/webrtc/base/sslidentity_unittest.cc @@ -11,10 +11,13 @@ #include #include +#include "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/helpers.h" #include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslfingerprint.h" #include "webrtc/base/sslidentity.h" +#include "webrtc/base/stringutils.h" using rtc::SSLIdentity; @@ -63,6 +66,145 @@ const unsigned char kTestCertSha512[] = { 0xf6, 0xa5, 0xda, 0xdc, 0x42, 0x42, 0x22, 0x35, 0xce, 0x26, 0x58, 0x4a, 0x33, 0x6d, 0xbc, 0xb6}; +// These PEM strings were created by generating an identity with +// |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|, +// |identity->PublicKeyToPEMString()| and +// |identity->certificate().ToPEMString()|. If the crypto library is updated, +// and the update changes the string form of the keys, these will have to be +// updated too. The fingerprint, fingerprint algorithm and base64 certificate +// were created by calling |identity->certificate().GetStats()|. +static const char kRSA_PRIVATE_KEY_PEM[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMQPqDStRlYeDpkX\n" + "erRmv+a1naM8vSVSY0gG2plnrnofViWRW3MRqWC+020MsIj3hPZeSAnt/y/FL/nr\n" + "4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHwcFcMr31HLFvHr0ZgcFwbgIuFLNEl\n" + "7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAECgYA8FWBC5GcNtSBcIinkZyigF0A7\n" + "6j081sa+J/uNz4xUuI257ZXM6biygUhhvuXK06/XoIULJfhyN0fAm1yb0HtNhiUs\n" + "kMOYeon6b8FqFaPjrQf7Gr9FMiIHXNK19uegTMKztXyPZoUWlX84X0iawY95x0Y3\n" + "73f6P2rN2UOjlVVjAQJBAOKy3l2w3Zj2w0oAJox0eMwl+RxBNt1C42SHrob2mFUT\n" + "rytpVVYOasr8CoDI0kjacjI94sLum+buJoXXX6YTGO0CQQDdZwlYIEkoS3ftfxPa\n" + "Ai0YTBzAWvHJg0r8Gk/TkHo6IM+LSsZ9ZYUv/vBe4BKLw1I4hZ+bQvBiq+f8ROtk\n" + "+TDRAkAPL3ghwoU1h+IRBO2QHwUwd6K2N9AbBi4BP+168O3HVSg4ujeTKigRLMzv\n" + "T4R2iNt5bhfQgvdCgtVlxcWMdF8JAkBwDCg3eEdt5BuyjwBt8XH+/O4ED0KUWCTH\n" + "x00k5dZlupsuhE5Fwe4QpzXg3gekwdnHjyCCQ/NCDHvgOMTkmhQxAkA9V03KRX9b\n" + "bhvEzY/fu8gEp+EzsER96/D79az5z1BaMGL5OPM2xHBPJATKlswnAa7Lp3QKGZGk\n" + "TxslfL18J71s\n" + "-----END PRIVATE KEY-----\n"; +static const char kRSA_PUBLIC_KEY_PEM[] = + "-----BEGIN PUBLIC KEY-----\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDED6g0rUZWHg6ZF3q0Zr/mtZ2j\n" + "PL0lUmNIBtqZZ656H1YlkVtzEalgvtNtDLCI94T2XkgJ7f8vxS/56+BGuzV3MHUa\n" + "Nf9cRCu1P3/3I0oNWrp8rxwh8HBXDK99Ryxbx69GYHBcG4CLhSzRJe5CuRzDvQD8\n" + "9Z7VI3pPAZgY/MjJfQIDAQAB\n" + "-----END PUBLIC KEY-----\n"; +static const char kRSA_CERT_PEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBnDCCAQWgAwIBAgIJAOEHLgeWYwrpMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV\n" + "BAMMBXRlc3QxMB4XDTE2MDQyNDE4MTAyMloXDTE2MDUyNTE4MTAyMlowEDEOMAwG\n" + "A1UEAwwFdGVzdDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMQPqDStRlYe\n" + "DpkXerRmv+a1naM8vSVSY0gG2plnrnofViWRW3MRqWC+020MsIj3hPZeSAnt/y/F\n" + "L/nr4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHwcFcMr31HLFvHr0ZgcFwbgIuF\n" + "LNEl7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAEwDQYJKoZIhvcNAQELBQADgYEA\n" + "C3ehaZFl+oEYN069C2ht/gMzuC77L854RF/x7xRtNZzkcg9TVgXXdM3auUvJi8dx\n" + "yTpU3ixErjQvoZew5ngXTEvTY8BSQUijJEaLWh8n6NDKRbEGTdAk8nPAmq9hdCFq\n" + "e3UkexqNHm3g/VxG4NUC1Y+w29ai0/Rgh+VvgbDwK+Q=\n" + "-----END CERTIFICATE-----\n"; +static const char kRSA_FINGERPRINT[] = + "3C:E8:B2:70:09:CF:A9:09:5A:F4:EF:8F:8D:8A:32:FF:EA:04:91:BA:6E:D4:17:78:16" + ":2A:EE:F9:9A:DD:E2:2B"; +static const char kRSA_FINGERPRINT_ALGORITHM[] = + "sha-256"; +static const char kRSA_BASE64_CERTIFICATE[] = + "MIIBnDCCAQWgAwIBAgIJAOEHLgeWYwrpMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNVBAMMBXRlc3" + "QxMB4XDTE2MDQyNDE4MTAyMloXDTE2MDUyNTE4MTAyMlowEDEOMAwGA1UEAwwFdGVzdDEwgZ8w" + "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMQPqDStRlYeDpkXerRmv+a1naM8vSVSY0gG2plnrn" + "ofViWRW3MRqWC+020MsIj3hPZeSAnt/y/FL/nr4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHw" + "cFcMr31HLFvHr0ZgcFwbgIuFLNEl7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAEwDQYJKoZIhv" + "cNAQELBQADgYEAC3ehaZFl+oEYN069C2ht/gMzuC77L854RF/x7xRtNZzkcg9TVgXXdM3auUvJ" + "i8dxyTpU3ixErjQvoZew5ngXTEvTY8BSQUijJEaLWh8n6NDKRbEGTdAk8nPAmq9hdCFqe3Ukex" + "qNHm3g/VxG4NUC1Y+w29ai0/Rgh+VvgbDwK+Q="; + +static const char kECDSA_PRIVATE_KEY_PEM[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/AkEA2hklq7dQ2rN\n" + "ZxYL6hOUACL4pn7P4FYlA3ZQhIChRANCAAR7YgdO3utP/8IqVRq8G4VZKreMAxeN\n" + "rUa12twthv4uFjuHAHa9D9oyAjncmn+xvZZRyVmKrA56jRzENcEEHoAg\n" + "-----END PRIVATE KEY-----\n"; +static const char kECDSA_PUBLIC_KEY_PEM[] = + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEe2IHTt7rT//CKlUavBuFWSq3jAMX\n" + "ja1GtdrcLYb+LhY7hwB2vQ/aMgI53Jp/sb2WUclZiqwOeo0cxDXBBB6AIA==\n" + "-----END PUBLIC KEY-----\n"; +static const char kECDSA_CERT_PEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBFDCBu6ADAgECAgkArpkxjw62sW4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF\n" + "dGVzdDMwHhcNMTYwNDI0MTgxNDM4WhcNMTYwNTI1MTgxNDM4WjAQMQ4wDAYDVQQD\n" + "DAV0ZXN0MzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHtiB07e60//wipVGrwb\n" + "hVkqt4wDF42tRrXa3C2G/i4WO4cAdr0P2jICOdyaf7G9llHJWYqsDnqNHMQ1wQQe\n" + "gCAwCgYIKoZIzj0EAwIDSAAwRQIhANyreQ/K5yuPPpirsd0e/4WGLHou6bIOSQks\n" + "DYzo56NmAiAKOr3u8ol3LmygbUCwEvtWrS8QcJDygxHPACo99hkekw==\n" + "-----END CERTIFICATE-----\n"; +static const char kECDSA_FINGERPRINT[] = + "9F:47:FA:88:76:3D:18:B8:00:A0:59:9D:C3:5D:34:0B:1F:B8:99:9E:68:DA:F3:A5:DA" + ":50:33:A9:FF:4D:31:89"; +static const char kECDSA_FINGERPRINT_ALGORITHM[] = + "sha-256"; +static const char kECDSA_BASE64_CERTIFICATE[] = + "MIIBFDCBu6ADAgECAgkArpkxjw62sW4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwFdGVzdDMwHh" + "cNMTYwNDI0MTgxNDM4WhcNMTYwNTI1MTgxNDM4WjAQMQ4wDAYDVQQDDAV0ZXN0MzBZMBMGByqG" + "SM49AgEGCCqGSM49AwEHA0IABHtiB07e60//wipVGrwbhVkqt4wDF42tRrXa3C2G/i4WO4cAdr" + "0P2jICOdyaf7G9llHJWYqsDnqNHMQ1wQQegCAwCgYIKoZIzj0EAwIDSAAwRQIhANyreQ/K5yuP" + "Ppirsd0e/4WGLHou6bIOSQksDYzo56NmAiAKOr3u8ol3LmygbUCwEvtWrS8QcJDygxHPACo99h" + "kekw=="; + +struct IdentityAndInfo { + std::unique_ptr identity; + std::vector ders; + std::vector pems; + std::vector fingerprints; +}; + +IdentityAndInfo CreateFakeIdentityAndInfoFromDers( + const std::vector& ders) { + RTC_CHECK(!ders.empty()); + IdentityAndInfo info; + info.ders = ders; + for (const std::string& der : ders) { + info.pems.push_back(rtc::SSLIdentity::DerToPem( + "CERTIFICATE", + reinterpret_cast(der.c_str()), + der.length())); + } + info.identity.reset( + new rtc::FakeSSLIdentity(rtc::FakeSSLCertificate(info.pems))); + // Strip header/footer and newline characters of PEM strings. + for (size_t i = 0; i < info.pems.size(); ++i) { + rtc::replace_substrs("-----BEGIN CERTIFICATE-----", 27, + "", 0, &info.pems[i]); + rtc::replace_substrs("-----END CERTIFICATE-----", 25, + "", 0, &info.pems[i]); + rtc::replace_substrs("\n", 1, + "", 0, &info.pems[i]); + } + // Fingerprint of leaf certificate. + std::unique_ptr fp( + rtc::SSLFingerprint::Create("sha-1", &info.identity->certificate())); + EXPECT_TRUE(fp); + info.fingerprints.push_back(fp->GetRfc4572Fingerprint()); + // Fingerprints of the rest of the chain. + std::unique_ptr chain = + info.identity->certificate().GetChain(); + if (chain) { + for (size_t i = 0; i < chain->GetSize(); i++) { + fp.reset(rtc::SSLFingerprint::Create("sha-1", &chain->Get(i))); + EXPECT_TRUE(fp); + info.fingerprints.push_back(fp->GetRfc4572Fingerprint()); + } + } + EXPECT_EQ(info.ders.size(), info.fingerprints.size()); + return info; +} + class SSLIdentityTest : public testing::Test { public: SSLIdentityTest() {} @@ -266,91 +408,21 @@ TEST_F(SSLIdentityTest, IdentityComparison) { } TEST_F(SSLIdentityTest, FromPEMStringsRSA) { - // These PEM strings were created by generating an identity with - // |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|, - // |identity->PublicKeyToPEMString()| and - // |identity->certificate().ToPEMString()|. If the crypto library is updated, - // and the update changes the string form of the keys, these will have to be - // updated too. - static const char kRSA_PRIVATE_KEY_PEM[] = - "-----BEGIN PRIVATE KEY-----\n" - "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMQPqDStRlYeDpkX\n" - "erRmv+a1naM8vSVSY0gG2plnrnofViWRW3MRqWC+020MsIj3hPZeSAnt/y/FL/nr\n" - "4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHwcFcMr31HLFvHr0ZgcFwbgIuFLNEl\n" - "7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAECgYA8FWBC5GcNtSBcIinkZyigF0A7\n" - "6j081sa+J/uNz4xUuI257ZXM6biygUhhvuXK06/XoIULJfhyN0fAm1yb0HtNhiUs\n" - "kMOYeon6b8FqFaPjrQf7Gr9FMiIHXNK19uegTMKztXyPZoUWlX84X0iawY95x0Y3\n" - "73f6P2rN2UOjlVVjAQJBAOKy3l2w3Zj2w0oAJox0eMwl+RxBNt1C42SHrob2mFUT\n" - "rytpVVYOasr8CoDI0kjacjI94sLum+buJoXXX6YTGO0CQQDdZwlYIEkoS3ftfxPa\n" - "Ai0YTBzAWvHJg0r8Gk/TkHo6IM+LSsZ9ZYUv/vBe4BKLw1I4hZ+bQvBiq+f8ROtk\n" - "+TDRAkAPL3ghwoU1h+IRBO2QHwUwd6K2N9AbBi4BP+168O3HVSg4ujeTKigRLMzv\n" - "T4R2iNt5bhfQgvdCgtVlxcWMdF8JAkBwDCg3eEdt5BuyjwBt8XH+/O4ED0KUWCTH\n" - "x00k5dZlupsuhE5Fwe4QpzXg3gekwdnHjyCCQ/NCDHvgOMTkmhQxAkA9V03KRX9b\n" - "bhvEzY/fu8gEp+EzsER96/D79az5z1BaMGL5OPM2xHBPJATKlswnAa7Lp3QKGZGk\n" - "TxslfL18J71s\n" - "-----END PRIVATE KEY-----\n"; - static const char kRSA_PUBLIC_KEY_PEM[] = - "-----BEGIN PUBLIC KEY-----\n" - "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDED6g0rUZWHg6ZF3q0Zr/mtZ2j\n" - "PL0lUmNIBtqZZ656H1YlkVtzEalgvtNtDLCI94T2XkgJ7f8vxS/56+BGuzV3MHUa\n" - "Nf9cRCu1P3/3I0oNWrp8rxwh8HBXDK99Ryxbx69GYHBcG4CLhSzRJe5CuRzDvQD8\n" - "9Z7VI3pPAZgY/MjJfQIDAQAB\n" - "-----END PUBLIC KEY-----\n"; - static const char kCERT_PEM[] = - "-----BEGIN CERTIFICATE-----\n" - "MIIBnDCCAQWgAwIBAgIJAOEHLgeWYwrpMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV\n" - "BAMMBXRlc3QxMB4XDTE2MDQyNDE4MTAyMloXDTE2MDUyNTE4MTAyMlowEDEOMAwG\n" - "A1UEAwwFdGVzdDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMQPqDStRlYe\n" - "DpkXerRmv+a1naM8vSVSY0gG2plnrnofViWRW3MRqWC+020MsIj3hPZeSAnt/y/F\n" - "L/nr4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHwcFcMr31HLFvHr0ZgcFwbgIuF\n" - "LNEl7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAEwDQYJKoZIhvcNAQELBQADgYEA\n" - "C3ehaZFl+oEYN069C2ht/gMzuC77L854RF/x7xRtNZzkcg9TVgXXdM3auUvJi8dx\n" - "yTpU3ixErjQvoZew5ngXTEvTY8BSQUijJEaLWh8n6NDKRbEGTdAk8nPAmq9hdCFq\n" - "e3UkexqNHm3g/VxG4NUC1Y+w29ai0/Rgh+VvgbDwK+Q=\n" - "-----END CERTIFICATE-----\n"; - std::unique_ptr identity( - SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM)); + SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kRSA_CERT_PEM)); EXPECT_TRUE(identity); EXPECT_EQ(kRSA_PRIVATE_KEY_PEM, identity->PrivateKeyToPEMString()); EXPECT_EQ(kRSA_PUBLIC_KEY_PEM, identity->PublicKeyToPEMString()); - EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString()); + EXPECT_EQ(kRSA_CERT_PEM, identity->certificate().ToPEMString()); } TEST_F(SSLIdentityTest, FromPEMStringsEC) { - // These PEM strings were created by generating an identity with - // |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|, - // |identity->PublicKeyToPEMString()| and - // |identity->certificate().ToPEMString()|. If the crypto library is updated, - // and the update changes the string form of the keys, these will have to be - // updated too. - static const char kECDSA_PRIVATE_KEY_PEM[] = - "-----BEGIN PRIVATE KEY-----\n" - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/AkEA2hklq7dQ2rN\n" - "ZxYL6hOUACL4pn7P4FYlA3ZQhIChRANCAAR7YgdO3utP/8IqVRq8G4VZKreMAxeN\n" - "rUa12twthv4uFjuHAHa9D9oyAjncmn+xvZZRyVmKrA56jRzENcEEHoAg\n" - "-----END PRIVATE KEY-----\n"; - static const char kECDSA_PUBLIC_KEY_PEM[] = - "-----BEGIN PUBLIC KEY-----\n" - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEe2IHTt7rT//CKlUavBuFWSq3jAMX\n" - "ja1GtdrcLYb+LhY7hwB2vQ/aMgI53Jp/sb2WUclZiqwOeo0cxDXBBB6AIA==\n" - "-----END PUBLIC KEY-----\n"; - static const char kCERT_PEM[] = - "-----BEGIN CERTIFICATE-----\n" - "MIIBFDCBu6ADAgECAgkArpkxjw62sW4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF\n" - "dGVzdDMwHhcNMTYwNDI0MTgxNDM4WhcNMTYwNTI1MTgxNDM4WjAQMQ4wDAYDVQQD\n" - "DAV0ZXN0MzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHtiB07e60//wipVGrwb\n" - "hVkqt4wDF42tRrXa3C2G/i4WO4cAdr0P2jICOdyaf7G9llHJWYqsDnqNHMQ1wQQe\n" - "gCAwCgYIKoZIzj0EAwIDSAAwRQIhANyreQ/K5yuPPpirsd0e/4WGLHou6bIOSQks\n" - "DYzo56NmAiAKOr3u8ol3LmygbUCwEvtWrS8QcJDygxHPACo99hkekw==\n" - "-----END CERTIFICATE-----\n"; - std::unique_ptr identity( - SSLIdentity::FromPEMStrings(kECDSA_PRIVATE_KEY_PEM, kCERT_PEM)); + SSLIdentity::FromPEMStrings(kECDSA_PRIVATE_KEY_PEM, kECDSA_CERT_PEM)); EXPECT_TRUE(identity); EXPECT_EQ(kECDSA_PRIVATE_KEY_PEM, identity->PrivateKeyToPEMString()); EXPECT_EQ(kECDSA_PUBLIC_KEY_PEM, identity->PublicKeyToPEMString()); - EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString()); + EXPECT_EQ(kECDSA_CERT_PEM, identity->certificate().ToPEMString()); } TEST_F(SSLIdentityTest, CloneIdentityRSA) { @@ -376,6 +448,51 @@ TEST_F(SSLIdentityTest, GetSignatureDigestAlgorithm) { TestGetSignatureDigestAlgorithm(); } +TEST_F(SSLIdentityTest, SSLCertificateGetStatsRSA) { + std::unique_ptr identity( + SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kRSA_CERT_PEM)); + std::unique_ptr stats = + identity->certificate().GetStats(); + EXPECT_EQ(stats->fingerprint, kRSA_FINGERPRINT); + EXPECT_EQ(stats->fingerprint_algorithm, kRSA_FINGERPRINT_ALGORITHM); + EXPECT_EQ(stats->base64_certificate, kRSA_BASE64_CERTIFICATE); + EXPECT_FALSE(stats->issuer); +} + +TEST_F(SSLIdentityTest, SSLCertificateGetStatsECDSA) { + std::unique_ptr identity( + SSLIdentity::FromPEMStrings(kECDSA_PRIVATE_KEY_PEM, kECDSA_CERT_PEM)); + std::unique_ptr stats = + identity->certificate().GetStats(); + EXPECT_EQ(stats->fingerprint, kECDSA_FINGERPRINT); + EXPECT_EQ(stats->fingerprint_algorithm, kECDSA_FINGERPRINT_ALGORITHM); + EXPECT_EQ(stats->base64_certificate, kECDSA_BASE64_CERTIFICATE); + EXPECT_FALSE(stats->issuer); +} + +TEST_F(SSLIdentityTest, SSLCertificateGetStatsWithChain) { + std::vector ders; + ders.push_back("every der results in"); + ders.push_back("an identity + certificate"); + ders.push_back("in a certificate chain"); + IdentityAndInfo info = CreateFakeIdentityAndInfoFromDers(ders); + EXPECT_TRUE(info.identity); + EXPECT_EQ(info.ders, ders); + EXPECT_EQ(info.pems.size(), info.ders.size()); + EXPECT_EQ(info.fingerprints.size(), info.ders.size()); + + std::unique_ptr first_stats = + info.identity->certificate().GetStats(); + rtc::SSLCertificateStats* cert_stats = first_stats.get(); + for (size_t i = 0; i < info.ders.size(); ++i) { + EXPECT_EQ(cert_stats->fingerprint, info.fingerprints[i]); + EXPECT_EQ(cert_stats->fingerprint_algorithm, "sha-1"); + EXPECT_EQ(cert_stats->base64_certificate, info.pems[i]); + cert_stats = cert_stats->issuer.get(); + EXPECT_EQ(static_cast(cert_stats), i + 1 < info.ders.size()); + } +} + class SSLIdentityExpirationTest : public testing::Test { public: SSLIdentityExpirationTest() {