/* * Copyright 2004 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtc_base/openssl_certificate.h" #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT #endif // WEBRTC_WIN #include #include #include #include #include #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/openssl_digest.h" #include "rtc_base/openssl_identity.h" #include "rtc_base/openssl_utility.h" namespace rtc { namespace { // Random bits for certificate serial number static const int SERIAL_RAND_BITS = 64; #if !defined(NDEBUG) // Print a certificate to the log, for debugging. static void PrintCert(X509* x509) { BIO* temp_memory_bio = BIO_new(BIO_s_mem()); if (!temp_memory_bio) { RTC_DLOG_F(LS_ERROR) << "Failed to allocate temporary memory bio"; return; } X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0); BIO_write(temp_memory_bio, "\0", 1); char* buffer; BIO_get_mem_data(temp_memory_bio, &buffer); RTC_DLOG(LS_VERBOSE) << buffer; BIO_free(temp_memory_bio); } #endif // Generate a self-signed certificate, with the public key from the // given key pair. Caller is responsible for freeing the returned object. static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) { RTC_LOG(LS_INFO) << "Making certificate for " << params.common_name; ASN1_INTEGER* asn1_serial_number = nullptr; std::unique_ptr serial_number{nullptr, ::BN_free}; std::unique_ptr x509{nullptr, ::X509_free}; std::unique_ptr name{ nullptr, ::X509_NAME_free}; time_t epoch_off = 0; // Time offset since epoch. x509.reset(X509_new()); if (x509 == nullptr) { return nullptr; } if (!X509_set_pubkey(x509.get(), pkey)) { return nullptr; } // serial number - temporary reference to serial number inside x509 struct serial_number.reset(BN_new()); if (serial_number == nullptr || !BN_pseudo_rand(serial_number.get(), SERIAL_RAND_BITS, 0, 0) || (asn1_serial_number = X509_get_serialNumber(x509.get())) == nullptr || !BN_to_ASN1_INTEGER(serial_number.get(), asn1_serial_number)) { return nullptr; } // Set version to X509.V3 if (!X509_set_version(x509.get(), 2L)) { return nullptr; } // There are a lot of possible components for the name entries. In // our P2P SSL mode however, the certificates are pre-exchanged // (through the secure XMPP channel), and so the certificate // identification is arbitrary. It can't be empty, so we set some // arbitrary common_name. Note that this certificate goes out in // clear during SSL negotiation, so there may be a privacy issue in // putting anything recognizable here. name.reset(X509_NAME_new()); if (name == nullptr || !X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_UTF8, (unsigned char*)params.common_name.c_str(), -1, -1, 0) || !X509_set_subject_name(x509.get(), name.get()) || !X509_set_issuer_name(x509.get(), name.get())) { return nullptr; } if (!X509_time_adj(X509_get_notBefore(x509.get()), params.not_before, &epoch_off) || !X509_time_adj(X509_get_notAfter(x509.get()), params.not_after, &epoch_off)) { return nullptr; } if (!X509_sign(x509.get(), pkey, EVP_sha256())) { return nullptr; } RTC_LOG(LS_INFO) << "Returning certificate"; return x509.release(); } } // namespace OpenSSLCertificate::OpenSSLCertificate(X509* x509) : x509_(x509) { RTC_DCHECK(x509_ != nullptr); X509_up_ref(x509_); } std::unique_ptr OpenSSLCertificate::Generate( OpenSSLKeyPair* key_pair, const SSLIdentityParams& params) { SSLIdentityParams actual_params(params); if (actual_params.common_name.empty()) { // Use a random string, arbitrarily 8chars long. actual_params.common_name = CreateRandomString(8); } X509* x509 = MakeCertificate(key_pair->pkey(), actual_params); if (!x509) { openssl::LogSSLErrors("Generating certificate"); return nullptr; } #if !defined(NDEBUG) PrintCert(x509); #endif auto ret = std::make_unique(x509); X509_free(x509); return ret; } std::unique_ptr OpenSSLCertificate::FromPEMString( absl::string_view pem_string) { BIO* bio = BIO_new_mem_buf(const_cast(pem_string.data()), -1); if (!bio) { return nullptr; } BIO_set_mem_eof_return(bio, 0); X509* x509 = PEM_read_bio_X509(bio, nullptr, nullptr, const_cast("\0")); BIO_free(bio); // Frees the BIO, but not the pointed-to string. if (!x509) { return nullptr; } auto ret = std::make_unique(x509); X509_free(x509); return ret; } // NOTE: This implementation only functions correctly after InitializeSSL // and before CleanupSSL. bool OpenSSLCertificate::GetSignatureDigestAlgorithm( std::string* algorithm) const { int nid = X509_get_signature_nid(x509_); switch (nid) { case NID_md5WithRSA: case NID_md5WithRSAEncryption: *algorithm = DIGEST_MD5; break; case NID_ecdsa_with_SHA1: case NID_dsaWithSHA1: case NID_dsaWithSHA1_2: case NID_sha1WithRSA: case NID_sha1WithRSAEncryption: *algorithm = DIGEST_SHA_1; break; case NID_ecdsa_with_SHA224: case NID_sha224WithRSAEncryption: case NID_dsa_with_SHA224: *algorithm = DIGEST_SHA_224; break; case NID_ecdsa_with_SHA256: case NID_sha256WithRSAEncryption: case NID_dsa_with_SHA256: *algorithm = DIGEST_SHA_256; break; case NID_ecdsa_with_SHA384: case NID_sha384WithRSAEncryption: *algorithm = DIGEST_SHA_384; break; case NID_ecdsa_with_SHA512: case NID_sha512WithRSAEncryption: *algorithm = DIGEST_SHA_512; break; default: // Unknown algorithm. There are several unhandled options that are less // common and more complex. RTC_LOG(LS_ERROR) << "Unknown signature algorithm NID: " << nid; algorithm->clear(); return false; } return true; } bool OpenSSLCertificate::ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const { return ComputeDigest(x509_, algorithm, digest, size, length); } bool OpenSSLCertificate::ComputeDigest(const X509* x509, absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) { const EVP_MD* md = nullptr; unsigned int n = 0; if (!OpenSSLDigest::GetDigestEVP(algorithm, &md)) { return false; } if (size < static_cast(EVP_MD_size(md))) { return false; } X509_digest(x509, md, digest, &n); *length = n; return true; } OpenSSLCertificate::~OpenSSLCertificate() { X509_free(x509_); } std::unique_ptr OpenSSLCertificate::Clone() const { return std::make_unique(x509_); } std::string OpenSSLCertificate::ToPEMString() const { BIO* bio = BIO_new(BIO_s_mem()); RTC_CHECK(bio); RTC_CHECK(PEM_write_bio_X509(bio, x509_)); BIO_write(bio, "\0", 1); char* buffer; BIO_get_mem_data(bio, &buffer); std::string ret(buffer); BIO_free(bio); return ret; } void OpenSSLCertificate::ToDER(Buffer* der_buffer) const { // In case of failure, make sure to leave the buffer empty. der_buffer->SetSize(0); // Calculates the DER representation of the certificate, from scratch. BIO* bio = BIO_new(BIO_s_mem()); RTC_CHECK(bio); RTC_CHECK(i2d_X509_bio(bio, x509_)); char* data = nullptr; size_t length = BIO_get_mem_data(bio, &data); der_buffer->SetData(data, length); BIO_free(bio); } bool OpenSSLCertificate::operator==(const OpenSSLCertificate& other) const { return X509_cmp(x509_, other.x509_) == 0; } bool OpenSSLCertificate::operator!=(const OpenSSLCertificate& other) const { return !(*this == other); } int64_t OpenSSLCertificate::CertificateExpirationTime() const { ASN1_TIME* expire_time = X509_get_notAfter(x509_); bool long_format; if (expire_time->type == V_ASN1_UTCTIME) { long_format = false; } else if (expire_time->type == V_ASN1_GENERALIZEDTIME) { long_format = true; } else { return -1; } return ASN1TimeToSec(expire_time->data, expire_time->length, long_format); } } // namespace rtc