certificate.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /**
  2. * Copyright (c) 2019 Paul-Louis Ageneau
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include "certificate.hpp"
  19. #include "threadpool.hpp"
  20. #include <cassert>
  21. #include <chrono>
  22. #include <iomanip>
  23. #include <mutex>
  24. #include <sstream>
  25. #include <unordered_map>
  26. namespace rtc::impl {
  27. #if USE_GNUTLS
  28. Certificate::Certificate(string crt_pem, string key_pem)
  29. : mCredentials(gnutls::new_credentials(), gnutls::free_credentials) {
  30. gnutls_datum_t crt_datum = gnutls::make_datum(crt_pem.data(), crt_pem.size());
  31. gnutls_datum_t key_datum = gnutls::make_datum(key_pem.data(), key_pem.size());
  32. gnutls::check(gnutls_certificate_set_x509_key_mem(*mCredentials, &crt_datum, &key_datum,
  33. GNUTLS_X509_FMT_PEM),
  34. "Unable to import PEM");
  35. auto new_crt_list = [this]() -> gnutls_x509_crt_t * {
  36. gnutls_x509_crt_t *crt_list = nullptr;
  37. unsigned int crt_list_size = 0;
  38. gnutls::check(gnutls_certificate_get_x509_crt(*mCredentials, 0, &crt_list, &crt_list_size));
  39. assert(crt_list_size == 1);
  40. return crt_list;
  41. };
  42. auto free_crt_list = [](gnutls_x509_crt_t *crt_list) {
  43. gnutls_x509_crt_deinit(crt_list[0]);
  44. gnutls_free(crt_list);
  45. };
  46. std::unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(),
  47. free_crt_list);
  48. mFingerprint = make_fingerprint(*crt_list);
  49. }
  50. Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
  51. : mCredentials(gnutls::new_credentials(), gnutls::free_credentials),
  52. mFingerprint(make_fingerprint(crt)) {
  53. gnutls::check(gnutls_certificate_set_x509_key(*mCredentials, &crt, 1, privkey),
  54. "Unable to set certificate and key pair in credentials");
  55. }
  56. gnutls_certificate_credentials_t Certificate::credentials() const { return *mCredentials; }
  57. string Certificate::fingerprint() const { return mFingerprint; }
  58. string make_fingerprint(gnutls_x509_crt_t crt) {
  59. const size_t size = 32;
  60. unsigned char buffer[size];
  61. size_t len = size;
  62. gnutls::check(gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_SHA256, buffer, &len),
  63. "X509 fingerprint error");
  64. std::ostringstream oss;
  65. oss << std::hex << std::uppercase << std::setfill('0');
  66. for (size_t i = 0; i < len; ++i) {
  67. if (i)
  68. oss << std::setw(1) << ':';
  69. oss << std::setw(2) << unsigned(buffer[i]);
  70. }
  71. return oss.str();
  72. }
  73. namespace {
  74. certificate_ptr make_certificate_impl(string commonName) {
  75. using namespace gnutls;
  76. unique_ptr<gnutls_x509_crt_t, decltype(&free_crt)> crt(new_crt(), free_crt);
  77. unique_ptr<gnutls_x509_privkey_t, decltype(&free_privkey)> privkey(new_privkey(), free_privkey);
  78. #ifdef RSA_KEY_BITS_2048
  79. const unsigned int bits = 2048;
  80. #else
  81. const unsigned int bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_HIGH);
  82. #endif
  83. gnutls::check(gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_RSA, bits, 0),
  84. "Unable to generate key pair");
  85. using namespace std::chrono;
  86. auto now = time_point_cast<seconds>(system_clock::now());
  87. gnutls_x509_crt_set_activation_time(*crt, (now - hours(1)).time_since_epoch().count());
  88. gnutls_x509_crt_set_expiration_time(*crt, (now + hours(24 * 365)).time_since_epoch().count());
  89. gnutls_x509_crt_set_version(*crt, 1);
  90. gnutls_x509_crt_set_key(*crt, *privkey);
  91. gnutls_x509_crt_set_dn_by_oid(*crt, GNUTLS_OID_X520_COMMON_NAME, 0, commonName.data(),
  92. commonName.size());
  93. const size_t serialSize = 16;
  94. char serial[serialSize];
  95. gnutls_rnd(GNUTLS_RND_NONCE, serial, serialSize);
  96. gnutls_x509_crt_set_serial(*crt, serial, serialSize);
  97. gnutls::check(gnutls_x509_crt_sign2(*crt, *crt, *privkey, GNUTLS_DIG_SHA256, 0),
  98. "Unable to auto-sign certificate");
  99. return std::make_shared<Certificate>(*crt, *privkey);
  100. }
  101. } // namespace
  102. #else // USE_GNUTLS==0
  103. Certificate::Certificate(string crt_pem, string key_pem) {
  104. BIO *bio = BIO_new(BIO_s_mem());
  105. BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
  106. mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);
  107. BIO_free(bio);
  108. if (!mX509)
  109. throw std::invalid_argument("Unable to import certificate PEM");
  110. bio = BIO_new(BIO_s_mem());
  111. BIO_write(bio, key_pem.data(), int(key_pem.size()));
  112. mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);
  113. BIO_free(bio);
  114. if (!mPKey)
  115. throw std::invalid_argument("Unable to import PEM key PEM");
  116. mFingerprint = make_fingerprint(mX509.get());
  117. }
  118. Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey)
  119. : mX509(std::move(x509)), mPKey(std::move(pkey)) {
  120. mFingerprint = make_fingerprint(mX509.get());
  121. }
  122. string Certificate::fingerprint() const { return mFingerprint; }
  123. std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const {
  124. return {mX509.get(), mPKey.get()};
  125. }
  126. string make_fingerprint(X509 *x509) {
  127. const size_t size = 32;
  128. unsigned char buffer[size];
  129. unsigned int len = size;
  130. if (!X509_digest(x509, EVP_sha256(), buffer, &len))
  131. throw std::runtime_error("X509 fingerprint error");
  132. std::ostringstream oss;
  133. oss << std::hex << std::uppercase << std::setfill('0');
  134. for (size_t i = 0; i < len; ++i) {
  135. if (i)
  136. oss << std::setw(1) << ':';
  137. oss << std::setw(2) << unsigned(buffer[i]);
  138. }
  139. return oss.str();
  140. }
  141. namespace {
  142. certificate_ptr make_certificate_impl(string commonName) {
  143. shared_ptr<X509> x509(X509_new(), X509_free);
  144. shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
  145. unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
  146. unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
  147. unique_ptr<BIGNUM, decltype(&BN_free)> serial_number(BN_new(), BN_free);
  148. unique_ptr<X509_NAME, decltype(&X509_NAME_free)> name(X509_NAME_new(), X509_NAME_free);
  149. if (!x509 || !pkey || !rsa || !exponent || !serial_number || !name)
  150. throw std::runtime_error("Unable allocate structures for certificate generation");
  151. #ifdef RSA_KEY_BITS_2048
  152. const int bits = 2048;
  153. #else
  154. const int bits = 3072;
  155. #endif
  156. const unsigned int e = 65537; // 2^16 + 1
  157. if (!pkey || !rsa || !exponent || !BN_set_word(exponent.get(), e) ||
  158. !RSA_generate_key_ex(rsa.get(), bits, exponent.get(), NULL) ||
  159. !EVP_PKEY_assign_RSA(pkey.get(), rsa.release())) // the key will be freed when pkey is freed
  160. throw std::runtime_error("Unable to generate key pair");
  161. const size_t serialSize = 16;
  162. auto *commonNameBytes =
  163. reinterpret_cast<unsigned char *>(const_cast<char *>(commonName.c_str()));
  164. if (!X509_gmtime_adj(X509_getm_notBefore(x509.get()), 3600 * -1) ||
  165. !X509_gmtime_adj(X509_getm_notAfter(x509.get()), 3600 * 24 * 365) ||
  166. !X509_set_version(x509.get(), 1) || !X509_set_pubkey(x509.get(), pkey.get()) ||
  167. !BN_pseudo_rand(serial_number.get(), serialSize, 0, 0) ||
  168. !BN_to_ASN1_INTEGER(serial_number.get(), X509_get_serialNumber(x509.get())) ||
  169. !X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_UTF8, commonNameBytes, -1,
  170. -1, 0) ||
  171. !X509_set_subject_name(x509.get(), name.get()) ||
  172. !X509_set_issuer_name(x509.get(), name.get()))
  173. throw std::runtime_error("Unable to set certificate properties");
  174. if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
  175. throw std::runtime_error("Unable to auto-sign certificate");
  176. return std::make_shared<Certificate>(x509, pkey);
  177. }
  178. } // namespace
  179. #endif
  180. // Common for GnuTLS and OpenSSL
  181. namespace {
  182. static std::unordered_map<string, future_certificate_ptr> CertificateCache;
  183. static std::mutex CertificateCacheMutex;
  184. } // namespace
  185. future_certificate_ptr make_certificate(string commonName) {
  186. std::lock_guard lock(CertificateCacheMutex);
  187. if (auto it = CertificateCache.find(commonName); it != CertificateCache.end())
  188. return it->second;
  189. auto future = ThreadPool::Instance().enqueue(make_certificate_impl, commonName);
  190. auto shared = future.share();
  191. CertificateCache.emplace(std::move(commonName), shared);
  192. return shared;
  193. }
  194. void CleanupCertificateCache() {
  195. std::lock_guard lock(CertificateCacheMutex);
  196. CertificateCache.clear();
  197. }
  198. } // namespace rtc::impl