1 #pragma once 2 #ifdef CROW_ENABLE_SSL 3 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> 14 #include <random> 15 #include <boost/asio.hpp> 16 17 namespace ensuressl { 18 static void init_openssl(); 19 static void cleanup_openssl(); 20 static EVP_PKEY *create_rsa_key(); 21 static EVP_PKEY *create_ec_key(); 22 static void handle_openssl_error(); 23 24 inline bool verify_openssl_key_cert(const std::string &filepath) { 25 bool private_key_valid = false; 26 bool cert_valid = false; 27 28 std::cout << "Checking certs in file " << filepath << "\n"; 29 30 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; 34 if (pkey != nullptr) { 35 RSA *rsa = EVP_PKEY_get1_RSA(pkey); 36 if (rsa != nullptr) { 37 std::cout << "Found an RSA key\n"; 38 if (RSA_check_key(rsa) == 1) { 39 // private_key_valid = true; 40 } else { 41 std::cerr << "Key not valid error number " << ERR_get_error() << "\n"; 42 } 43 RSA_free(rsa); 44 } else { 45 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); 46 if (ec != nullptr) { 47 std::cout << "Found an EC key\n"; 48 if (EC_KEY_check_key(ec) == 1) { 49 private_key_valid = true; 50 } else { 51 std::cerr << "Key not valid error number " << ERR_get_error() 52 << "\n"; 53 } 54 EC_KEY_free(ec); 55 } 56 } 57 58 if (private_key_valid) { 59 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL); 60 if (x509 == nullptr) { 61 std::cout << "error getting x509 cert " << ERR_get_error() << "\n"; 62 } else { 63 rc = X509_verify(x509, pkey); 64 if (rc == 1) { 65 cert_valid = true; 66 } else { 67 std::cerr << "Error in verifying private key signature " 68 << ERR_get_error() << "\n"; 69 } 70 } 71 } 72 73 EVP_PKEY_free(pkey); 74 } 75 fclose(file); 76 } 77 return cert_valid; 78 } 79 80 inline void generate_ssl_certificate(const std::string &filepath) { 81 FILE *pFile = NULL; 82 std::cout << "Generating new keys\n"; 83 init_openssl(); 84 85 // std::cerr << "Generating RSA key"; 86 // EVP_PKEY *pRsaPrivKey = create_rsa_key(); 87 88 std::cerr << "Generating EC key\n"; 89 EVP_PKEY *pRsaPrivKey = create_ec_key(); 90 if (pRsaPrivKey != nullptr) { 91 std::cerr << "Generating x509 Certificate\n"; 92 // Use this code to directly generate a certificate 93 X509 *x509; 94 x509 = X509_new(); 95 if (x509 != nullptr) { 96 // 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(); 100 101 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); 102 103 // 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); 107 108 // set the public key to the key we just generated 109 X509_set_pubkey(x509, pRsaPrivKey); 110 111 // Get the subject name 112 X509_NAME *name; 113 name = X509_get_subject_name(x509); 114 115 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, 116 reinterpret_cast<const unsigned char *>("US"), 117 -1, -1, 0); 118 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); 124 // set the CSR options 125 X509_set_issuer_name(x509, name); 126 127 // Sign the certificate with our private key 128 X509_sign(x509, pRsaPrivKey, EVP_sha256()); 129 130 pFile = fopen(filepath.c_str(), "wt"); 131 132 if (pFile != nullptr) { 133 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL); 134 135 PEM_write_X509(pFile, x509); 136 fclose(pFile); 137 pFile = NULL; 138 } 139 140 X509_free(x509); 141 } 142 143 EVP_PKEY_free(pRsaPrivKey); 144 pRsaPrivKey = NULL; 145 } 146 147 // cleanup_openssl(); 148 } 149 150 EVP_PKEY *create_rsa_key() { 151 RSA *pRSA = NULL; 152 #if OPENSSL_VERSION_NUMBER < 0x00908000L 153 pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL); 154 #else 155 RSA_generate_key_ex(pRSA, 2048, NULL, NULL); 156 #endif 157 158 EVP_PKEY *pKey = EVP_PKEY_new(); 159 if ((pRSA != nullptr) && (pKey != nullptr) && 160 EVP_PKEY_assign_RSA(pKey, pRSA)) { 161 /* 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; 167 } 168 } else { 169 handle_openssl_error(); 170 if (pRSA != nullptr) { 171 RSA_free(pRSA); 172 pRSA = NULL; 173 } 174 if (pKey != nullptr) { 175 EVP_PKEY_free(pKey); 176 pKey = NULL; 177 } 178 } 179 return pKey; 180 } 181 182 EVP_PKEY *create_ec_key() { 183 EVP_PKEY *pKey = NULL; 184 int eccgrp = 0; 185 eccgrp = OBJ_txt2nid("prime256v1"); 186 187 EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp); 188 if (myecc != nullptr) { 189 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); 190 EC_KEY_generate_key(myecc); 191 pKey = EVP_PKEY_new(); 192 if (pKey != nullptr) { 193 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 } 199 } 200 } 201 return pKey; 202 } 203 204 void 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); 209 #endif 210 } 211 212 void cleanup_openssl() { 213 CRYPTO_cleanup_all_ex_data(); 214 ERR_free_strings(); 215 #if OPENSSL_VERSION_NUMBER < 0x10100000L 216 ERR_remove_thread_state(0); 217 #endif 218 EVP_cleanup(); 219 } 220 221 void handle_openssl_error() { ERR_print_errors_fp(stderr); } 222 inline void ensure_openssl_key_present_and_valid(const std::string &filepath) { 223 bool pem_file_valid = false; 224 225 pem_file_valid = verify_openssl_key_cert(filepath); 226 227 if (!pem_file_valid) { 228 std::cerr << "Error in verifying signature, regenerating\n"; 229 generate_ssl_certificate(filepath); 230 } 231 } 232 233 inline boost::asio::ssl::context get_ssl_context( 234 const std::string &ssl_pem_file) { 235 boost::asio::ssl::context m_ssl_context{boost::asio::ssl::context::sslv23}; 236 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); 242 243 // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); 244 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); 248 249 // Set up EC curves to auto (boost asio doesn't have a method for this) 250 // There is a pull request to add this. Once this is included in an asio 251 // drop, use the right way 252 // 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" 258 std::string mozilla_compatibility_ciphers = 259 "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" 292 std::string mozilla_modern_ciphers = 293 "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 304 std::string aes_only_ciphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL"; 305 306 if (SSL_CTX_set_cipher_list(m_ssl_context.native_handle(), 307 mozilla_compatibility_ciphers.c_str()) != 1) { 308 CROW_LOG_ERROR << "Error setting cipher list\n"; 309 } 310 return m_ssl_context; 311 } 312 } // namespace ensuressl 313 314 #endif