• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "rtc_base/openssl_certificate.h"
12 
13 #if defined(WEBRTC_WIN)
14 // Must be included first before openssl headers.
15 #include "rtc_base/win32.h"  // NOLINT
16 #endif                       // WEBRTC_WIN
17 
18 #include <openssl/bio.h>
19 #include <openssl/bn.h>
20 #include <openssl/pem.h>
21 #include <time.h>
22 
23 #include <memory>
24 
25 #include "rtc_base/checks.h"
26 #include "rtc_base/helpers.h"
27 #include "rtc_base/logging.h"
28 #include "rtc_base/message_digest.h"
29 #include "rtc_base/openssl_digest.h"
30 #include "rtc_base/openssl_identity.h"
31 #include "rtc_base/openssl_utility.h"
32 
33 namespace rtc {
34 namespace {
35 
36 // Random bits for certificate serial number
37 static const int SERIAL_RAND_BITS = 64;
38 
39 #if !defined(NDEBUG)
40 // Print a certificate to the log, for debugging.
PrintCert(X509 * x509)41 static void PrintCert(X509* x509) {
42   BIO* temp_memory_bio = BIO_new(BIO_s_mem());
43   if (!temp_memory_bio) {
44     RTC_DLOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
45     return;
46   }
47   X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0);
48   BIO_write(temp_memory_bio, "\0", 1);
49   char* buffer;
50   BIO_get_mem_data(temp_memory_bio, &buffer);
51   RTC_DLOG(LS_VERBOSE) << buffer;
52   BIO_free(temp_memory_bio);
53 }
54 #endif
55 
56 // Generate a self-signed certificate, with the public key from the
57 // given key pair. Caller is responsible for freeing the returned object.
MakeCertificate(EVP_PKEY * pkey,const SSLIdentityParams & params)58 static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) {
59   RTC_LOG(LS_INFO) << "Making certificate for " << params.common_name;
60 
61   ASN1_INTEGER* asn1_serial_number = nullptr;
62   BIGNUM* serial_number = nullptr;
63   X509* x509 = nullptr;
64   X509_NAME* name = nullptr;
65   time_t epoch_off = 0;  // Time offset since epoch.
66 
67   if ((x509 = X509_new()) == nullptr) {
68     goto error;
69   }
70   if (!X509_set_pubkey(x509, pkey)) {
71     goto error;
72   }
73   // serial number - temporary reference to serial number inside x509 struct
74   if ((serial_number = BN_new()) == nullptr ||
75       !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) ||
76       (asn1_serial_number = X509_get_serialNumber(x509)) == nullptr ||
77       !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number)) {
78     goto error;
79   }
80   // Set version to X509.V3
81   if (!X509_set_version(x509, 2L)) {
82     goto error;
83   }
84 
85   // There are a lot of possible components for the name entries. In
86   // our P2P SSL mode however, the certificates are pre-exchanged
87   // (through the secure XMPP channel), and so the certificate
88   // identification is arbitrary. It can't be empty, so we set some
89   // arbitrary common_name. Note that this certificate goes out in
90   // clear during SSL negotiation, so there may be a privacy issue in
91   // putting anything recognizable here.
92   if ((name = X509_NAME_new()) == nullptr ||
93       !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8,
94                                   (unsigned char*)params.common_name.c_str(),
95                                   -1, -1, 0) ||
96       !X509_set_subject_name(x509, name) || !X509_set_issuer_name(x509, name)) {
97     goto error;
98   }
99   if (!X509_time_adj(X509_get_notBefore(x509), params.not_before, &epoch_off) ||
100       !X509_time_adj(X509_get_notAfter(x509), params.not_after, &epoch_off)) {
101     goto error;
102   }
103   if (!X509_sign(x509, pkey, EVP_sha256())) {
104     goto error;
105   }
106 
107   BN_free(serial_number);
108   X509_NAME_free(name);
109   RTC_LOG(LS_INFO) << "Returning certificate";
110   return x509;
111 
112 error:
113   BN_free(serial_number);
114   X509_NAME_free(name);
115   X509_free(x509);
116   return nullptr;
117 }
118 
119 }  // namespace
120 
OpenSSLCertificate(X509 * x509)121 OpenSSLCertificate::OpenSSLCertificate(X509* x509) : x509_(x509) {
122   RTC_DCHECK(x509_ != nullptr);
123   X509_up_ref(x509_);
124 }
125 
Generate(OpenSSLKeyPair * key_pair,const SSLIdentityParams & params)126 std::unique_ptr<OpenSSLCertificate> OpenSSLCertificate::Generate(
127     OpenSSLKeyPair* key_pair,
128     const SSLIdentityParams& params) {
129   SSLIdentityParams actual_params(params);
130   if (actual_params.common_name.empty()) {
131     // Use a random string, arbitrarily 8chars long.
132     actual_params.common_name = CreateRandomString(8);
133   }
134   X509* x509 = MakeCertificate(key_pair->pkey(), actual_params);
135   if (!x509) {
136     openssl::LogSSLErrors("Generating certificate");
137     return nullptr;
138   }
139 #if !defined(NDEBUG)
140   PrintCert(x509);
141 #endif
142   auto ret = std::make_unique<OpenSSLCertificate>(x509);
143   X509_free(x509);
144   return ret;
145 }
146 
FromPEMString(const std::string & pem_string)147 std::unique_ptr<OpenSSLCertificate> OpenSSLCertificate::FromPEMString(
148     const std::string& pem_string) {
149   BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
150   if (!bio) {
151     return nullptr;
152   }
153 
154   BIO_set_mem_eof_return(bio, 0);
155   X509* x509 =
156       PEM_read_bio_X509(bio, nullptr, nullptr, const_cast<char*>("\0"));
157   BIO_free(bio);  // Frees the BIO, but not the pointed-to string.
158 
159   if (!x509) {
160     return nullptr;
161   }
162   auto ret = std::make_unique<OpenSSLCertificate>(x509);
163   X509_free(x509);
164   return ret;
165 }
166 
167 // NOTE: This implementation only functions correctly after InitializeSSL
168 // and before CleanupSSL.
GetSignatureDigestAlgorithm(std::string * algorithm) const169 bool OpenSSLCertificate::GetSignatureDigestAlgorithm(
170     std::string* algorithm) const {
171   int nid = X509_get_signature_nid(x509_);
172   switch (nid) {
173     case NID_md5WithRSA:
174     case NID_md5WithRSAEncryption:
175       *algorithm = DIGEST_MD5;
176       break;
177     case NID_ecdsa_with_SHA1:
178     case NID_dsaWithSHA1:
179     case NID_dsaWithSHA1_2:
180     case NID_sha1WithRSA:
181     case NID_sha1WithRSAEncryption:
182       *algorithm = DIGEST_SHA_1;
183       break;
184     case NID_ecdsa_with_SHA224:
185     case NID_sha224WithRSAEncryption:
186     case NID_dsa_with_SHA224:
187       *algorithm = DIGEST_SHA_224;
188       break;
189     case NID_ecdsa_with_SHA256:
190     case NID_sha256WithRSAEncryption:
191     case NID_dsa_with_SHA256:
192       *algorithm = DIGEST_SHA_256;
193       break;
194     case NID_ecdsa_with_SHA384:
195     case NID_sha384WithRSAEncryption:
196       *algorithm = DIGEST_SHA_384;
197       break;
198     case NID_ecdsa_with_SHA512:
199     case NID_sha512WithRSAEncryption:
200       *algorithm = DIGEST_SHA_512;
201       break;
202     default:
203       // Unknown algorithm.  There are several unhandled options that are less
204       // common and more complex.
205       RTC_LOG(LS_ERROR) << "Unknown signature algorithm NID: " << nid;
206       algorithm->clear();
207       return false;
208   }
209   return true;
210 }
211 
ComputeDigest(const std::string & algorithm,unsigned char * digest,size_t size,size_t * length) const212 bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm,
213                                        unsigned char* digest,
214                                        size_t size,
215                                        size_t* length) const {
216   return ComputeDigest(x509_, algorithm, digest, size, length);
217 }
218 
ComputeDigest(const X509 * x509,const std::string & algorithm,unsigned char * digest,size_t size,size_t * length)219 bool OpenSSLCertificate::ComputeDigest(const X509* x509,
220                                        const std::string& algorithm,
221                                        unsigned char* digest,
222                                        size_t size,
223                                        size_t* length) {
224   const EVP_MD* md = nullptr;
225   unsigned int n = 0;
226   if (!OpenSSLDigest::GetDigestEVP(algorithm, &md)) {
227     return false;
228   }
229   if (size < static_cast<size_t>(EVP_MD_size(md))) {
230     return false;
231   }
232   X509_digest(x509, md, digest, &n);
233   *length = n;
234   return true;
235 }
236 
~OpenSSLCertificate()237 OpenSSLCertificate::~OpenSSLCertificate() {
238   X509_free(x509_);
239 }
240 
Clone() const241 std::unique_ptr<SSLCertificate> OpenSSLCertificate::Clone() const {
242   return std::make_unique<OpenSSLCertificate>(x509_);
243 }
244 
ToPEMString() const245 std::string OpenSSLCertificate::ToPEMString() const {
246   BIO* bio = BIO_new(BIO_s_mem());
247   if (!bio) {
248     FATAL() << "Unreachable code.";
249   }
250   if (!PEM_write_bio_X509(bio, x509_)) {
251     BIO_free(bio);
252     FATAL() << "Unreachable code.";
253   }
254   BIO_write(bio, "\0", 1);
255   char* buffer;
256   BIO_get_mem_data(bio, &buffer);
257   std::string ret(buffer);
258   BIO_free(bio);
259   return ret;
260 }
261 
ToDER(Buffer * der_buffer) const262 void OpenSSLCertificate::ToDER(Buffer* der_buffer) const {
263   // In case of failure, make sure to leave the buffer empty.
264   der_buffer->SetSize(0);
265   // Calculates the DER representation of the certificate, from scratch.
266   BIO* bio = BIO_new(BIO_s_mem());
267   if (!bio) {
268     FATAL() << "Unreachable code.";
269   }
270   if (!i2d_X509_bio(bio, x509_)) {
271     BIO_free(bio);
272     FATAL() << "Unreachable code.";
273   }
274   char* data = nullptr;
275   size_t length = BIO_get_mem_data(bio, &data);
276   der_buffer->SetData(data, length);
277   BIO_free(bio);
278 }
279 
operator ==(const OpenSSLCertificate & other) const280 bool OpenSSLCertificate::operator==(const OpenSSLCertificate& other) const {
281   return X509_cmp(x509_, other.x509_) == 0;
282 }
283 
operator !=(const OpenSSLCertificate & other) const284 bool OpenSSLCertificate::operator!=(const OpenSSLCertificate& other) const {
285   return !(*this == other);
286 }
287 
CertificateExpirationTime() const288 int64_t OpenSSLCertificate::CertificateExpirationTime() const {
289   ASN1_TIME* expire_time = X509_get_notAfter(x509_);
290   bool long_format;
291   if (expire_time->type == V_ASN1_UTCTIME) {
292     long_format = false;
293   } else if (expire_time->type == V_ASN1_GENERALIZEDTIME) {
294     long_format = true;
295   } else {
296     return -1;
297   }
298   return ASN1TimeToSec(expire_time->data, expire_time->length, long_format);
299 }
300 
301 }  // namespace rtc
302