Home | History | Annotate | Download | only in base
      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 #include "talk/base/opensslidentity.h"
     29 
     30 #include <openssl/ssl.h>
     31 #include <openssl/bio.h>
     32 #include <openssl/err.h>
     33 #include <openssl/pem.h>
     34 #include <openssl/bn.h>
     35 #include <openssl/rsa.h>
     36 #include <openssl/crypto.h>
     37 
     38 #include "talk/base/logging.h"
     39 #include "talk/base/helpers.h"
     40 
     41 namespace talk_base {
     42 
     43 // We could have exposed a myriad of parameters for the crypto stuff,
     44 // but keeping it simple seems best.
     45 
     46 // Strength of generated keys. Those are RSA.
     47 static const int KEY_LENGTH = 1024;
     48 
     49 // Random bits for certificate serial number
     50 static const int SERIAL_RAND_BITS = 64;
     51 
     52 // Certificate validity lifetime
     53 static const int CERTIFICATE_LIFETIME = 60*60*24*365;  // one year, arbitrarily
     54 
     55 // Generate a key pair. Caller is responsible for freeing the returned object.
     56 static EVP_PKEY* MakeKey() {
     57   LOG(LS_INFO) << "Making key pair";
     58   EVP_PKEY* pkey = EVP_PKEY_new();
     59 #if OPENSSL_VERSION_NUMBER < 0x00908000l
     60   // Only RSA_generate_key is available. Use that.
     61   RSA* rsa = RSA_generate_key(KEY_LENGTH, 0x10001, NULL, NULL);
     62   if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
     63     EVP_PKEY_free(pkey);
     64     RSA_free(rsa);
     65     return NULL;
     66   }
     67 #else
     68   // RSA_generate_key is deprecated. Use _ex version.
     69   BIGNUM* exponent = BN_new();
     70   RSA* rsa = RSA_new();
     71   if (!pkey || !exponent || !rsa ||
     72       !BN_set_word(exponent, 0x10001) ||  // 65537 RSA exponent
     73       !RSA_generate_key_ex(rsa, KEY_LENGTH, exponent, NULL) ||
     74       !EVP_PKEY_assign_RSA(pkey, rsa)) {
     75     EVP_PKEY_free(pkey);
     76     BN_free(exponent);
     77     RSA_free(rsa);
     78     return NULL;
     79   }
     80   // ownership of rsa struct was assigned, don't free it.
     81   BN_free(exponent);
     82 #endif
     83   LOG(LS_INFO) << "Returning key pair";
     84   return pkey;
     85 }
     86 
     87 // Generate a self-signed certificate, with the public key from the
     88 // given key pair. Caller is responsible for freeing the returned object.
     89 static X509* MakeCertificate(EVP_PKEY* pkey, const char* common_name) {
     90   LOG(LS_INFO) << "Making certificate for " << common_name;
     91   X509* x509 = NULL;
     92   BIGNUM* serial_number = NULL;
     93   X509_NAME* name = NULL;
     94 
     95   if ((x509=X509_new()) == NULL)
     96     goto error;
     97 
     98   if (!X509_set_pubkey(x509, pkey))
     99     goto error;
    100 
    101   // serial number
    102   // temporary reference to serial number inside x509 struct
    103   ASN1_INTEGER* asn1_serial_number;
    104   if (!(serial_number = BN_new()) ||
    105       !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) ||
    106       !(asn1_serial_number = X509_get_serialNumber(x509)) ||
    107       !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number))
    108     goto error;
    109 
    110   if (!X509_set_version(x509, 0L))  // version 1
    111     goto error;
    112 
    113   // There are a lot of possible components for the name entries. In
    114   // our P2P SSL mode however, the certificates are pre-exchanged
    115   // (through the secure XMPP channel), and so the certificate
    116   // identification is arbitrary. It can't be empty, so we set some
    117   // arbitrary common_name. Note that this certificate goes out in
    118   // clear during SSL negotiation, so there may be a privacy issue in
    119   // putting anything recognizable here.
    120   if (!(name = X509_NAME_new()) ||
    121       !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8,
    122                                      (unsigned char*)common_name, -1, -1, 0) ||
    123       !X509_set_subject_name(x509, name) ||
    124       !X509_set_issuer_name(x509, name))
    125     goto error;
    126 
    127   if (!X509_gmtime_adj(X509_get_notBefore(x509), 0) ||
    128       !X509_gmtime_adj(X509_get_notAfter(x509), CERTIFICATE_LIFETIME))
    129     goto error;
    130 
    131   if (!X509_sign(x509, pkey, EVP_sha1()))
    132     goto error;
    133 
    134   BN_free(serial_number);
    135   X509_NAME_free(name);
    136   LOG(LS_INFO) << "Returning certificate";
    137   return x509;
    138 
    139  error:
    140   BN_free(serial_number);
    141   X509_NAME_free(name);
    142   X509_free(x509);
    143   return NULL;
    144 }
    145 
    146 // This dumps the SSL error stack to the log.
    147 static void LogSSLErrors(const std::string& prefix) {
    148   char error_buf[200];
    149   unsigned long err;
    150   while ((err = ERR_get_error())) {
    151     ERR_error_string_n(err, error_buf, sizeof(error_buf));
    152     LOG(LS_ERROR) << prefix << ": " << error_buf << "\n";
    153   }
    154 }
    155 
    156 OpenSSLKeyPair* OpenSSLKeyPair::Generate() {
    157   EVP_PKEY* pkey = MakeKey();
    158   if (!pkey) {
    159     LogSSLErrors("Generating key pair");
    160     return NULL;
    161   }
    162   return new OpenSSLKeyPair(pkey);
    163 }
    164 
    165 OpenSSLKeyPair::~OpenSSLKeyPair() {
    166   EVP_PKEY_free(pkey_);
    167 }
    168 
    169 void OpenSSLKeyPair::AddReference() {
    170   CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY);
    171 }
    172 
    173 #ifdef _DEBUG
    174 // Print a certificate to the log, for debugging.
    175 static void PrintCert(X509* x509) {
    176   BIO* temp_memory_bio = BIO_new(BIO_s_mem());
    177   if (!temp_memory_bio) {
    178     LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
    179     return;
    180   }
    181   X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0);
    182   BIO_write(temp_memory_bio, "\0", 1);
    183   char* buffer;
    184   BIO_get_mem_data(temp_memory_bio, &buffer);
    185   LOG(LS_VERBOSE) << buffer;
    186   BIO_free(temp_memory_bio);
    187 }
    188 #endif
    189 
    190 OpenSSLCertificate* OpenSSLCertificate::Generate(
    191     OpenSSLKeyPair* key_pair, const std::string& common_name) {
    192   std::string actual_common_name = common_name;
    193   if (actual_common_name.empty())
    194     // Use a random string, arbitrarily 8chars long.
    195     actual_common_name = CreateRandomString(8);
    196   X509* x509 = MakeCertificate(key_pair->pkey(), actual_common_name.c_str());
    197   if (!x509) {
    198     LogSSLErrors("Generating certificate");
    199     return NULL;
    200   }
    201 #ifdef _DEBUG
    202   PrintCert(x509);
    203 #endif
    204   return new OpenSSLCertificate(x509);
    205 }
    206 
    207 OpenSSLCertificate* OpenSSLCertificate::FromPEMString(
    208     const std::string& pem_string, int* pem_length) {
    209   BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
    210   if (!bio)
    211     return NULL;
    212   (void)BIO_set_close(bio, BIO_NOCLOSE);
    213   BIO_set_mem_eof_return(bio, 0);
    214   X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL,
    215                                  const_cast<char*>("\0"));
    216   char *ptr;
    217   int remaining_length = BIO_get_mem_data(bio, &ptr);
    218   BIO_free(bio);
    219   if (pem_length)
    220     *pem_length = pem_string.length() - remaining_length;
    221   if (x509)
    222     return new OpenSSLCertificate(x509);
    223   else
    224     return NULL;
    225 }
    226 
    227 OpenSSLCertificate::~OpenSSLCertificate() {
    228   X509_free(x509_);
    229 }
    230 
    231 std::string OpenSSLCertificate::ToPEMString() const {
    232   BIO* bio = BIO_new(BIO_s_mem());
    233   if (!bio)
    234     return NULL;
    235   if (!PEM_write_bio_X509(bio, x509_)) {
    236     BIO_free(bio);
    237     return NULL;
    238   }
    239   BIO_write(bio, "\0", 1);
    240   char* buffer;
    241   BIO_get_mem_data(bio, &buffer);
    242   std::string ret(buffer);
    243   BIO_free(bio);
    244   return ret;
    245 }
    246 
    247 void OpenSSLCertificate::AddReference() {
    248   CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509);
    249 }
    250 
    251 OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) {
    252   OpenSSLKeyPair *key_pair = OpenSSLKeyPair::Generate();
    253   if (key_pair) {
    254     OpenSSLCertificate *certificate =
    255         OpenSSLCertificate::Generate(key_pair, common_name);
    256     if (certificate)
    257       return new OpenSSLIdentity(key_pair, certificate);
    258     delete key_pair;
    259   }
    260   LOG(LS_INFO) << "Identity generation failed";
    261   return NULL;
    262 }
    263 
    264 bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
    265   // 1 is the documented success return code.
    266   if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 ||
    267      SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) {
    268     LogSSLErrors("Configuring key and certificate");
    269     return false;
    270   }
    271   return true;
    272 }
    273 
    274 }  // talk_base namespace
    275