diff --git a/webrtc/base/base_tests.gyp b/webrtc/base/base_tests.gyp index 11aea29afb..8248cef9da 100644 --- a/webrtc/base/base_tests.gyp +++ b/webrtc/base/base_tests.gyp @@ -85,7 +85,7 @@ 'ratetracker_unittest.cc', 'referencecountedsingletonfactory_unittest.cc', 'rollingaccumulator_unittest.cc', - 'rtccertificate_unittests.cc', + 'rtccertificate_unittest.cc', 'rtccertificategenerator_unittest.cc', 'scopedptrcollection_unittest.cc', 'sha1digest_unittest.cc', diff --git a/webrtc/base/fakesslidentity.h b/webrtc/base/fakesslidentity.h index 6640b029c9..9f98c4e550 100644 --- a/webrtc/base/fakesslidentity.h +++ b/webrtc/base/fakesslidentity.h @@ -99,6 +99,18 @@ class FakeSSLIdentity : public rtc::SSLIdentity { return new FakeSSLIdentity(*this); } virtual const FakeSSLCertificate& certificate() const { return cert_; } + virtual std::string PrivateKeyToPEMString() const { + RTC_NOTREACHED(); // Not implemented. + return ""; + } + virtual std::string PublicKeyToPEMString() const { + RTC_NOTREACHED(); // Not implemented. + return ""; + } + virtual bool operator==(const SSLIdentity& other) const { + RTC_NOTREACHED(); // Not implemented. + return false; + } private: FakeSSLCertificate cert_; }; diff --git a/webrtc/base/opensslidentity.cc b/webrtc/base/opensslidentity.cc index ec9c00710b..58a0cd8ade 100644 --- a/webrtc/base/opensslidentity.cc +++ b/webrtc/base/opensslidentity.cc @@ -166,6 +166,29 @@ OpenSSLKeyPair* OpenSSLKeyPair::Generate(const KeyParams& key_params) { return new OpenSSLKeyPair(pkey); } +OpenSSLKeyPair* OpenSSLKeyPair::FromPrivateKeyPEMString( + const std::string& pem_string) { + BIO* bio = BIO_new_mem_buf(const_cast(pem_string.c_str()), -1); + if (!bio) { + LOG(LS_ERROR) << "Failed to create a new BIO buffer."; + return nullptr; + } + BIO_set_mem_eof_return(bio, 0); + EVP_PKEY* pkey = + PEM_read_bio_PrivateKey(bio, nullptr, nullptr, const_cast("\0")); + BIO_free(bio); // Frees the BIO, but not the pointed-to string. + if (!pkey) { + LOG(LS_ERROR) << "Failed to create the private key from PEM string."; + return nullptr; + } + if (EVP_PKEY_missing_parameters(pkey) != 0) { + LOG(LS_ERROR) << "The resulting key pair is missing public key parameters."; + EVP_PKEY_free(pkey); + return nullptr; + } + return new OpenSSLKeyPair(pkey); +} + OpenSSLKeyPair::~OpenSSLKeyPair() { EVP_PKEY_free(pkey_); } @@ -183,6 +206,57 @@ void OpenSSLKeyPair::AddReference() { #endif } +std::string OpenSSLKeyPair::PrivateKeyToPEMString() const { + BIO* temp_memory_bio = BIO_new(BIO_s_mem()); + if (!temp_memory_bio) { + LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio"; + RTC_NOTREACHED(); + return ""; + } + if (!PEM_write_bio_PrivateKey( + temp_memory_bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr)) { + LOG_F(LS_ERROR) << "Failed to write private key"; + BIO_free(temp_memory_bio); + RTC_NOTREACHED(); + return ""; + } + BIO_write(temp_memory_bio, "\0", 1); + char* buffer; + BIO_get_mem_data(temp_memory_bio, &buffer); + std::string priv_key_str = buffer; + BIO_free(temp_memory_bio); + return priv_key_str; +} + +std::string OpenSSLKeyPair::PublicKeyToPEMString() const { + BIO* temp_memory_bio = BIO_new(BIO_s_mem()); + if (!temp_memory_bio) { + LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio"; + RTC_NOTREACHED(); + return ""; + } + if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) { + LOG_F(LS_ERROR) << "Failed to write public key"; + BIO_free(temp_memory_bio); + RTC_NOTREACHED(); + return ""; + } + BIO_write(temp_memory_bio, "\0", 1); + char* buffer; + BIO_get_mem_data(temp_memory_bio, &buffer); + std::string pub_key_str = buffer; + BIO_free(temp_memory_bio); + return pub_key_str; +} + +bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const { + return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1; +} + +bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const { + return !(*this == other); +} + #if !defined(NDEBUG) // Print a certificate to the log, for debugging. static void PrintCert(X509* x509) { @@ -370,6 +444,14 @@ void OpenSSLCertificate::AddReference() const { #endif } +bool OpenSSLCertificate::operator==(const OpenSSLCertificate& other) const { + return X509_cmp(this->x509_, other.x509_) == 0; +} + +bool OpenSSLCertificate::operator!=(const OpenSSLCertificate& other) const { + return !(*this == other); +} + // Documented in sslidentity.h. int64_t OpenSSLCertificate::CertificateExpirationTime() const { ASN1_TIME* expire_time = X509_get_notAfter(x509_); @@ -436,25 +518,17 @@ SSLIdentity* OpenSSLIdentity::FromPEMStrings( OpenSSLCertificate::FromPEMString(certificate)); if (!cert) { LOG(LS_ERROR) << "Failed to create OpenSSLCertificate from PEM string."; - return NULL; + return nullptr; } - BIO* bio = BIO_new_mem_buf(const_cast(private_key.c_str()), -1); - if (!bio) { - LOG(LS_ERROR) << "Failed to create a new BIO buffer."; - return NULL; - } - BIO_set_mem_eof_return(bio, 0); - EVP_PKEY* pkey = - PEM_read_bio_PrivateKey(bio, NULL, NULL, const_cast("\0")); - BIO_free(bio); // Frees the BIO, but not the pointed-to string. - - if (!pkey) { - LOG(LS_ERROR) << "Failed to create the private key from PEM string."; - return NULL; + OpenSSLKeyPair* key_pair = + OpenSSLKeyPair::FromPrivateKeyPEMString(private_key); + if (!key_pair) { + LOG(LS_ERROR) << "Failed to create key pair from PEM string."; + return nullptr; } - return new OpenSSLIdentity(new OpenSSLKeyPair(pkey), + return new OpenSSLIdentity(key_pair, cert.release()); } @@ -477,6 +551,23 @@ bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) { return true; } +std::string OpenSSLIdentity::PrivateKeyToPEMString() const { + return key_pair_->PrivateKeyToPEMString(); +} + +std::string OpenSSLIdentity::PublicKeyToPEMString() const { + return key_pair_->PublicKeyToPEMString(); +} + +bool OpenSSLIdentity::operator==(const OpenSSLIdentity& other) const { + return *this->key_pair_ == *other.key_pair_ && + *this->certificate_ == *other.certificate_; +} + +bool OpenSSLIdentity::operator!=(const OpenSSLIdentity& other) const { + return !(*this == other); +} + } // namespace rtc #endif // HAVE_OPENSSL_SSL_H diff --git a/webrtc/base/opensslidentity.h b/webrtc/base/opensslidentity.h index 823f5388ae..316572c48b 100644 --- a/webrtc/base/opensslidentity.h +++ b/webrtc/base/opensslidentity.h @@ -34,12 +34,20 @@ 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); virtual ~OpenSSLKeyPair(); virtual OpenSSLKeyPair* GetReference(); EVP_PKEY* pkey() const { return pkey_; } + std::string PrivateKeyToPEMString() const; + std::string PublicKeyToPEMString() const; + bool operator==(const OpenSSLKeyPair& other) const; + bool operator!=(const OpenSSLKeyPair& other) const; private: void AddReference(); @@ -69,8 +77,9 @@ class OpenSSLCertificate : public SSLCertificate { X509* x509() const { return x509_; } std::string ToPEMString() const override; - void ToDER(Buffer* der_buffer) const override; + bool operator==(const OpenSSLCertificate& other) const; + bool operator!=(const OpenSSLCertificate& other) const; // Compute the digest of the certificate given algorithm bool ComputeDigest(const std::string& algorithm, @@ -116,6 +125,11 @@ class OpenSSLIdentity : public SSLIdentity { // Configure an SSL context object to use our key and certificate. bool ConfigureIdentity(SSL_CTX* ctx); + std::string PrivateKeyToPEMString() const override; + std::string PublicKeyToPEMString() const override; + bool operator==(const OpenSSLIdentity& other) const; + bool operator!=(const OpenSSLIdentity& other) const; + private: OpenSSLIdentity(OpenSSLKeyPair* key_pair, OpenSSLCertificate* certificate); diff --git a/webrtc/base/rtccertificate.cc b/webrtc/base/rtccertificate.cc index 70959eea38..574bf75bf2 100644 --- a/webrtc/base/rtccertificate.cc +++ b/webrtc/base/rtccertificate.cc @@ -45,4 +45,24 @@ const SSLCertificate& RTCCertificate::ssl_certificate() const { return identity_->certificate(); } +RTCCertificatePEM RTCCertificate::ToPEM() const { + return RTCCertificatePEM(identity_->PrivateKeyToPEMString(), + ssl_certificate().ToPEMString()); +} + +scoped_refptr RTCCertificate::FromPEM( + const RTCCertificatePEM& pem) { + std::unique_ptr identity(SSLIdentity::FromPEMStrings( + pem.private_key(), pem.certificate())); + return new RefCountedObject(identity.release()); +} + +bool RTCCertificate::operator==(const RTCCertificate& certificate) const { + return *this->identity_ == *certificate.identity_; +} + +bool RTCCertificate::operator!=(const RTCCertificate& certificate) const { + return !(*this == certificate); +} + } // namespace rtc diff --git a/webrtc/base/rtccertificate.h b/webrtc/base/rtccertificate.h index 3d4f36e6a3..46d6fd427c 100644 --- a/webrtc/base/rtccertificate.h +++ b/webrtc/base/rtccertificate.h @@ -20,6 +20,28 @@ namespace rtc { +// This class contains PEM strings of an RTCCertificate's private key and +// certificate and acts as a text representation of RTCCertificate. Certificates +// can be serialized and deserialized to and from this format, which allows for +// cloning and storing of certificates to disk. The PEM format is that of +// |SSLIdentity::PrivateKeyToPEMString| and |SSLCertificate::ToPEMString|, e.g. +// the string representations used by OpenSSL. +class RTCCertificatePEM { + public: + RTCCertificatePEM( + const std::string& private_key, + const std::string& certificate) + : private_key_(private_key), + certificate_(certificate) {} + + const std::string& private_key() const { return private_key_; } + const std::string& certificate() const { return certificate_; } + + private: + std::string private_key_; + std::string certificate_; +}; + // A thin abstraction layer between "lower level crypto stuff" like // SSLCertificate and WebRTC usage. Takes ownership of some lower level objects, // reference counting protects these from premature destruction. @@ -42,6 +64,12 @@ class RTCCertificate : public RefCountInterface { // However, some places might need SSLIdentity* for its public/private key... SSLIdentity* identity() const { return identity_.get(); } + // To/from PEM, a text representation of the RTCCertificate. + RTCCertificatePEM ToPEM() const; + static scoped_refptr FromPEM(const RTCCertificatePEM& pem); + bool operator==(const RTCCertificate& certificate) const; + bool operator!=(const RTCCertificate& certificate) const; + protected: explicit RTCCertificate(SSLIdentity* identity); ~RTCCertificate() override; diff --git a/webrtc/base/rtccertificate_unittests.cc b/webrtc/base/rtccertificate_unittest.cc similarity index 82% rename from webrtc/base/rtccertificate_unittests.cc rename to webrtc/base/rtccertificate_unittest.cc index 7795fe7a18..f5df7f1130 100644 --- a/webrtc/base/rtccertificate_unittests.cc +++ b/webrtc/base/rtccertificate_unittest.cc @@ -35,6 +35,13 @@ class RTCCertificateTest : public testing::Test { ~RTCCertificateTest() {} protected: + scoped_refptr GenerateECDSA() { + std::unique_ptr identity( + SSLIdentity::Generate(kTestCertCommonName, KeyParams::ECDSA())); + RTC_CHECK(identity); + return RTCCertificate::Create(std::move(identity)); + } + // Timestamp note: // All timestamps in this unittest are expressed in number of seconds since // epoch, 1970-01-01T00:00:00Z (UTC). The RTCCertificate interface uses ms, @@ -85,10 +92,7 @@ class RTCCertificateTest : public testing::Test { TEST_F(RTCCertificateTest, NewCertificateNotExpired) { // Generate a real certificate without specifying the expiration time. // Certificate type doesn't matter, using ECDSA because it's fast to generate. - std::unique_ptr identity( - SSLIdentity::Generate(kTestCertCommonName, KeyParams::ECDSA())); - scoped_refptr certificate = - RTCCertificate::Create(std::move(identity)); + scoped_refptr certificate = GenerateECDSA(); uint64_t now = NowSeconds(); EXPECT_FALSE(HasExpiredSeconds(certificate, now)); @@ -115,4 +119,22 @@ TEST_F(RTCCertificateTest, ExpiresInOneSecond) { EXPECT_TRUE(HasExpiredSeconds(certificate, now + 2)); } +TEST_F(RTCCertificateTest, DifferentCertificatesNotEqual) { + scoped_refptr a = GenerateECDSA(); + scoped_refptr b = GenerateECDSA(); + EXPECT_TRUE(*a != *b); +} + +TEST_F(RTCCertificateTest, CloneWithPEMSerialization) { + scoped_refptr orig = GenerateECDSA(); + + // To PEM. + RTCCertificatePEM orig_pem = orig->ToPEM(); + // Clone from PEM. + scoped_refptr clone = RTCCertificate::FromPEM(orig_pem); + EXPECT_TRUE(clone); + EXPECT_TRUE(*orig == *clone); + EXPECT_EQ(orig->Expires(), clone->Expires()); +} + } // namespace rtc diff --git a/webrtc/base/sslidentity.cc b/webrtc/base/sslidentity.cc index 5fa8bbf6b4..5f3a73fad1 100644 --- a/webrtc/base/sslidentity.cc +++ b/webrtc/base/sslidentity.cc @@ -187,6 +187,14 @@ SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, return OpenSSLIdentity::FromPEMStrings(private_key, certificate); } +bool operator==(const SSLIdentity& a, const SSLIdentity& b) { + return static_cast(a) == + static_cast(b); +} +bool operator!=(const SSLIdentity& a, const SSLIdentity& b) { + return !(a == b); +} + #else // !SSL_USE_OPENSSL #error "No SSL implementation" diff --git a/webrtc/base/sslidentity.h b/webrtc/base/sslidentity.h index f9fef312f5..da94e76486 100644 --- a/webrtc/base/sslidentity.h +++ b/webrtc/base/sslidentity.h @@ -227,6 +227,8 @@ class SSLIdentity { // Returns a temporary reference to the certificate. virtual const SSLCertificate& certificate() const = 0; + virtual std::string PrivateKeyToPEMString() const = 0; + virtual std::string PublicKeyToPEMString() const = 0; // Helpers for parsing converting between PEM and DER format. static bool PemToDer(const std::string& pem_type, @@ -237,6 +239,9 @@ class SSLIdentity { size_t length); }; +bool operator==(const SSLIdentity& a, const SSLIdentity& b); +bool operator!=(const SSLIdentity& a, const SSLIdentity& b); + // Convert from ASN1 time as restricted by RFC 5280 to seconds from 1970-01-01 // 00.00 ("epoch"). If the ASN1 time cannot be read, return -1. The data at // |s| is not 0-terminated; its char count is defined by |length|. diff --git a/webrtc/base/sslidentity_unittest.cc b/webrtc/base/sslidentity_unittest.cc index e108b7d8f8..399fe9d474 100644 --- a/webrtc/base/sslidentity_unittest.cc +++ b/webrtc/base/sslidentity_unittest.cc @@ -174,7 +174,38 @@ class SSLIdentityTest : public testing::Test { EXPECT_EQ(0, memcmp(digest, expected_digest, expected_len)); } - private: + void TestCloningIdentity(const SSLIdentity& identity) { + // Convert |identity| to PEM strings and create a new identity by converting + // back from the string format. + std::string priv_pem = identity.PrivateKeyToPEMString(); + std::string publ_pem = identity.PublicKeyToPEMString(); + std::string cert_pem = identity.certificate().ToPEMString(); + std::unique_ptr clone( + SSLIdentity::FromPEMStrings(priv_pem, cert_pem)); + EXPECT_TRUE(clone); + + // Make sure the clone is identical to the original. + EXPECT_TRUE(identity == *clone); + ASSERT_EQ(identity.certificate().CertificateExpirationTime(), + clone->certificate().CertificateExpirationTime()); + + // At this point we are confident that the identities are identical. To be + // extra sure, we compare PEM strings of the clone with the original. Note + // that the PEM strings of two identities are not strictly guaranteed to be + // equal (they describe structs whose members could be listed in a different + // order, for example). But because the same function is used to produce + // both PEMs, its a good enough bet that this comparison will work. If the + // assumption stops holding in the future we can always remove this from the + // unittest. + std::string clone_priv_pem = clone->PrivateKeyToPEMString(); + std::string clone_publ_pem = clone->PublicKeyToPEMString(); + std::string clone_cert_pem = clone->certificate().ToPEMString(); + ASSERT_EQ(priv_pem, clone_priv_pem); + ASSERT_EQ(publ_pem, clone_publ_pem); + ASSERT_EQ(cert_pem, clone_cert_pem); + } + + protected: std::unique_ptr identity_rsa1_; std::unique_ptr identity_rsa2_; std::unique_ptr identity_ecdsa1_; @@ -220,71 +251,118 @@ TEST_F(SSLIdentityTest, DigestSHA512) { TestDigestForGeneratedCert(rtc::DIGEST_SHA_512, 64); } -TEST_F(SSLIdentityTest, FromPEMStringsRSA) { - static const char kRSA_PRIVATE_KEY_PEM[] = - "-----BEGIN RSA PRIVATE KEY-----\n" - "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" - "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" - "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" - "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" - "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" - "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" - "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" - "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" - "kdqANep0uwZciiNiWxsQrCHztywOvbFhdp8iYVFG9EK8DMY41Y5TxUwsHD+67zao\n" - "ZNqJAkEA1suLUP/GvL8IwuRneQd2tWDqqRQ/Td3qq03hP7e77XtF/buya3Ghclo5\n" - "54czUR89QyVfJEC6278nzA7n2h1uVQJAcG6mztNL6ja/dKZjYZye2CY44QjSlLo0\n" - "MTgTSjdfg/28fFn2Jjtqf9Pi/X+50LWI/RcYMC2no606wRk9kyOuIQJBAK6VSAim\n" - "1pOEjsYQn0X5KEIrz1G3bfCbB848Ime3U2/FWlCHMr6ch8kCZ5d1WUeJD3LbwMNG\n" - "UCXiYxSsu20QNVw=\n" - "-----END RSA PRIVATE KEY-----\n"; +TEST_F(SSLIdentityTest, IdentityComparison) { + EXPECT_TRUE(*identity_rsa1_ == *identity_rsa1_); + EXPECT_FALSE(*identity_rsa1_ == *identity_rsa2_); + EXPECT_FALSE(*identity_rsa1_ == *identity_ecdsa1_); + EXPECT_FALSE(*identity_rsa1_ == *identity_ecdsa2_); + EXPECT_TRUE(*identity_rsa2_ == *identity_rsa2_); + EXPECT_FALSE(*identity_rsa2_ == *identity_ecdsa1_); + EXPECT_FALSE(*identity_rsa2_ == *identity_ecdsa2_); + + EXPECT_TRUE(*identity_ecdsa1_ == *identity_ecdsa1_); + EXPECT_FALSE(*identity_ecdsa1_ == *identity_ecdsa2_); +} + +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" - "MIIBmTCCAQKgAwIBAgIEbzBSAjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZX\n" - "ZWJSVEMwHhcNMTQwMTAyMTgyNDQ3WhcNMTQwMjAxMTgyNDQ3WjARMQ8wDQYDVQQD\n" - "EwZXZWJSVEMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYRkbhmI7kVA/rM\n" - "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" - "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" - "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAUflI\n" - "VUe5Krqf5RVa5C3u/UTAOAUJBiDS3VANTCLBxjuMsvqOG0WvaYWP3HYPgrz0jXK2\n" - "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" - "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\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)); 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()); } TEST_F(SSLIdentityTest, FromPEMStringsEC) { - static const char kRSA_PRIVATE_KEY_PEM[] = - "-----BEGIN EC PRIVATE KEY-----\n" - "MHcCAQEEIKkIztWLPbs4Y2zWv7VW2Ov4is2ifleCuPgRB8fRv3IkoAoGCCqGSM49\n" - "AwEHoUQDQgAEDPV33NrhSdhg9cBRkUWUXnVMXc3h17i9ARbSmNgminKcBXb8/y8L\n" - "A76cMWQPPM0ybHO8OS7ZVg2U/m+TwE1M2g==\n" - "-----END EC PRIVATE KEY-----\n"; + // 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" - "MIIB0jCCAXmgAwIBAgIJAMCjpFt9t6LMMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT\n" - "AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn\n" - "aXRzIFB0eSBMdGQwIBcNMTUwNjMwMTMwMTIyWhgPMjI4OTA0MTMxMzAxMjJaMEUx\n" - "CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\n" - "cm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQM\n" - "9Xfc2uFJ2GD1wFGRRZRedUxdzeHXuL0BFtKY2CaKcpwFdvz/LwsDvpwxZA88zTJs\n" - "c7w5LtlWDZT+b5PATUzao1AwTjAdBgNVHQ4EFgQUYHq6nxNNIE832ZmaHc/noODO\n" - "rtAwHwYDVR0jBBgwFoAUYHq6nxNNIE832ZmaHc/noODOrtAwDAYDVR0TBAUwAwEB\n" - "/zAKBggqhkjOPQQDAgNHADBEAiAQRojsTyZG0BlKoU7gOt5h+yAMLl2cxmDtOIQr\n" - "GWP/PwIgJynB4AUDsPT0DWmethOXYijB5sY5UPd9DvgmiS/Mr6s=\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(kRSA_PRIVATE_KEY_PEM, kCERT_PEM)); + SSLIdentity::FromPEMStrings(kECDSA_PRIVATE_KEY_PEM, kCERT_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()); } +TEST_F(SSLIdentityTest, CloneIdentityRSA) { + TestCloningIdentity(*identity_rsa1_); + TestCloningIdentity(*identity_rsa2_); +} + +TEST_F(SSLIdentityTest, CloneIdentityECDSA) { + TestCloningIdentity(*identity_ecdsa1_); + TestCloningIdentity(*identity_ecdsa2_); +} + TEST_F(SSLIdentityTest, PemDerConversion) { std::string der; EXPECT_TRUE(SSLIdentity::PemToDer("CERTIFICATE", kTestCertificate, &der));