1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/openssl_key_pair.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <memory>
14*d9f75844SAndroid Build Coastguard Worker #include <utility>
15*d9f75844SAndroid Build Coastguard Worker
16*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
17*d9f75844SAndroid Build Coastguard Worker
18*d9f75844SAndroid Build Coastguard Worker #if defined(WEBRTC_WIN)
19*d9f75844SAndroid Build Coastguard Worker // Must be included first before openssl headers.
20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/win32.h" // NOLINT
21*d9f75844SAndroid Build Coastguard Worker #endif // WEBRTC_WIN
22*d9f75844SAndroid Build Coastguard Worker
23*d9f75844SAndroid Build Coastguard Worker #include <openssl/bio.h>
24*d9f75844SAndroid Build Coastguard Worker #include <openssl/bn.h>
25*d9f75844SAndroid Build Coastguard Worker #include <openssl/pem.h>
26*d9f75844SAndroid Build Coastguard Worker #include <openssl/rsa.h>
27*d9f75844SAndroid Build Coastguard Worker
28*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
29*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
30*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/openssl.h"
31*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/openssl_utility.h"
32*d9f75844SAndroid Build Coastguard Worker
33*d9f75844SAndroid Build Coastguard Worker namespace rtc {
34*d9f75844SAndroid Build Coastguard Worker
35*d9f75844SAndroid Build Coastguard Worker // We could have exposed a myriad of parameters for the crypto stuff,
36*d9f75844SAndroid Build Coastguard Worker // but keeping it simple seems best.
37*d9f75844SAndroid Build Coastguard Worker
38*d9f75844SAndroid Build Coastguard Worker // Generate a key pair. Caller is responsible for freeing the returned object.
MakeKey(const KeyParams & key_params)39*d9f75844SAndroid Build Coastguard Worker static EVP_PKEY* MakeKey(const KeyParams& key_params) {
40*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Making key pair";
41*d9f75844SAndroid Build Coastguard Worker EVP_PKEY* pkey = EVP_PKEY_new();
42*d9f75844SAndroid Build Coastguard Worker if (key_params.type() == KT_RSA) {
43*d9f75844SAndroid Build Coastguard Worker int key_length = key_params.rsa_params().mod_size;
44*d9f75844SAndroid Build Coastguard Worker BIGNUM* exponent = BN_new();
45*d9f75844SAndroid Build Coastguard Worker RSA* rsa = RSA_new();
46*d9f75844SAndroid Build Coastguard Worker if (!pkey || !exponent || !rsa ||
47*d9f75844SAndroid Build Coastguard Worker !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
48*d9f75844SAndroid Build Coastguard Worker !RSA_generate_key_ex(rsa, key_length, exponent, nullptr) ||
49*d9f75844SAndroid Build Coastguard Worker !EVP_PKEY_assign_RSA(pkey, rsa)) {
50*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey);
51*d9f75844SAndroid Build Coastguard Worker BN_free(exponent);
52*d9f75844SAndroid Build Coastguard Worker RSA_free(rsa);
53*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Failed to make RSA key pair";
54*d9f75844SAndroid Build Coastguard Worker return nullptr;
55*d9f75844SAndroid Build Coastguard Worker }
56*d9f75844SAndroid Build Coastguard Worker // ownership of rsa struct was assigned, don't free it.
57*d9f75844SAndroid Build Coastguard Worker BN_free(exponent);
58*d9f75844SAndroid Build Coastguard Worker } else if (key_params.type() == KT_ECDSA) {
59*d9f75844SAndroid Build Coastguard Worker if (key_params.ec_curve() == EC_NIST_P256) {
60*d9f75844SAndroid Build Coastguard Worker EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
61*d9f75844SAndroid Build Coastguard Worker if (!ec_key) {
62*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey);
63*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Failed to allocate EC key";
64*d9f75844SAndroid Build Coastguard Worker return nullptr;
65*d9f75844SAndroid Build Coastguard Worker }
66*d9f75844SAndroid Build Coastguard Worker
67*d9f75844SAndroid Build Coastguard Worker // Ensure curve name is included when EC key is serialized.
68*d9f75844SAndroid Build Coastguard Worker // Without this call, OpenSSL versions before 1.1.0 will create
69*d9f75844SAndroid Build Coastguard Worker // certificates that don't work for TLS.
70*d9f75844SAndroid Build Coastguard Worker // This is a no-op for BoringSSL and OpenSSL 1.1.0+
71*d9f75844SAndroid Build Coastguard Worker EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);
72*d9f75844SAndroid Build Coastguard Worker
73*d9f75844SAndroid Build Coastguard Worker if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
74*d9f75844SAndroid Build Coastguard Worker !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
75*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey);
76*d9f75844SAndroid Build Coastguard Worker EC_KEY_free(ec_key);
77*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Failed to make EC key pair";
78*d9f75844SAndroid Build Coastguard Worker return nullptr;
79*d9f75844SAndroid Build Coastguard Worker }
80*d9f75844SAndroid Build Coastguard Worker // ownership of ec_key struct was assigned, don't free it.
81*d9f75844SAndroid Build Coastguard Worker } else {
82*d9f75844SAndroid Build Coastguard Worker // Add generation of any other curves here.
83*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey);
84*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
85*d9f75844SAndroid Build Coastguard Worker return nullptr;
86*d9f75844SAndroid Build Coastguard Worker }
87*d9f75844SAndroid Build Coastguard Worker } else {
88*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey);
89*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Key type requested not understood";
90*d9f75844SAndroid Build Coastguard Worker return nullptr;
91*d9f75844SAndroid Build Coastguard Worker }
92*d9f75844SAndroid Build Coastguard Worker
93*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << "Returning key pair";
94*d9f75844SAndroid Build Coastguard Worker return pkey;
95*d9f75844SAndroid Build Coastguard Worker }
96*d9f75844SAndroid Build Coastguard Worker
Generate(const KeyParams & key_params)97*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::Generate(
98*d9f75844SAndroid Build Coastguard Worker const KeyParams& key_params) {
99*d9f75844SAndroid Build Coastguard Worker EVP_PKEY* pkey = MakeKey(key_params);
100*d9f75844SAndroid Build Coastguard Worker if (!pkey) {
101*d9f75844SAndroid Build Coastguard Worker openssl::LogSSLErrors("Generating key pair");
102*d9f75844SAndroid Build Coastguard Worker return nullptr;
103*d9f75844SAndroid Build Coastguard Worker }
104*d9f75844SAndroid Build Coastguard Worker return std::make_unique<OpenSSLKeyPair>(pkey);
105*d9f75844SAndroid Build Coastguard Worker }
106*d9f75844SAndroid Build Coastguard Worker
FromPrivateKeyPEMString(absl::string_view pem_string)107*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::FromPrivateKeyPEMString(
108*d9f75844SAndroid Build Coastguard Worker absl::string_view pem_string) {
109*d9f75844SAndroid Build Coastguard Worker BIO* bio =
110*d9f75844SAndroid Build Coastguard Worker BIO_new_mem_buf(const_cast<char*>(pem_string.data()), pem_string.size());
111*d9f75844SAndroid Build Coastguard Worker if (!bio) {
112*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
113*d9f75844SAndroid Build Coastguard Worker return nullptr;
114*d9f75844SAndroid Build Coastguard Worker }
115*d9f75844SAndroid Build Coastguard Worker BIO_set_mem_eof_return(bio, 0);
116*d9f75844SAndroid Build Coastguard Worker EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
117*d9f75844SAndroid Build Coastguard Worker BIO_free(bio); // Frees the BIO, but not the pointed-to string.
118*d9f75844SAndroid Build Coastguard Worker if (!pkey) {
119*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
120*d9f75844SAndroid Build Coastguard Worker return nullptr;
121*d9f75844SAndroid Build Coastguard Worker }
122*d9f75844SAndroid Build Coastguard Worker if (EVP_PKEY_missing_parameters(pkey) != 0) {
123*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR)
124*d9f75844SAndroid Build Coastguard Worker << "The resulting key pair is missing public key parameters.";
125*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey);
126*d9f75844SAndroid Build Coastguard Worker return nullptr;
127*d9f75844SAndroid Build Coastguard Worker }
128*d9f75844SAndroid Build Coastguard Worker return std::make_unique<OpenSSLKeyPair>(pkey);
129*d9f75844SAndroid Build Coastguard Worker }
130*d9f75844SAndroid Build Coastguard Worker
~OpenSSLKeyPair()131*d9f75844SAndroid Build Coastguard Worker OpenSSLKeyPair::~OpenSSLKeyPair() {
132*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_free(pkey_);
133*d9f75844SAndroid Build Coastguard Worker }
134*d9f75844SAndroid Build Coastguard Worker
Clone()135*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::Clone() {
136*d9f75844SAndroid Build Coastguard Worker AddReference();
137*d9f75844SAndroid Build Coastguard Worker return std::make_unique<OpenSSLKeyPair>(pkey_);
138*d9f75844SAndroid Build Coastguard Worker }
139*d9f75844SAndroid Build Coastguard Worker
AddReference()140*d9f75844SAndroid Build Coastguard Worker void OpenSSLKeyPair::AddReference() {
141*d9f75844SAndroid Build Coastguard Worker EVP_PKEY_up_ref(pkey_);
142*d9f75844SAndroid Build Coastguard Worker }
143*d9f75844SAndroid Build Coastguard Worker
PrivateKeyToPEMString() const144*d9f75844SAndroid Build Coastguard Worker std::string OpenSSLKeyPair::PrivateKeyToPEMString() const {
145*d9f75844SAndroid Build Coastguard Worker BIO* temp_memory_bio = BIO_new(BIO_s_mem());
146*d9f75844SAndroid Build Coastguard Worker if (!temp_memory_bio) {
147*d9f75844SAndroid Build Coastguard Worker RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
148*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED();
149*d9f75844SAndroid Build Coastguard Worker return "";
150*d9f75844SAndroid Build Coastguard Worker }
151*d9f75844SAndroid Build Coastguard Worker if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0,
152*d9f75844SAndroid Build Coastguard Worker nullptr, nullptr)) {
153*d9f75844SAndroid Build Coastguard Worker RTC_LOG_F(LS_ERROR) << "Failed to write private key";
154*d9f75844SAndroid Build Coastguard Worker BIO_free(temp_memory_bio);
155*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED();
156*d9f75844SAndroid Build Coastguard Worker return "";
157*d9f75844SAndroid Build Coastguard Worker }
158*d9f75844SAndroid Build Coastguard Worker char* buffer;
159*d9f75844SAndroid Build Coastguard Worker size_t len = BIO_get_mem_data(temp_memory_bio, &buffer);
160*d9f75844SAndroid Build Coastguard Worker std::string priv_key_str(buffer, len);
161*d9f75844SAndroid Build Coastguard Worker BIO_free(temp_memory_bio);
162*d9f75844SAndroid Build Coastguard Worker return priv_key_str;
163*d9f75844SAndroid Build Coastguard Worker }
164*d9f75844SAndroid Build Coastguard Worker
PublicKeyToPEMString() const165*d9f75844SAndroid Build Coastguard Worker std::string OpenSSLKeyPair::PublicKeyToPEMString() const {
166*d9f75844SAndroid Build Coastguard Worker BIO* temp_memory_bio = BIO_new(BIO_s_mem());
167*d9f75844SAndroid Build Coastguard Worker if (!temp_memory_bio) {
168*d9f75844SAndroid Build Coastguard Worker RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
169*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED();
170*d9f75844SAndroid Build Coastguard Worker return "";
171*d9f75844SAndroid Build Coastguard Worker }
172*d9f75844SAndroid Build Coastguard Worker if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) {
173*d9f75844SAndroid Build Coastguard Worker RTC_LOG_F(LS_ERROR) << "Failed to write public key";
174*d9f75844SAndroid Build Coastguard Worker BIO_free(temp_memory_bio);
175*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED();
176*d9f75844SAndroid Build Coastguard Worker return "";
177*d9f75844SAndroid Build Coastguard Worker }
178*d9f75844SAndroid Build Coastguard Worker BIO_write(temp_memory_bio, "\0", 1);
179*d9f75844SAndroid Build Coastguard Worker char* buffer;
180*d9f75844SAndroid Build Coastguard Worker BIO_get_mem_data(temp_memory_bio, &buffer);
181*d9f75844SAndroid Build Coastguard Worker std::string pub_key_str = buffer;
182*d9f75844SAndroid Build Coastguard Worker BIO_free(temp_memory_bio);
183*d9f75844SAndroid Build Coastguard Worker return pub_key_str;
184*d9f75844SAndroid Build Coastguard Worker }
185*d9f75844SAndroid Build Coastguard Worker
operator ==(const OpenSSLKeyPair & other) const186*d9f75844SAndroid Build Coastguard Worker bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const {
187*d9f75844SAndroid Build Coastguard Worker return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1;
188*d9f75844SAndroid Build Coastguard Worker }
189*d9f75844SAndroid Build Coastguard Worker
operator !=(const OpenSSLKeyPair & other) const190*d9f75844SAndroid Build Coastguard Worker bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const {
191*d9f75844SAndroid Build Coastguard Worker return !(*this == other);
192*d9f75844SAndroid Build Coastguard Worker }
193*d9f75844SAndroid Build Coastguard Worker
194*d9f75844SAndroid Build Coastguard Worker } // namespace rtc
195