blob: ec9c00710be8be632ef8455db0d73073512ee927 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
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#if HAVE_OPENSSL_SSL_H
12
13#include "webrtc/base/opensslidentity.h"
14
jbauch555604a2016-04-26 03:13:22 -070015#include <memory>
16
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000017// Must be included first before openssl headers.
18#include "webrtc/base/win32.h" // NOLINT
19
20#include <openssl/bio.h>
21#include <openssl/err.h>
22#include <openssl/pem.h>
23#include <openssl/bn.h>
24#include <openssl/rsa.h>
25#include <openssl/crypto.h>
26
27#include "webrtc/base/checks.h"
28#include "webrtc/base/helpers.h"
29#include "webrtc/base/logging.h"
30#include "webrtc/base/openssl.h"
31#include "webrtc/base/openssldigest.h"
32
33namespace rtc {
34
35// We could have exposed a myriad of parameters for the crypto stuff,
36// but keeping it simple seems best.
37
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000038// Random bits for certificate serial number
39static const int SERIAL_RAND_BITS = 64;
40
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000041// Generate a key pair. Caller is responsible for freeing the returned object.
torbjorng4e572472015-10-08 09:42:49 -070042static EVP_PKEY* MakeKey(const KeyParams& key_params) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000043 LOG(LS_INFO) << "Making key pair";
44 EVP_PKEY* pkey = EVP_PKEY_new();
torbjorng4e572472015-10-08 09:42:49 -070045 if (key_params.type() == KT_RSA) {
46 int key_length = key_params.rsa_params().mod_size;
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +020047 BIGNUM* exponent = BN_new();
48 RSA* rsa = RSA_new();
49 if (!pkey || !exponent || !rsa ||
torbjorng4e572472015-10-08 09:42:49 -070050 !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
51 !RSA_generate_key_ex(rsa, key_length, exponent, NULL) ||
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +020052 !EVP_PKEY_assign_RSA(pkey, rsa)) {
53 EVP_PKEY_free(pkey);
54 BN_free(exponent);
55 RSA_free(rsa);
56 LOG(LS_ERROR) << "Failed to make RSA key pair";
57 return NULL;
58 }
59 // ownership of rsa struct was assigned, don't free it.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000060 BN_free(exponent);
torbjorng4e572472015-10-08 09:42:49 -070061 } else if (key_params.type() == KT_ECDSA) {
62 if (key_params.ec_curve() == EC_NIST_P256) {
63 EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
64 if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
65 !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
66 EVP_PKEY_free(pkey);
67 EC_KEY_free(ec_key);
68 LOG(LS_ERROR) << "Failed to make EC key pair";
69 return NULL;
70 }
71 // ownership of ec_key struct was assigned, don't free it.
72 } else {
73 // Add generation of any other curves here.
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +020074 EVP_PKEY_free(pkey);
torbjorng4e572472015-10-08 09:42:49 -070075 LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +020076 return NULL;
77 }
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +020078 } else {
79 EVP_PKEY_free(pkey);
80 LOG(LS_ERROR) << "Key type requested not understood";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000081 return NULL;
82 }
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +020083
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000084 LOG(LS_INFO) << "Returning key pair";
85 return pkey;
86}
87
88// Generate a self-signed certificate, with the public key from the
89// given key pair. Caller is responsible for freeing the returned object.
90static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) {
91 LOG(LS_INFO) << "Making certificate for " << params.common_name;
92 X509* x509 = NULL;
93 BIGNUM* serial_number = NULL;
94 X509_NAME* name = NULL;
Torbjorn Granlund46c9cc02015-12-01 13:06:34 +010095 time_t epoch_off = 0; // Time offset since epoch.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000096
97 if ((x509=X509_new()) == NULL)
98 goto error;
99
100 if (!X509_set_pubkey(x509, pkey))
101 goto error;
102
103 // serial number
104 // temporary reference to serial number inside x509 struct
105 ASN1_INTEGER* asn1_serial_number;
106 if ((serial_number = BN_new()) == NULL ||
107 !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) ||
108 (asn1_serial_number = X509_get_serialNumber(x509)) == NULL ||
109 !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number))
110 goto error;
111
torbjorngf8160352016-03-29 07:57:43 -0700112 if (!X509_set_version(x509, 2L)) // version 3
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000113 goto error;
114
115 // There are a lot of possible components for the name entries. In
116 // our P2P SSL mode however, the certificates are pre-exchanged
117 // (through the secure XMPP channel), and so the certificate
118 // identification is arbitrary. It can't be empty, so we set some
119 // arbitrary common_name. Note that this certificate goes out in
120 // clear during SSL negotiation, so there may be a privacy issue in
121 // putting anything recognizable here.
122 if ((name = X509_NAME_new()) == NULL ||
123 !X509_NAME_add_entry_by_NID(
124 name, NID_commonName, MBSTRING_UTF8,
125 (unsigned char*)params.common_name.c_str(), -1, -1, 0) ||
126 !X509_set_subject_name(x509, name) ||
127 !X509_set_issuer_name(x509, name))
128 goto error;
129
Torbjorn Granlund46c9cc02015-12-01 13:06:34 +0100130 if (!X509_time_adj(X509_get_notBefore(x509), params.not_before, &epoch_off) ||
131 !X509_time_adj(X509_get_notAfter(x509), params.not_after, &epoch_off))
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000132 goto error;
133
Joachim Bauch1b794d52015-05-12 03:32:11 +0200134 if (!X509_sign(x509, pkey, EVP_sha256()))
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000135 goto error;
136
137 BN_free(serial_number);
138 X509_NAME_free(name);
139 LOG(LS_INFO) << "Returning certificate";
140 return x509;
141
142 error:
143 BN_free(serial_number);
144 X509_NAME_free(name);
145 X509_free(x509);
146 return NULL;
147}
148
149// This dumps the SSL error stack to the log.
150static void LogSSLErrors(const std::string& prefix) {
151 char error_buf[200];
152 unsigned long err;
153
154 while ((err = ERR_get_error()) != 0) {
155 ERR_error_string_n(err, error_buf, sizeof(error_buf));
156 LOG(LS_ERROR) << prefix << ": " << error_buf << "\n";
157 }
158}
159
torbjorng4e572472015-10-08 09:42:49 -0700160OpenSSLKeyPair* OpenSSLKeyPair::Generate(const KeyParams& key_params) {
161 EVP_PKEY* pkey = MakeKey(key_params);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000162 if (!pkey) {
163 LogSSLErrors("Generating key pair");
164 return NULL;
165 }
166 return new OpenSSLKeyPair(pkey);
167}
168
169OpenSSLKeyPair::~OpenSSLKeyPair() {
170 EVP_PKEY_free(pkey_);
171}
172
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000173OpenSSLKeyPair* OpenSSLKeyPair::GetReference() {
174 AddReference();
175 return new OpenSSLKeyPair(pkey_);
176}
177
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000178void OpenSSLKeyPair::AddReference() {
Torbjorn Granlund9adc91d2016-03-24 14:05:06 +0100179#if defined(OPENSSL_IS_BORINGSSL)
Jiayang Liu770cc382015-05-28 15:36:29 -0700180 EVP_PKEY_up_ref(pkey_);
Torbjorn Granlund9adc91d2016-03-24 14:05:06 +0100181#else
182 CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY);
183#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000184}
185
tfarinaa41ab932015-10-30 16:08:48 -0700186#if !defined(NDEBUG)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000187// Print a certificate to the log, for debugging.
188static void PrintCert(X509* x509) {
189 BIO* temp_memory_bio = BIO_new(BIO_s_mem());
190 if (!temp_memory_bio) {
191 LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
192 return;
193 }
194 X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0);
195 BIO_write(temp_memory_bio, "\0", 1);
196 char* buffer;
197 BIO_get_mem_data(temp_memory_bio, &buffer);
198 LOG(LS_VERBOSE) << buffer;
199 BIO_free(temp_memory_bio);
200}
201#endif
202
203OpenSSLCertificate* OpenSSLCertificate::Generate(
204 OpenSSLKeyPair* key_pair, const SSLIdentityParams& params) {
205 SSLIdentityParams actual_params(params);
206 if (actual_params.common_name.empty()) {
207 // Use a random string, arbitrarily 8chars long.
208 actual_params.common_name = CreateRandomString(8);
209 }
210 X509* x509 = MakeCertificate(key_pair->pkey(), actual_params);
211 if (!x509) {
212 LogSSLErrors("Generating certificate");
213 return NULL;
214 }
tfarinaa41ab932015-10-30 16:08:48 -0700215#if !defined(NDEBUG)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000216 PrintCert(x509);
217#endif
218 OpenSSLCertificate* ret = new OpenSSLCertificate(x509);
219 X509_free(x509);
220 return ret;
221}
222
223OpenSSLCertificate* OpenSSLCertificate::FromPEMString(
224 const std::string& pem_string) {
225 BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
226 if (!bio)
227 return NULL;
228 BIO_set_mem_eof_return(bio, 0);
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +0200229 X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, const_cast<char*>("\0"));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000230 BIO_free(bio); // Frees the BIO, but not the pointed-to string.
231
232 if (!x509)
233 return NULL;
234
235 OpenSSLCertificate* ret = new OpenSSLCertificate(x509);
236 X509_free(x509);
237 return ret;
238}
239
240// NOTE: This implementation only functions correctly after InitializeSSL
241// and before CleanupSSL.
242bool OpenSSLCertificate::GetSignatureDigestAlgorithm(
243 std::string* algorithm) const {
JiaYang (佳扬) Liu01aeaee2015-04-22 12:18:33 -0700244 int nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
245 switch (nid) {
246 case NID_md5WithRSA:
247 case NID_md5WithRSAEncryption:
248 *algorithm = DIGEST_MD5;
249 break;
250 case NID_ecdsa_with_SHA1:
251 case NID_dsaWithSHA1:
252 case NID_dsaWithSHA1_2:
253 case NID_sha1WithRSA:
254 case NID_sha1WithRSAEncryption:
255 *algorithm = DIGEST_SHA_1;
256 break;
257 case NID_ecdsa_with_SHA224:
258 case NID_sha224WithRSAEncryption:
259 case NID_dsa_with_SHA224:
260 *algorithm = DIGEST_SHA_224;
261 break;
262 case NID_ecdsa_with_SHA256:
263 case NID_sha256WithRSAEncryption:
264 case NID_dsa_with_SHA256:
265 *algorithm = DIGEST_SHA_256;
266 break;
267 case NID_ecdsa_with_SHA384:
268 case NID_sha384WithRSAEncryption:
269 *algorithm = DIGEST_SHA_384;
270 break;
271 case NID_ecdsa_with_SHA512:
272 case NID_sha512WithRSAEncryption:
273 *algorithm = DIGEST_SHA_512;
274 break;
275 default:
276 // Unknown algorithm. There are several unhandled options that are less
277 // common and more complex.
278 LOG(LS_ERROR) << "Unknown signature algorithm NID: " << nid;
279 algorithm->clear();
280 return false;
281 }
282 return true;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000283}
284
jbauch555604a2016-04-26 03:13:22 -0700285std::unique_ptr<SSLCertChain> OpenSSLCertificate::GetChain() const {
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000286 // Chains are not yet supported when using OpenSSL.
287 // OpenSSLStreamAdapter::SSLVerifyCallback currently requires the remote
288 // certificate to be self-signed.
kwibergf5d47862016-03-15 12:53:24 -0700289 return nullptr;
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000290}
291
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000292bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm,
293 unsigned char* digest,
294 size_t size,
295 size_t* length) const {
296 return ComputeDigest(x509_, algorithm, digest, size, length);
297}
298
299bool OpenSSLCertificate::ComputeDigest(const X509* x509,
300 const std::string& algorithm,
301 unsigned char* digest,
302 size_t size,
303 size_t* length) {
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +0200304 const EVP_MD* md;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000305 unsigned int n;
306
307 if (!OpenSSLDigest::GetDigestEVP(algorithm, &md))
308 return false;
309
310 if (size < static_cast<size_t>(EVP_MD_size(md)))
311 return false;
312
313 X509_digest(x509, md, digest, &n);
314
315 *length = n;
316
317 return true;
318}
319
320OpenSSLCertificate::~OpenSSLCertificate() {
321 X509_free(x509_);
322}
323
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000324OpenSSLCertificate* OpenSSLCertificate::GetReference() const {
325 return new OpenSSLCertificate(x509_);
326}
327
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000328std::string OpenSSLCertificate::ToPEMString() const {
329 BIO* bio = BIO_new(BIO_s_mem());
330 if (!bio) {
andrew@webrtc.orga5b78692014-08-28 16:28:26 +0000331 FATAL() << "unreachable code";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000332 }
333 if (!PEM_write_bio_X509(bio, x509_)) {
334 BIO_free(bio);
andrew@webrtc.orga5b78692014-08-28 16:28:26 +0000335 FATAL() << "unreachable code";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000336 }
337 BIO_write(bio, "\0", 1);
338 char* buffer;
339 BIO_get_mem_data(bio, &buffer);
340 std::string ret(buffer);
341 BIO_free(bio);
342 return ret;
343}
344
345void OpenSSLCertificate::ToDER(Buffer* der_buffer) const {
346 // In case of failure, make sure to leave the buffer empty.
Karl Wiberg94784372015-04-20 14:03:07 +0200347 der_buffer->SetSize(0);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000348
349 // Calculates the DER representation of the certificate, from scratch.
350 BIO* bio = BIO_new(BIO_s_mem());
351 if (!bio) {
andrew@webrtc.orga5b78692014-08-28 16:28:26 +0000352 FATAL() << "unreachable code";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000353 }
354 if (!i2d_X509_bio(bio, x509_)) {
355 BIO_free(bio);
andrew@webrtc.orga5b78692014-08-28 16:28:26 +0000356 FATAL() << "unreachable code";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000357 }
358 char* data;
359 size_t length = BIO_get_mem_data(bio, &data);
360 der_buffer->SetData(data, length);
361 BIO_free(bio);
362}
363
364void OpenSSLCertificate::AddReference() const {
365 ASSERT(x509_ != NULL);
Torbjorn Granlund9adc91d2016-03-24 14:05:06 +0100366#if defined(OPENSSL_IS_BORINGSSL)
Jiayang Liu770cc382015-05-28 15:36:29 -0700367 X509_up_ref(x509_);
Torbjorn Granlund9adc91d2016-03-24 14:05:06 +0100368#else
369 CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509);
370#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000371}
372
Torbjorn Granlund46c9cc02015-12-01 13:06:34 +0100373// Documented in sslidentity.h.
374int64_t OpenSSLCertificate::CertificateExpirationTime() const {
375 ASN1_TIME* expire_time = X509_get_notAfter(x509_);
376 bool long_format;
377
378 if (expire_time->type == V_ASN1_UTCTIME) {
379 long_format = false;
380 } else if (expire_time->type == V_ASN1_GENERALIZEDTIME) {
381 long_format = true;
382 } else {
383 return -1;
384 }
385
386 return ASN1TimeToSec(expire_time->data, expire_time->length, long_format);
387}
388
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000389OpenSSLIdentity::OpenSSLIdentity(OpenSSLKeyPair* key_pair,
390 OpenSSLCertificate* certificate)
391 : key_pair_(key_pair), certificate_(certificate) {
392 ASSERT(key_pair != NULL);
393 ASSERT(certificate != NULL);
394}
395
396OpenSSLIdentity::~OpenSSLIdentity() = default;
397
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000398OpenSSLIdentity* OpenSSLIdentity::GenerateInternal(
399 const SSLIdentityParams& params) {
torbjorng4e572472015-10-08 09:42:49 -0700400 OpenSSLKeyPair* key_pair = OpenSSLKeyPair::Generate(params.key_params);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000401 if (key_pair) {
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +0200402 OpenSSLCertificate* certificate =
403 OpenSSLCertificate::Generate(key_pair, params);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000404 if (certificate)
405 return new OpenSSLIdentity(key_pair, certificate);
406 delete key_pair;
407 }
408 LOG(LS_INFO) << "Identity generation failed";
409 return NULL;
410}
411
Torbjorn Granlund1d846b22016-03-31 16:21:04 +0200412OpenSSLIdentity* OpenSSLIdentity::GenerateWithExpiration(
413 const std::string& common_name,
414 const KeyParams& key_params,
415 time_t certificate_lifetime) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000416 SSLIdentityParams params;
torbjorng4e572472015-10-08 09:42:49 -0700417 params.key_params = key_params;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000418 params.common_name = common_name;
Torbjorn Granlund46c9cc02015-12-01 13:06:34 +0100419 time_t now = time(NULL);
Torbjorn Granlund1d846b22016-03-31 16:21:04 +0200420 params.not_before = now + kCertificateWindowInSeconds;
torbjornge8dc0812016-02-15 09:35:54 -0800421 params.not_after = now + certificate_lifetime;
Torbjorn Granlund1d846b22016-03-31 16:21:04 +0200422 if (params.not_before > params.not_after)
423 return nullptr;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000424 return GenerateInternal(params);
425}
426
427OpenSSLIdentity* OpenSSLIdentity::GenerateForTest(
428 const SSLIdentityParams& params) {
429 return GenerateInternal(params);
430}
431
432SSLIdentity* OpenSSLIdentity::FromPEMStrings(
433 const std::string& private_key,
434 const std::string& certificate) {
jbauch555604a2016-04-26 03:13:22 -0700435 std::unique_ptr<OpenSSLCertificate> cert(
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000436 OpenSSLCertificate::FromPEMString(certificate));
437 if (!cert) {
438 LOG(LS_ERROR) << "Failed to create OpenSSLCertificate from PEM string.";
439 return NULL;
440 }
441
442 BIO* bio = BIO_new_mem_buf(const_cast<char*>(private_key.c_str()), -1);
443 if (!bio) {
444 LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
445 return NULL;
446 }
447 BIO_set_mem_eof_return(bio, 0);
Torbjorn Granlundb6d4ec42015-08-17 14:08:59 +0200448 EVP_PKEY* pkey =
449 PEM_read_bio_PrivateKey(bio, NULL, NULL, const_cast<char*>("\0"));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000450 BIO_free(bio); // Frees the BIO, but not the pointed-to string.
451
452 if (!pkey) {
453 LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
454 return NULL;
455 }
456
457 return new OpenSSLIdentity(new OpenSSLKeyPair(pkey),
458 cert.release());
459}
460
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000461const OpenSSLCertificate& OpenSSLIdentity::certificate() const {
462 return *certificate_;
463}
464
465OpenSSLIdentity* OpenSSLIdentity::GetReference() const {
466 return new OpenSSLIdentity(key_pair_->GetReference(),
467 certificate_->GetReference());
468}
469
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000470bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
471 // 1 is the documented success return code.
472 if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 ||
473 SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) {
474 LogSSLErrors("Configuring key and certificate");
475 return false;
476 }
477 return true;
478}
479
480} // namespace rtc
481
482#endif // HAVE_OPENSSL_SSL_H