blob: 47893bfb5e83f3b419ab67a173ba27fd91f83663 [file] [log] [blame]
Ed Tanous0fdddb12017-02-28 11:06:34 -08001#pragma once
Ed Tanous55c7b7a2018-05-22 15:27:24 -07002#ifdef BMCWEB_ENABLE_SSL
Ed Tanous0fdddb12017-02-28 11:06:34 -08003
4#include <openssl/bio.h>
5#include <openssl/dh.h>
6#include <openssl/dsa.h>
Ed Tanous0fdddb12017-02-28 11:06:34 -08007#include <openssl/err.h>
8#include <openssl/evp.h>
9#include <openssl/pem.h>
10#include <openssl/rand.h>
11#include <openssl/rsa.h>
12#include <openssl/ssl.h>
Ed Tanous9b65f1f2017-03-07 15:17:13 -080013
Ed Tanous1abe55e2018-09-05 08:30:59 -070014#include <boost/asio.hpp>
15#include <random>
16
17namespace ensuressl
18{
Ed Tanous55c7b7a2018-05-22 15:27:24 -070019static void initOpenssl();
20static void cleanupOpenssl();
21static EVP_PKEY *createRsaKey();
22static EVP_PKEY *createEcKey();
23static void handleOpensslError();
Ed Tanous0fdddb12017-02-28 11:06:34 -080024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025inline bool verifyOpensslKeyCert(const std::string &filepath)
26{
27 bool privateKeyValid = false;
28 bool certValid = false;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030 std::cout << "Checking certs in file " << filepath << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080031
Ed Tanous1abe55e2018-09-05 08:30:59 -070032 FILE *file = fopen(filepath.c_str(), "r");
33 if (file != NULL)
34 {
35 EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
36 int rc;
37 if (pkey != nullptr)
38 {
39 RSA *rsa = EVP_PKEY_get1_RSA(pkey);
40 if (rsa != nullptr)
41 {
42 std::cout << "Found an RSA key\n";
43 if (RSA_check_key(rsa) == 1)
44 {
45 // private_key_valid = true;
46 }
47 else
48 {
49 std::cerr << "Key not valid error number "
50 << ERR_get_error() << "\n";
51 }
52 RSA_free(rsa);
53 }
54 else
55 {
56 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
57 if (ec != nullptr)
58 {
59 std::cout << "Found an EC key\n";
60 if (EC_KEY_check_key(ec) == 1)
61 {
62 privateKeyValid = true;
63 }
64 else
65 {
66 std::cerr << "Key not valid error number "
67 << ERR_get_error() << "\n";
68 }
69 EC_KEY_free(ec);
70 }
71 }
72
73 if (privateKeyValid)
74 {
75 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL);
76 if (x509 == nullptr)
77 {
78 std::cout << "error getting x509 cert " << ERR_get_error()
79 << "\n";
80 }
81 else
82 {
83 rc = X509_verify(x509, pkey);
84 if (rc == 1)
85 {
86 certValid = true;
87 }
88 else
89 {
90 std::cerr << "Error in verifying private key signature "
91 << ERR_get_error() << "\n";
92 }
93 }
94 }
95
96 EVP_PKEY_free(pkey);
Ed Tanous9b65f1f2017-03-07 15:17:13 -080097 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070098 fclose(file);
Ed Tanous0fdddb12017-02-28 11:06:34 -080099 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 return certValid;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800101}
102
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103inline void generateSslCertificate(const std::string &filepath)
104{
105 FILE *pFile = NULL;
106 std::cout << "Generating new keys\n";
107 initOpenssl();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800108
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 // std::cerr << "Generating RSA key";
110 // EVP_PKEY *pRsaPrivKey = create_rsa_key();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800111
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 std::cerr << "Generating EC key\n";
113 EVP_PKEY *pRsaPrivKey = createEcKey();
114 if (pRsaPrivKey != nullptr)
115 {
116 std::cerr << "Generating x509 Certificate\n";
117 // Use this code to directly generate a certificate
118 X509 *x509;
119 x509 = X509_new();
120 if (x509 != nullptr)
121 {
122 // get a random number from the RNG for the certificate serial
123 // number If this is not random, regenerating certs throws broswer
124 // errors
125 std::random_device rd;
126 int serial = rd();
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800127
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800129
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 // not before this moment
131 X509_gmtime_adj(X509_get_notBefore(x509), 0);
132 // Cert is valid for 10 years
133 X509_gmtime_adj(X509_get_notAfter(x509),
134 60L * 60L * 24L * 365L * 10L);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800135
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 // set the public key to the key we just generated
137 X509_set_pubkey(x509, pRsaPrivKey);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800138
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 // get the subject name
140 X509_NAME *name;
141 name = X509_get_subject_name(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800142
Ed Tanous1abe55e2018-09-05 08:30:59 -0700143 X509_NAME_add_entry_by_txt(
144 name, "C", MBSTRING_ASC,
145 reinterpret_cast<const unsigned char *>("US"), -1, -1, 0);
146 X509_NAME_add_entry_by_txt(
147 name, "O", MBSTRING_ASC,
148 reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1,
149 0);
150 X509_NAME_add_entry_by_txt(
151 name, "CN", MBSTRING_ASC,
152 reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0);
153 // set the CSR options
154 X509_set_issuer_name(x509, name);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800155
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 // Sign the certificate with our private key
157 X509_sign(x509, pRsaPrivKey, EVP_sha256());
Ed Tanous0fdddb12017-02-28 11:06:34 -0800158
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 pFile = fopen(filepath.c_str(), "wt");
Ed Tanous0fdddb12017-02-28 11:06:34 -0800160
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 if (pFile != nullptr)
162 {
163 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0,
164 NULL);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800165
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 PEM_write_X509(pFile, x509);
167 fclose(pFile);
168 pFile = NULL;
169 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800170
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 X509_free(x509);
172 }
173
174 EVP_PKEY_free(pRsaPrivKey);
175 pRsaPrivKey = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800176 }
177
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 // cleanup_openssl();
Ed Tanous99923322017-03-03 14:21:24 -0800179}
180
Ed Tanous1abe55e2018-09-05 08:30:59 -0700181EVP_PKEY *createRsaKey()
182{
183 RSA *pRSA = NULL;
Ed Tanousa1e077c2017-04-25 12:42:19 -0700184#if OPENSSL_VERSION_NUMBER < 0x00908000L
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700186#else
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 RSA_generate_key_ex(pRSA, 2048, NULL, NULL);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700188#endif
189
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 EVP_PKEY *pKey = EVP_PKEY_new();
191 if ((pRSA != nullptr) && (pKey != nullptr) &&
192 EVP_PKEY_assign_RSA(pKey, pRSA))
193 {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700194 /* pKey owns pRSA from now */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195 if (RSA_check_key(pRSA) <= 0)
196 {
197 fprintf(stderr, "RSA_check_key failed.\n");
198 handleOpensslError();
199 EVP_PKEY_free(pKey);
200 pKey = NULL;
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700201 }
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800202 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700203 else
204 {
205 handleOpensslError();
206 if (pRSA != nullptr)
207 {
208 RSA_free(pRSA);
209 pRSA = NULL;
210 }
211 if (pKey != nullptr)
212 {
213 EVP_PKEY_free(pKey);
214 pKey = NULL;
215 }
216 }
217 return pKey;
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800218}
219
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220EVP_PKEY *createEcKey()
221{
222 EVP_PKEY *pKey = NULL;
223 int eccgrp = 0;
224 eccgrp = OBJ_txt2nid("prime256v1");
225
226 EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp);
227 if (myecc != nullptr)
228 {
229 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
230 EC_KEY_generate_key(myecc);
231 pKey = EVP_PKEY_new();
232 if (pKey != nullptr)
233 {
234 if (EVP_PKEY_assign_EC_KEY(pKey, myecc))
235 {
236 /* pKey owns pRSA from now */
237 if (EC_KEY_check_key(myecc) <= 0)
238 {
239 fprintf(stderr, "EC_check_key failed.\n");
240 }
241 }
242 }
243 }
244 return pKey;
245}
246
247void initOpenssl()
248{
Ed Tanous911ac312017-08-15 09:37:42 -0700249#if OPENSSL_VERSION_NUMBER < 0x10100000L
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 SSL_load_error_strings();
251 OpenSSL_add_all_algorithms();
252 RAND_load_file("/dev/urandom", 1024);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700253#endif
Ed Tanous0fdddb12017-02-28 11:06:34 -0800254}
255
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256void cleanupOpenssl()
257{
258 CRYPTO_cleanup_all_ex_data();
259 ERR_free_strings();
Ed Tanousa1e077c2017-04-25 12:42:19 -0700260#if OPENSSL_VERSION_NUMBER < 0x10100000L
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 ERR_remove_thread_state(0);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700262#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 EVP_cleanup();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800264}
265
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266void handleOpensslError()
267{
268 ERR_print_errors_fp(stderr);
269}
270inline void ensureOpensslKeyPresentAndValid(const std::string &filepath)
271{
272 bool pemFileValid = false;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800273
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 pemFileValid = verifyOpensslKeyCert(filepath);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700275
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 if (!pemFileValid)
277 {
278 std::cerr << "Error in verifying signature, regenerating\n";
279 generateSslCertificate(filepath);
280 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800281}
Ed Tanousc4771fb2017-03-13 13:39:49 -0700282
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283inline boost::asio::ssl::context getSslContext(const std::string &ssl_pem_file)
284{
285 boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23};
286 mSslContext.set_options(boost::asio::ssl::context::default_workarounds |
287 boost::asio::ssl::context::no_sslv2 |
288 boost::asio::ssl::context::no_sslv3 |
289 boost::asio::ssl::context::single_dh_use |
290 boost::asio::ssl::context::no_tlsv1 |
291 boost::asio::ssl::context::no_tlsv1_1);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700292
Ed Tanous1abe55e2018-09-05 08:30:59 -0700293 // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
294 mSslContext.use_certificate_file(ssl_pem_file,
295 boost::asio::ssl::context::pem);
296 mSslContext.use_private_key_file(ssl_pem_file,
297 boost::asio::ssl::context::pem);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700298
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 // Set up EC curves to auto (boost asio doesn't have a method for this)
300 // There is a pull request to add this. Once this is included in an asio
301 // drop, use the right way
302 // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
303 if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1)
304 {
305 BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n";
306 }
Ed Tanousc4771fb2017-03-13 13:39:49 -0700307
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308 // From mozilla "compatibility"
309 std::string mozillaCompatibilityCiphers = "ECDHE-ECDSA-CHACHA20-POLY1305:"
310 "ECDHE-RSA-CHACHA20-POLY1305:"
311 "ECDHE-ECDSA-AES128-GCM-SHA256:"
312 "ECDHE-RSA-AES128-GCM-SHA256:"
313 "ECDHE-ECDSA-AES256-GCM-SHA384:"
314 "ECDHE-RSA-AES256-GCM-SHA384:"
315 "DHE-RSA-AES128-GCM-SHA256:"
316 "DHE-RSA-AES256-GCM-SHA384:"
317 "ECDHE-ECDSA-AES128-SHA256:"
318 "ECDHE-RSA-AES128-SHA256:"
319 "ECDHE-ECDSA-AES128-SHA:"
320 "ECDHE-RSA-AES256-SHA384:"
321 "ECDHE-RSA-AES128-SHA:"
322 "ECDHE-ECDSA-AES256-SHA384:"
323 "ECDHE-ECDSA-AES256-SHA:"
324 "ECDHE-RSA-AES256-SHA:"
325 "DHE-RSA-AES128-SHA256:"
326 "DHE-RSA-AES128-SHA:"
327 "DHE-RSA-AES256-SHA256:"
328 "DHE-RSA-AES256-SHA:"
329 "ECDHE-ECDSA-DES-CBC3-SHA:"
330 "ECDHE-RSA-DES-CBC3-SHA:"
331 "EDH-RSA-DES-CBC3-SHA:"
332 "AES128-GCM-SHA256:"
333 "AES256-GCM-SHA384:"
334 "AES128-SHA256:"
335 "AES256-SHA256:"
336 "AES128-SHA:"
337 "AES256-SHA:"
338 "DES-CBC3-SHA:"
339 "!DSS";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700340
Ed Tanous1abe55e2018-09-05 08:30:59 -0700341 // From mozilla "modern"
342 std::string mozillaModernCiphers = "ECDHE-ECDSA-AES256-GCM-SHA384:"
343 "ECDHE-RSA-AES256-GCM-SHA384:"
344 "ECDHE-ECDSA-CHACHA20-POLY1305:"
345 "ECDHE-RSA-CHACHA20-POLY1305:"
346 "ECDHE-ECDSA-AES128-GCM-SHA256:"
347 "ECDHE-RSA-AES128-GCM-SHA256:"
348 "ECDHE-ECDSA-AES256-SHA384:"
349 "ECDHE-RSA-AES256-SHA384:"
350 "ECDHE-ECDSA-AES128-SHA256:"
351 "ECDHE-RSA-AES128-SHA256";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700352
Ed Tanous1abe55e2018-09-05 08:30:59 -0700353 std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700354
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 if (SSL_CTX_set_cipher_list(mSslContext.native_handle(),
356 mozillaCompatibilityCiphers.c_str()) != 1)
357 {
358 BMCWEB_LOG_ERROR << "Error setting cipher list\n";
359 }
360 return mSslContext;
Ed Tanousc4771fb2017-03-13 13:39:49 -0700361}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700362} // namespace ensuressl
Ed Tanous01250f22017-04-18 17:49:51 -0700363
364#endif