Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/talk/base/opensslidentity.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 11.5 KB
Line 
1/*
2 * libjingle
3 * Copyright 2004--2008, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#if HAVE_OPENSSL_SSL_H
29
30#include "talk/base/opensslidentity.h"
31
32// Must be included first before openssl headers.
33#include "talk/base/win32.h"  // NOLINT
34
35#include <openssl/ssl.h>
36#include <openssl/bio.h>
37#include <openssl/err.h>
38#include <openssl/pem.h>
39#include <openssl/bn.h>
40#include <openssl/rsa.h>
41#include <openssl/crypto.h>
42
43#include "talk/base/checks.h"
44#include "talk/base/helpers.h"
45#include "talk/base/logging.h"
46#include "talk/base/openssldigest.h"
47
48namespace talk_base {
49
50// We could have exposed a myriad of parameters for the crypto stuff,
51// but keeping it simple seems best.
52
53// Strength of generated keys. Those are RSA.
54static const int KEY_LENGTH = 1024;
55
56// Random bits for certificate serial number
57static const int SERIAL_RAND_BITS = 64;
58
59// Certificate validity lifetime
60static const int CERTIFICATE_LIFETIME = 60*60*24*365;  // one year, arbitrarily
61// Certificate validity window.
62// This is to compensate for slightly incorrect system clocks.
63static const int CERTIFICATE_WINDOW = -60*60*24;
64
65// Generate a key pair. Caller is responsible for freeing the returned object.
66static EVP_PKEY* MakeKey() {
67  LOG(LS_INFO) << "Making key pair";
68  EVP_PKEY* pkey = EVP_PKEY_new();
69#if OPENSSL_VERSION_NUMBER < 0x00908000l
70  // Only RSA_generate_key is available. Use that.
71  RSA* rsa = RSA_generate_key(KEY_LENGTH, 0x10001, NULL, NULL);
72  if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
73    EVP_PKEY_free(pkey);
74    RSA_free(rsa);
75    return NULL;
76  }
77#else
78  // RSA_generate_key is deprecated. Use _ex version.
79  BIGNUM* exponent = BN_new();
80  RSA* rsa = RSA_new();
81  if (!pkey || !exponent || !rsa ||
82      !BN_set_word(exponent, 0x10001) ||  // 65537 RSA exponent
83      !RSA_generate_key_ex(rsa, KEY_LENGTH, exponent, NULL) ||
84      !EVP_PKEY_assign_RSA(pkey, rsa)) {
85    EVP_PKEY_free(pkey);
86    BN_free(exponent);
87    RSA_free(rsa);
88    return NULL;
89  }
90  // ownership of rsa struct was assigned, don't free it.
91  BN_free(exponent);
92#endif
93  LOG(LS_INFO) << "Returning key pair";
94  return pkey;
95}
96
97// Generate a self-signed certificate, with the public key from the
98// given key pair. Caller is responsible for freeing the returned object.
99static X509* MakeCertificate(EVP_PKEY* pkey, const char* common_name) {
100  LOG(LS_INFO) << "Making certificate for " << common_name;
101  X509* x509 = NULL;
102  BIGNUM* serial_number = NULL;
103  X509_NAME* name = NULL;
104
105  if ((x509=X509_new()) == NULL)
106    goto error;
107
108  if (!X509_set_pubkey(x509, pkey))
109    goto error;
110
111  // serial number
112  // temporary reference to serial number inside x509 struct
113  ASN1_INTEGER* asn1_serial_number;
114  if ((serial_number = BN_new()) == NULL ||
115      !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) ||
116      (asn1_serial_number = X509_get_serialNumber(x509)) == NULL ||
117      !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number))
118    goto error;
119
120  if (!X509_set_version(x509, 0L))  // version 1
121    goto error;
122
123  // There are a lot of possible components for the name entries. In
124  // our P2P SSL mode however, the certificates are pre-exchanged
125  // (through the secure XMPP channel), and so the certificate
126  // identification is arbitrary. It can't be empty, so we set some
127  // arbitrary common_name. Note that this certificate goes out in
128  // clear during SSL negotiation, so there may be a privacy issue in
129  // putting anything recognizable here.
130  if ((name = X509_NAME_new()) == NULL ||
131      !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8,
132                                     (unsigned char*)common_name, -1, -1, 0) ||
133      !X509_set_subject_name(x509, name) ||
134      !X509_set_issuer_name(x509, name))
135    goto error;
136
137  if (!X509_gmtime_adj(X509_get_notBefore(x509), CERTIFICATE_WINDOW) ||
138      !X509_gmtime_adj(X509_get_notAfter(x509), CERTIFICATE_LIFETIME))
139    goto error;
140
141  if (!X509_sign(x509, pkey, EVP_sha1()))
142    goto error;
143
144  BN_free(serial_number);
145  X509_NAME_free(name);
146  LOG(LS_INFO) << "Returning certificate";
147  return x509;
148
149 error:
150  BN_free(serial_number);
151  X509_NAME_free(name);
152  X509_free(x509);
153  return NULL;
154}
155
156// This dumps the SSL error stack to the log.
157static void LogSSLErrors(const std::string& prefix) {
158  char error_buf[200];
159  unsigned long err;
160
161  while ((err = ERR_get_error()) != 0) {
162    ERR_error_string_n(err, error_buf, sizeof(error_buf));
163    LOG(LS_ERROR) << prefix << ": " << error_buf << "\n";
164  }
165}
166
167OpenSSLKeyPair* OpenSSLKeyPair::Generate() {
168  EVP_PKEY* pkey = MakeKey();
169  if (!pkey) {
170    LogSSLErrors("Generating key pair");
171    return NULL;
172  }
173  return new OpenSSLKeyPair(pkey);
174}
175
176OpenSSLKeyPair::~OpenSSLKeyPair() {
177  EVP_PKEY_free(pkey_);
178}
179
180void OpenSSLKeyPair::AddReference() {
181  CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY);
182}
183
184#ifdef _DEBUG
185// Print a certificate to the log, for debugging.
186static void PrintCert(X509* x509) {
187  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
188  if (!temp_memory_bio) {
189    LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
190    return;
191  }
192  X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0);
193  BIO_write(temp_memory_bio, "\0", 1);
194  char* buffer;
195  BIO_get_mem_data(temp_memory_bio, &buffer);
196  LOG(LS_VERBOSE) << buffer;
197  BIO_free(temp_memory_bio);
198}
199#endif
200
201OpenSSLCertificate* OpenSSLCertificate::Generate(
202    OpenSSLKeyPair* key_pair, const std::string& common_name) {
203  std::string actual_common_name = common_name;
204  if (actual_common_name.empty())
205    // Use a random string, arbitrarily 8chars long.
206    actual_common_name = CreateRandomString(8);
207  X509* x509 = MakeCertificate(key_pair->pkey(), actual_common_name.c_str());
208  if (!x509) {
209    LogSSLErrors("Generating certificate");
210    return NULL;
211  }
212#ifdef _DEBUG
213  PrintCert(x509);
214#endif
215  OpenSSLCertificate* ret = new OpenSSLCertificate(x509);
216  X509_free(x509);
217  return ret;
218}
219
220OpenSSLCertificate* OpenSSLCertificate::FromPEMString(
221    const std::string& pem_string) {
222  BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
223  if (!bio)
224    return NULL;
225  (void)BIO_set_close(bio, BIO_NOCLOSE);
226  BIO_set_mem_eof_return(bio, 0);
227  X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL,
228                                 const_cast<char*>("\0"));
229  BIO_free(bio);
230  if (!x509)
231    return NULL;
232
233  OpenSSLCertificate* ret = new OpenSSLCertificate(x509);
234  X509_free(x509);
235  return ret;
236}
237
238// NOTE: This implementation only functions correctly after InitializeSSL
239// and before CleanupSSL.
240bool OpenSSLCertificate::GetSignatureDigestAlgorithm(
241    std::string* algorithm) const {
242  return OpenSSLDigest::GetDigestName(
243      EVP_get_digestbyobj(x509_->sig_alg->algorithm), algorithm);
244}
245
246bool OpenSSLCertificate::ComputeDigest(const std::string &algorithm,
247                                       unsigned char *digest,
248                                       std::size_t size,
249                                       std::size_t *length) const {
250  return ComputeDigest(x509_, algorithm, digest, size, length);
251}
252
253bool OpenSSLCertificate::ComputeDigest(const X509 *x509,
254                                       const std::string &algorithm,
255                                       unsigned char *digest,
256                                       std::size_t size,
257                                       std::size_t *length) {
258  const EVP_MD *md;
259  unsigned int n;
260
261  if (!OpenSSLDigest::GetDigestEVP(algorithm, &md))
262    return false;
263
264  if (size < static_cast<size_t>(EVP_MD_size(md)))
265    return false;
266
267  X509_digest(x509, md, digest, &n);
268
269  *length = n;
270
271  return true;
272}
273
274OpenSSLCertificate::~OpenSSLCertificate() {
275  X509_free(x509_);
276}
277
278std::string OpenSSLCertificate::ToPEMString() const {
279  BIO* bio = BIO_new(BIO_s_mem());
280  if (!bio) {
281    UNREACHABLE();
282    return std::string();
283  }
284  if (!PEM_write_bio_X509(bio, x509_)) {
285    BIO_free(bio);
286    UNREACHABLE();
287    return std::string();
288  }
289  BIO_write(bio, "\0", 1);
290  char* buffer;
291  BIO_get_mem_data(bio, &buffer);
292  std::string ret(buffer);
293  BIO_free(bio);
294  return ret;
295}
296
297void OpenSSLCertificate::ToDER(Buffer* der_buffer) const {
298  // In case of failure, make sure to leave the buffer empty.
299  der_buffer->SetData(NULL, 0);
300
301  // Calculates the DER representation of the certificate, from scratch.
302  BIO* bio = BIO_new(BIO_s_mem());
303  if (!bio) {
304    UNREACHABLE();
305    return;
306  }
307  if (!i2d_X509_bio(bio, x509_)) {
308    BIO_free(bio);
309    UNREACHABLE();
310    return;
311  }
312  char* data;
313  size_t length = BIO_get_mem_data(bio, &data);
314  der_buffer->SetData(data, length);
315  BIO_free(bio);
316}
317
318void OpenSSLCertificate::AddReference() const {
319  ASSERT(x509_ != NULL);
320  CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509);
321}
322
323OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) {
324  OpenSSLKeyPair *key_pair = OpenSSLKeyPair::Generate();
325  if (key_pair) {
326    OpenSSLCertificate *certificate =
327        OpenSSLCertificate::Generate(key_pair, common_name);
328    if (certificate)
329      return new OpenSSLIdentity(key_pair, certificate);
330    delete key_pair;
331  }
332  LOG(LS_INFO) << "Identity generation failed";
333  return NULL;
334}
335
336SSLIdentity* OpenSSLIdentity::FromPEMStrings(
337    const std::string& private_key,
338    const std::string& certificate) {
339  scoped_ptr<OpenSSLCertificate> cert(
340      OpenSSLCertificate::FromPEMString(certificate));
341  if (!cert) {
342    LOG(LS_ERROR) << "Failed to create OpenSSLCertificate from PEM string.";
343    return NULL;
344  }
345
346  BIO* bio = BIO_new_mem_buf(const_cast<char*>(private_key.c_str()), -1);
347  if (!bio) {
348    LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
349    return NULL;
350  }
351  (void)BIO_set_close(bio, BIO_NOCLOSE);
352  BIO_set_mem_eof_return(bio, 0);
353  EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL,
354                                           const_cast<char*>("\0"));
355  BIO_free(bio);
356
357  if (!pkey) {
358    LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
359    return NULL;
360  }
361
362  return new OpenSSLIdentity(new OpenSSLKeyPair(pkey),
363                             cert.release());
364}
365
366bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
367  // 1 is the documented success return code.
368  if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 ||
369     SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) {
370    LogSSLErrors("Configuring key and certificate");
371    return false;
372  }
373  return true;
374}
375
376}  // namespace talk_base
377
378#endif  // HAVE_OPENSSL_SSL_H
379
380
Note: See TracBrowser for help on using the repository browser.