certificate.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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::FromString(string crt_pem, string key_pem) {
  29. PLOG_DEBUG << "Importing certificate from PEM string (GnuTLS)";
  30. shared_ptr<gnutls_certificate_credentials_t> creds(gnutls::new_credentials(),
  31. gnutls::free_credentials);
  32. gnutls_datum_t crt_datum = gnutls::make_datum(crt_pem.data(), crt_pem.size());
  33. gnutls_datum_t key_datum = gnutls::make_datum(key_pem.data(), key_pem.size());
  34. gnutls::check(
  35. gnutls_certificate_set_x509_key_mem(*creds, &crt_datum, &key_datum, GNUTLS_X509_FMT_PEM),
  36. "Unable to import PEM certificate and key");
  37. return Certificate(std::move(creds));
  38. }
  39. Certificate Certificate::FromFile(const string &crt_pem_file, const string &key_pem_file,
  40. const string &pass) {
  41. PLOG_DEBUG << "Importing certificate from PEM file (GnuTLS): " << crt_pem_file;
  42. shared_ptr<gnutls_certificate_credentials_t> creds(gnutls::new_credentials(),
  43. gnutls::free_credentials);
  44. gnutls::check(gnutls_certificate_set_x509_key_file2(*creds, crt_pem_file.c_str(),
  45. key_pem_file.c_str(), GNUTLS_X509_FMT_PEM,
  46. pass.c_str(), 0),
  47. "Unable to import PEM certificate and key from file");
  48. return Certificate(std::move(creds));
  49. }
  50. Certificate Certificate::Generate(CertificateType type, const string &commonName) {
  51. PLOG_DEBUG << "Generating certificate (GnuTLS)";
  52. using namespace gnutls;
  53. unique_ptr<gnutls_x509_crt_t, decltype(&free_crt)> crt(new_crt(), free_crt);
  54. unique_ptr<gnutls_x509_privkey_t, decltype(&free_privkey)> privkey(new_privkey(), free_privkey);
  55. switch (type) {
  56. // RFC 8827 WebRTC Security Architecture 6.5. Communications Security
  57. // All implementations MUST support DTLS 1.2 with the TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  58. // cipher suite and the P-256 curve
  59. // See https://tools.ietf.org/html/rfc8827#section-6.5
  60. case CertificateType::Default:
  61. case CertificateType::Ecdsa: {
  62. gnutls::check(gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_ECDSA,
  63. GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1),
  64. 0),
  65. "Unable to generate ECDSA P-256 key pair");
  66. break;
  67. }
  68. case CertificateType::Rsa: {
  69. const unsigned int bits = 2048;
  70. gnutls::check(gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_RSA, bits, 0),
  71. "Unable to generate RSA key pair");
  72. break;
  73. }
  74. default:
  75. throw std::invalid_argument("Unknown certificate type");
  76. }
  77. using namespace std::chrono;
  78. auto now = time_point_cast<seconds>(system_clock::now());
  79. gnutls_x509_crt_set_activation_time(*crt, (now - hours(1)).time_since_epoch().count());
  80. gnutls_x509_crt_set_expiration_time(*crt, (now + hours(24 * 365)).time_since_epoch().count());
  81. gnutls_x509_crt_set_version(*crt, 1);
  82. gnutls_x509_crt_set_key(*crt, *privkey);
  83. gnutls_x509_crt_set_dn_by_oid(*crt, GNUTLS_OID_X520_COMMON_NAME, 0, commonName.data(),
  84. commonName.size());
  85. const size_t serialSize = 16;
  86. char serial[serialSize];
  87. gnutls_rnd(GNUTLS_RND_NONCE, serial, serialSize);
  88. gnutls_x509_crt_set_serial(*crt, serial, serialSize);
  89. gnutls::check(gnutls_x509_crt_sign2(*crt, *crt, *privkey, GNUTLS_DIG_SHA256, 0),
  90. "Unable to auto-sign certificate");
  91. return Certificate(*crt, *privkey);
  92. }
  93. Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
  94. : mCredentials(gnutls::new_credentials(), gnutls::free_credentials),
  95. mFingerprint(make_fingerprint(crt)) {
  96. gnutls::check(gnutls_certificate_set_x509_key(*mCredentials, &crt, 1, privkey),
  97. "Unable to set certificate and key pair in credentials");
  98. }
  99. Certificate::Certificate(shared_ptr<gnutls_certificate_credentials_t> creds)
  100. : mCredentials(std::move(creds)), mFingerprint(make_fingerprint(*mCredentials)) {}
  101. gnutls_certificate_credentials_t Certificate::credentials() const { return *mCredentials; }
  102. string Certificate::fingerprint() const { return mFingerprint; }
  103. string make_fingerprint(gnutls_certificate_credentials_t credentials) {
  104. auto new_crt_list = [credentials]() -> gnutls_x509_crt_t * {
  105. gnutls_x509_crt_t *crt_list = nullptr;
  106. unsigned int crt_list_size = 0;
  107. gnutls::check(gnutls_certificate_get_x509_crt(credentials, 0, &crt_list, &crt_list_size));
  108. assert(crt_list_size == 1);
  109. return crt_list;
  110. };
  111. auto free_crt_list = [](gnutls_x509_crt_t *crt_list) {
  112. gnutls_x509_crt_deinit(crt_list[0]);
  113. gnutls_free(crt_list);
  114. };
  115. unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(), free_crt_list);
  116. return make_fingerprint(*crt_list);
  117. }
  118. string make_fingerprint(gnutls_x509_crt_t crt) {
  119. const size_t size = 32;
  120. unsigned char buffer[size];
  121. size_t len = size;
  122. gnutls::check(gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_SHA256, buffer, &len),
  123. "X509 fingerprint error");
  124. std::ostringstream oss;
  125. oss << std::hex << std::uppercase << std::setfill('0');
  126. for (size_t i = 0; i < len; ++i) {
  127. if (i)
  128. oss << std::setw(1) << ':';
  129. oss << std::setw(2) << unsigned(buffer[i]);
  130. }
  131. return oss.str();
  132. }
  133. #else // USE_GNUTLS==0
  134. #include <cstdio>
  135. namespace {
  136. // Dummy password callback that copies the password from user data
  137. int dummy_pass_cb(char *buf, int size, int /*rwflag*/, void *u) {
  138. const char *pass = static_cast<char *>(u);
  139. return snprintf(buf, size, "%s", pass);
  140. }
  141. } // namespace
  142. Certificate Certificate::FromString(string crt_pem, string key_pem) {
  143. PLOG_DEBUG << "Importing certificate from PEM string (OpenSSL)";
  144. BIO *bio = BIO_new(BIO_s_mem());
  145. BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
  146. auto x509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, nullptr, nullptr), X509_free);
  147. BIO_free(bio);
  148. if (!x509)
  149. throw std::invalid_argument("Unable to import PEM certificate");
  150. bio = BIO_new(BIO_s_mem());
  151. BIO_write(bio, key_pem.data(), int(key_pem.size()));
  152. auto pkey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr),
  153. EVP_PKEY_free);
  154. BIO_free(bio);
  155. if (!pkey)
  156. throw std::invalid_argument("Unable to import PEM key");
  157. return Certificate(x509, pkey);
  158. }
  159. Certificate Certificate::FromFile(const string &crt_pem_file, const string &key_pem_file,
  160. const string &pass) {
  161. PLOG_DEBUG << "Importing certificate from PEM file (OpenSSL): " << crt_pem_file;
  162. FILE *file = fopen(crt_pem_file.c_str(), "r");
  163. if (!file)
  164. throw std::invalid_argument("Unable to open PEM certificate file");
  165. auto x509 = shared_ptr<X509>(PEM_read_X509(file, nullptr, nullptr, nullptr), X509_free);
  166. fclose(file);
  167. if (!x509)
  168. throw std::invalid_argument("Unable to import PEM certificate from file");
  169. file = fopen(key_pem_file.c_str(), "r");
  170. if (!file)
  171. throw std::invalid_argument("Unable to open PEM key file");
  172. auto pkey = shared_ptr<EVP_PKEY>(
  173. PEM_read_PrivateKey(file, nullptr, dummy_pass_cb, const_cast<char *>(pass.c_str())),
  174. EVP_PKEY_free);
  175. fclose(file);
  176. if (!pkey)
  177. throw std::invalid_argument("Unable to import PEM key from file");
  178. return Certificate(x509, pkey);
  179. }
  180. Certificate Certificate::Generate(CertificateType type, const string &commonName) {
  181. PLOG_DEBUG << "Generating certificate (OpenSSL)";
  182. shared_ptr<X509> x509(X509_new(), X509_free);
  183. shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
  184. unique_ptr<BIGNUM, decltype(&BN_free)> serial_number(BN_new(), BN_free);
  185. unique_ptr<X509_NAME, decltype(&X509_NAME_free)> name(X509_NAME_new(), X509_NAME_free);
  186. if (!x509 || !pkey || !serial_number || !name)
  187. throw std::runtime_error("Unable to allocate structures for certificate generation");
  188. switch (type) {
  189. // RFC 8827 WebRTC Security Architecture 6.5. Communications Security
  190. // All implementations MUST support DTLS 1.2 with the TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  191. // cipher suite and the P-256 curve
  192. // See https://tools.ietf.org/html/rfc8827#section-6.5
  193. case CertificateType::Default:
  194. case CertificateType::Ecdsa: {
  195. PLOG_VERBOSE << "Generating ECDSA P-256 key pair";
  196. unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ecc(
  197. EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), EC_KEY_free);
  198. if (!ecc)
  199. throw std::runtime_error("Unable to allocate structure for ECDSA P-256 key pair");
  200. EC_KEY_set_asn1_flag(ecc.get(), OPENSSL_EC_NAMED_CURVE); // Set ASN1 OID
  201. if (!EC_KEY_generate_key(ecc.get()) ||
  202. !EVP_PKEY_assign_EC_KEY(pkey.get(),
  203. ecc.release())) // the key will be freed when pkey is freed
  204. throw std::runtime_error("Unable to generate ECDSA P-256 key pair");
  205. break;
  206. }
  207. case CertificateType::Rsa: {
  208. PLOG_VERBOSE << "Generating RSA key pair";
  209. const int bits = 2048;
  210. const unsigned int e = 65537; // 2^16 + 1
  211. unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
  212. unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
  213. if (!rsa || !exponent)
  214. throw std::runtime_error("Unable to allocate structures for RSA key pair");
  215. if (!BN_set_word(exponent.get(), e) ||
  216. !RSA_generate_key_ex(rsa.get(), bits, exponent.get(), NULL) ||
  217. !EVP_PKEY_assign_RSA(pkey.get(),
  218. rsa.release())) // the key will be freed when pkey is freed
  219. throw std::runtime_error("Unable to generate RSA key pair");
  220. break;
  221. }
  222. default:
  223. throw std::invalid_argument("Unknown certificate type");
  224. }
  225. const size_t serialSize = 16;
  226. auto *commonNameBytes =
  227. reinterpret_cast<unsigned char *>(const_cast<char *>(commonName.c_str()));
  228. if (!X509_set_pubkey(x509.get(), pkey.get()))
  229. throw std::runtime_error("Unable to set certificate public key");
  230. if (!X509_gmtime_adj(X509_getm_notBefore(x509.get()), 3600 * -1) ||
  231. !X509_gmtime_adj(X509_getm_notAfter(x509.get()), 3600 * 24 * 365) ||
  232. !X509_set_version(x509.get(), 1) ||
  233. !BN_pseudo_rand(serial_number.get(), serialSize, 0, 0) ||
  234. !BN_to_ASN1_INTEGER(serial_number.get(), X509_get_serialNumber(x509.get())) ||
  235. !X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_UTF8, commonNameBytes, -1,
  236. -1, 0) ||
  237. !X509_set_subject_name(x509.get(), name.get()) ||
  238. !X509_set_issuer_name(x509.get(), name.get()))
  239. throw std::runtime_error("Unable to set certificate properties");
  240. if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
  241. throw std::runtime_error("Unable to auto-sign certificate");
  242. return Certificate(x509, pkey);
  243. }
  244. Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey)
  245. : mX509(std::move(x509)), mPKey(std::move(pkey)), mFingerprint(make_fingerprint(mX509.get())) {}
  246. string Certificate::fingerprint() const { return mFingerprint; }
  247. std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const {
  248. return {mX509.get(), mPKey.get()};
  249. }
  250. string make_fingerprint(X509 *x509) {
  251. const size_t size = 32;
  252. unsigned char buffer[size];
  253. unsigned int len = size;
  254. if (!X509_digest(x509, EVP_sha256(), buffer, &len))
  255. throw std::runtime_error("X509 fingerprint error");
  256. std::ostringstream oss;
  257. oss << std::hex << std::uppercase << std::setfill('0');
  258. for (size_t i = 0; i < len; ++i) {
  259. if (i)
  260. oss << std::setw(1) << ':';
  261. oss << std::setw(2) << unsigned(buffer[i]);
  262. }
  263. return oss.str();
  264. }
  265. #endif
  266. // Common for GnuTLS and OpenSSL
  267. future_certificate_ptr make_certificate(CertificateType type) {
  268. return ThreadPool::Instance().enqueue([type]() {
  269. return std::make_shared<Certificate>(Certificate::Generate(type, "libdatachannel"));
  270. });
  271. }
  272. } // namespace rtc::impl