blob: 2a1a8524735b73f4b73d6d402d210077b2acaa4d [file] [log] [blame]
Ed Tanous0fdddb12017-02-28 11:06:34 -08001#pragma once
Ed Tanous01250f22017-04-18 17:49:51 -07002#ifdef CROW_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>
7#include <openssl/dsa.h>
8#include <openssl/err.h>
9#include <openssl/evp.h>
10#include <openssl/pem.h>
11#include <openssl/rand.h>
12#include <openssl/rsa.h>
13#include <openssl/ssl.h>
Ed Tanous1ff48782017-04-18 12:45:08 -070014#include <random>
Ed Tanousa1e077c2017-04-25 12:42:19 -070015#include <boost/asio.hpp>
Ed Tanous9b65f1f2017-03-07 15:17:13 -080016
Ed Tanous99923322017-03-03 14:21:24 -080017namespace ensuressl {
Ed Tanous911ac312017-08-15 09:37:42 -070018static void init_openssl();
19static void cleanup_openssl();
20static EVP_PKEY *create_rsa_key();
21static EVP_PKEY *create_ec_key();
22static void handle_openssl_error();
Ed Tanous0fdddb12017-02-28 11:06:34 -080023
Ed Tanous99923322017-03-03 14:21:24 -080024inline bool verify_openssl_key_cert(const std::string &filepath) {
25 bool private_key_valid = false;
26 bool cert_valid = false;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080027
Ed Tanous4d92cbf2017-06-22 15:41:02 -070028 std::cout << "Checking certs in file " << filepath << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080029
Ed Tanous99923322017-03-03 14:21:24 -080030 FILE *file = fopen(filepath.c_str(), "r");
31 if (file != NULL) {
32 EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
33 int rc;
Ed Tanous911ac312017-08-15 09:37:42 -070034 if (pkey != nullptr) {
Ed Tanousa1e077c2017-04-25 12:42:19 -070035 RSA *rsa = EVP_PKEY_get1_RSA(pkey);
Ed Tanous911ac312017-08-15 09:37:42 -070036 if (rsa != nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070037 std::cout << "Found an RSA key\n";
Ed Tanousa1e077c2017-04-25 12:42:19 -070038 if (RSA_check_key(rsa) == 1) {
39 // private_key_valid = true;
40 } else {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070041 std::cerr << "Key not valid error number " << ERR_get_error() << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080042 }
Ed Tanousa1e077c2017-04-25 12:42:19 -070043 RSA_free(rsa);
44 } else {
45 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
Ed Tanous911ac312017-08-15 09:37:42 -070046 if (ec != nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070047 std::cout << "Found an EC key\n";
Ed Tanousa1e077c2017-04-25 12:42:19 -070048 if (EC_KEY_check_key(ec) == 1) {
49 private_key_valid = true;
50 } else {
Ed Tanous911ac312017-08-15 09:37:42 -070051 std::cerr << "Key not valid error number " << ERR_get_error()
52 << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080053 }
Ed Tanousa1e077c2017-04-25 12:42:19 -070054 EC_KEY_free(ec);
Ed Tanous0fdddb12017-02-28 11:06:34 -080055 }
Ed Tanous99923322017-03-03 14:21:24 -080056 }
57
58 if (private_key_valid) {
59 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL);
Ed Tanous911ac312017-08-15 09:37:42 -070060 if (x509 == nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070061 std::cout << "error getting x509 cert " << ERR_get_error() << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080062 } else {
63 rc = X509_verify(x509, pkey);
64 if (rc == 1) {
65 cert_valid = true;
66 } else {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070067 std::cerr << "Error in verifying private key signature "
Ed Tanous911ac312017-08-15 09:37:42 -070068 << ERR_get_error() << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080069 }
Ed Tanous99923322017-03-03 14:21:24 -080070 }
71 }
72
73 EVP_PKEY_free(pkey);
Ed Tanous0fdddb12017-02-28 11:06:34 -080074 }
Ed Tanous99923322017-03-03 14:21:24 -080075 fclose(file);
76 }
77 return cert_valid;
Ed Tanous0fdddb12017-02-28 11:06:34 -080078}
79
Ed Tanous99923322017-03-03 14:21:24 -080080inline void generate_ssl_certificate(const std::string &filepath) {
Ed Tanous99923322017-03-03 14:21:24 -080081 FILE *pFile = NULL;
Ed Tanous4d92cbf2017-06-22 15:41:02 -070082 std::cout << "Generating new keys\n";
Ed Tanous99923322017-03-03 14:21:24 -080083 init_openssl();
Ed Tanous0fdddb12017-02-28 11:06:34 -080084
Ed Tanous4d92cbf2017-06-22 15:41:02 -070085 // std::cerr << "Generating RSA key";
Ed Tanous1ccd57c2017-03-21 13:15:58 -070086 // EVP_PKEY *pRsaPrivKey = create_rsa_key();
Ed Tanous0fdddb12017-02-28 11:06:34 -080087
Ed Tanous4d92cbf2017-06-22 15:41:02 -070088 std::cerr << "Generating EC key\n";
Ed Tanousc4771fb2017-03-13 13:39:49 -070089 EVP_PKEY *pRsaPrivKey = create_ec_key();
Ed Tanous911ac312017-08-15 09:37:42 -070090 if (pRsaPrivKey != nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070091 std::cerr << "Generating x509 Certificate\n";
Ed Tanous1ccd57c2017-03-21 13:15:58 -070092 // Use this code to directly generate a certificate
93 X509 *x509;
94 x509 = X509_new();
Ed Tanous911ac312017-08-15 09:37:42 -070095 if (x509 != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -070096 // Get a random number from the RNG for the certificate serial number
97 // If this is not random, regenerating certs throws broswer errors
98 std::random_device rd;
99 int serial = rd();
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800100
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700101 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800102
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700103 // not before this moment
104 X509_gmtime_adj(X509_get_notBefore(x509), 0);
105 // Cert is valid for 10 years
106 X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * 365L * 10L);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800107
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700108 // set the public key to the key we just generated
109 X509_set_pubkey(x509, pRsaPrivKey);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800110
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700111 // Get the subject name
112 X509_NAME *name;
113 name = X509_get_subject_name(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800114
Ed Tanous911ac312017-08-15 09:37:42 -0700115 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
116 reinterpret_cast<const unsigned char *>("US"),
Ed Tanous1ff48782017-04-18 12:45:08 -0700117 -1, -1, 0);
Ed Tanous911ac312017-08-15 09:37:42 -0700118 X509_NAME_add_entry_by_txt(
119 name, "O", MBSTRING_ASC,
120 reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, 0);
121 X509_NAME_add_entry_by_txt(
122 name, "CN", MBSTRING_ASC,
123 reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700124 // set the CSR options
125 X509_set_issuer_name(x509, name);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800126
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700127 // Sign the certificate with our private key
128 X509_sign(x509, pRsaPrivKey, EVP_sha256());
Ed Tanous0fdddb12017-02-28 11:06:34 -0800129
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700130 pFile = fopen(filepath.c_str(), "wt");
Ed Tanous0fdddb12017-02-28 11:06:34 -0800131
Ed Tanous911ac312017-08-15 09:37:42 -0700132 if (pFile != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700133 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800134
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700135 PEM_write_X509(pFile, x509);
136 fclose(pFile);
137 pFile = NULL;
138 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800139
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700140 X509_free(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800141 }
142
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800143 EVP_PKEY_free(pRsaPrivKey);
144 pRsaPrivKey = NULL;
Ed Tanous99923322017-03-03 14:21:24 -0800145 }
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700146
Ed Tanous99923322017-03-03 14:21:24 -0800147 // cleanup_openssl();
148}
149
Ed Tanous911ac312017-08-15 09:37:42 -0700150EVP_PKEY *create_rsa_key() {
Ed Tanous99923322017-03-03 14:21:24 -0800151 RSA *pRSA = NULL;
Ed Tanousa1e077c2017-04-25 12:42:19 -0700152#if OPENSSL_VERSION_NUMBER < 0x00908000L
Ed Tanous99923322017-03-03 14:21:24 -0800153 pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700154#else
155 RSA_generate_key_ex(pRSA, 2048, NULL, NULL);
156#endif
157
158 EVP_PKEY *pKey = EVP_PKEY_new();
Ed Tanous911ac312017-08-15 09:37:42 -0700159 if ((pRSA != nullptr) && (pKey != nullptr) &&
160 EVP_PKEY_assign_RSA(pKey, pRSA)) {
Ed Tanous99923322017-03-03 14:21:24 -0800161 /* pKey owns pRSA from now */
162 if (RSA_check_key(pRSA) <= 0) {
163 fprintf(stderr, "RSA_check_key failed.\n");
164 handle_openssl_error();
165 EVP_PKEY_free(pKey);
166 pKey = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800167 }
Ed Tanous99923322017-03-03 14:21:24 -0800168 } else {
169 handle_openssl_error();
Ed Tanous911ac312017-08-15 09:37:42 -0700170 if (pRSA != nullptr) {
Ed Tanous99923322017-03-03 14:21:24 -0800171 RSA_free(pRSA);
172 pRSA = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800173 }
Ed Tanous911ac312017-08-15 09:37:42 -0700174 if (pKey != nullptr) {
Ed Tanous99923322017-03-03 14:21:24 -0800175 EVP_PKEY_free(pKey);
176 pKey = NULL;
177 }
178 }
179 return pKey;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800180}
181
Ed Tanous911ac312017-08-15 09:37:42 -0700182EVP_PKEY *create_ec_key() {
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800183 EVP_PKEY *pKey = NULL;
184 int eccgrp = 0;
185 eccgrp = OBJ_txt2nid("prime256v1");
186
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700187 EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp);
Ed Tanous911ac312017-08-15 09:37:42 -0700188 if (myecc != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700189 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
190 EC_KEY_generate_key(myecc);
Ed Tanous01250f22017-04-18 17:49:51 -0700191 pKey = EVP_PKEY_new();
Ed Tanous911ac312017-08-15 09:37:42 -0700192 if (pKey != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700193 if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) {
194 /* pKey owns pRSA from now */
195 if (EC_KEY_check_key(myecc) <= 0) {
196 fprintf(stderr, "EC_check_key failed.\n");
197 }
198 }
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800199 }
200 }
201 return pKey;
202}
203
Ed Tanous911ac312017-08-15 09:37:42 -0700204void init_openssl() {
205#if OPENSSL_VERSION_NUMBER < 0x10100000L
206 SSL_load_error_strings();
207 OpenSSL_add_all_algorithms();
208 RAND_load_file("/dev/urandom", 1024);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700209#endif
Ed Tanous0fdddb12017-02-28 11:06:34 -0800210}
211
Ed Tanous911ac312017-08-15 09:37:42 -0700212void cleanup_openssl() {
Ed Tanous99923322017-03-03 14:21:24 -0800213 CRYPTO_cleanup_all_ex_data();
214 ERR_free_strings();
Ed Tanousa1e077c2017-04-25 12:42:19 -0700215#if OPENSSL_VERSION_NUMBER < 0x10100000L
Ed Tanous99923322017-03-03 14:21:24 -0800216 ERR_remove_thread_state(0);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700217#endif
Ed Tanous99923322017-03-03 14:21:24 -0800218 EVP_cleanup();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800219}
220
Ed Tanous911ac312017-08-15 09:37:42 -0700221void handle_openssl_error() { ERR_print_errors_fp(stderr); }
Ed Tanous99923322017-03-03 14:21:24 -0800222inline void ensure_openssl_key_present_and_valid(const std::string &filepath) {
223 bool pem_file_valid = false;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800224
Ed Tanous99923322017-03-03 14:21:24 -0800225 pem_file_valid = verify_openssl_key_cert(filepath);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700226
Ed Tanous99923322017-03-03 14:21:24 -0800227 if (!pem_file_valid) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700228 std::cerr << "Error in verifying signature, regenerating\n";
Ed Tanous99923322017-03-03 14:21:24 -0800229 generate_ssl_certificate(filepath);
230 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800231}
Ed Tanousc4771fb2017-03-13 13:39:49 -0700232
Ed Tanous911ac312017-08-15 09:37:42 -0700233inline boost::asio::ssl::context get_ssl_context(
234 const std::string &ssl_pem_file) {
Ed Tanousc4771fb2017-03-13 13:39:49 -0700235 boost::asio::ssl::context m_ssl_context{boost::asio::ssl::context::sslv23};
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700236 m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds |
237 boost::asio::ssl::context::no_sslv2 |
238 boost::asio::ssl::context::no_sslv3 |
239 boost::asio::ssl::context::single_dh_use |
240 boost::asio::ssl::context::no_tlsv1 |
241 boost::asio::ssl::context::no_tlsv1_1);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700242
243 // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700244 m_ssl_context.use_certificate_file(ssl_pem_file,
245 boost::asio::ssl::context::pem);
246 m_ssl_context.use_private_key_file(ssl_pem_file,
247 boost::asio::ssl::context::pem);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700248
249 // Set up EC curves to auto (boost asio doesn't have a method for this)
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700250 // There is a pull request to add this. Once this is included in an asio
251 // drop, use the right way
Ed Tanousc4771fb2017-03-13 13:39:49 -0700252 // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
253 if (SSL_CTX_set_ecdh_auto(m_ssl_context.native_handle(), 1) != 1) {
254 CROW_LOG_ERROR << "Error setting tmp ecdh list\n";
255 }
256
257 // From mozilla "compatibility"
Ed Tanous911ac312017-08-15 09:37:42 -0700258 std::string mozilla_compatibility_ciphers =
Ed Tanousc4771fb2017-03-13 13:39:49 -0700259 "ECDHE-ECDSA-CHACHA20-POLY1305:"
260 "ECDHE-RSA-CHACHA20-POLY1305:"
261 "ECDHE-ECDSA-AES128-GCM-SHA256:"
262 "ECDHE-RSA-AES128-GCM-SHA256:"
263 "ECDHE-ECDSA-AES256-GCM-SHA384:"
264 "ECDHE-RSA-AES256-GCM-SHA384:"
265 "DHE-RSA-AES128-GCM-SHA256:"
266 "DHE-RSA-AES256-GCM-SHA384:"
267 "ECDHE-ECDSA-AES128-SHA256:"
268 "ECDHE-RSA-AES128-SHA256:"
269 "ECDHE-ECDSA-AES128-SHA:"
270 "ECDHE-RSA-AES256-SHA384:"
271 "ECDHE-RSA-AES128-SHA:"
272 "ECDHE-ECDSA-AES256-SHA384:"
273 "ECDHE-ECDSA-AES256-SHA:"
274 "ECDHE-RSA-AES256-SHA:"
275 "DHE-RSA-AES128-SHA256:"
276 "DHE-RSA-AES128-SHA:"
277 "DHE-RSA-AES256-SHA256:"
278 "DHE-RSA-AES256-SHA:"
279 "ECDHE-ECDSA-DES-CBC3-SHA:"
280 "ECDHE-RSA-DES-CBC3-SHA:"
281 "EDH-RSA-DES-CBC3-SHA:"
282 "AES128-GCM-SHA256:"
283 "AES256-GCM-SHA384:"
284 "AES128-SHA256:"
285 "AES256-SHA256:"
286 "AES128-SHA:"
287 "AES256-SHA:"
288 "DES-CBC3-SHA:"
289 "!DSS";
290
291 // From mozilla "modern"
Ed Tanous911ac312017-08-15 09:37:42 -0700292 std::string mozilla_modern_ciphers =
Ed Tanousc4771fb2017-03-13 13:39:49 -0700293 "ECDHE-ECDSA-AES256-GCM-SHA384:"
294 "ECDHE-RSA-AES256-GCM-SHA384:"
295 "ECDHE-ECDSA-CHACHA20-POLY1305:"
296 "ECDHE-RSA-CHACHA20-POLY1305:"
297 "ECDHE-ECDSA-AES128-GCM-SHA256:"
298 "ECDHE-RSA-AES128-GCM-SHA256:"
299 "ECDHE-ECDSA-AES256-SHA384:"
300 "ECDHE-RSA-AES256-SHA384:"
301 "ECDHE-ECDSA-AES128-SHA256:"
302 "ECDHE-RSA-AES128-SHA256";
303
Ed Tanous911ac312017-08-15 09:37:42 -0700304 std::string aes_only_ciphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700305
Ed Tanous911ac312017-08-15 09:37:42 -0700306 if (SSL_CTX_set_cipher_list(m_ssl_context.native_handle(),
307 mozilla_compatibility_ciphers.c_str()) != 1) {
Ed Tanousc4771fb2017-03-13 13:39:49 -0700308 CROW_LOG_ERROR << "Error setting cipher list\n";
309 }
310 return m_ssl_context;
311}
Ed Tanous911ac312017-08-15 09:37:42 -0700312} // namespace ensuressl
Ed Tanous01250f22017-04-18 17:49:51 -0700313
314#endif