1 #pragma once 2 3 #include <openssl/bio.h> 4 #include <openssl/dh.h> 5 #include <openssl/dsa.h> 6 #include <openssl/dsa.h> 7 #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> 13 #include <boost/asio.hpp> 14 #include <g3log/g3log.hpp> 15 #include <random> 16 17 namespace ensuressl { 18 static void init_openssl(void); 19 static void cleanup_openssl(void); 20 static EVP_PKEY *create_rsa_key(void); 21 static EVP_PKEY *create_ec_key(void); 22 static void handle_openssl_error(void); 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 LOG(DEBUG) << "Checking certs in file " << filepath; 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) { 35 int type = EVP_PKEY_type(pkey->type); 36 switch (type) { 37 case EVP_PKEY_RSA: 38 case EVP_PKEY_RSA2: { 39 LOG(DEBUG) << "Found an RSA key"; 40 RSA *rsa = EVP_PKEY_get1_RSA(pkey); 41 if (rsa) { 42 if (RSA_check_key(rsa) == 1) { 43 // private_key_valid = true; 44 } else { 45 LOG(WARNING) << "Key not valid error number " << ERR_get_error(); 46 } 47 RSA_free(rsa); 48 } 49 break; 50 } 51 case EVP_PKEY_EC: { 52 LOG(DEBUG) << "Found an EC key"; 53 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); 54 if (ec) { 55 if (EC_KEY_check_key(ec) == 1) { 56 private_key_valid = true; 57 } else { 58 LOG(WARNING) << "Key not valid error number " << ERR_get_error(); 59 } 60 EC_KEY_free(ec); 61 } 62 break; 63 } 64 default: 65 LOG(WARNING) << "Found an unrecognized key type " << type; 66 break; 67 } 68 69 if (private_key_valid) { 70 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL); 71 if (!x509) { 72 LOG(DEBUG) << "error getting x509 cert " << ERR_get_error(); 73 } else { 74 rc = X509_verify(x509, pkey); 75 if (rc == 1) { 76 cert_valid = true; 77 } else { 78 LOG(WARNING) << "Error in verifying private key signature " 79 << ERR_get_error(); 80 } 81 } 82 } 83 84 EVP_PKEY_free(pkey); 85 } 86 fclose(file); 87 } 88 return cert_valid; 89 } 90 91 inline void generate_ssl_certificate(const std::string &filepath) { 92 FILE *pFile = NULL; 93 LOG(WARNING) << "Generating new keys"; 94 init_openssl(); 95 96 // LOG(WARNING) << "Generating RSA key"; 97 // EVP_PKEY *pRsaPrivKey = create_rsa_key(); 98 99 LOG(WARNING) << "Generating EC key"; 100 EVP_PKEY *pRsaPrivKey = create_ec_key(); 101 if (pRsaPrivKey) { 102 LOG(WARNING) << "Generating x509 Certificate"; 103 // Use this code to directly generate a certificate 104 X509 *x509; 105 x509 = X509_new(); 106 if (x509) { 107 // Get a random number from the RNG for the certificate serial number 108 // If this is not random, regenerating certs throws broswer errors 109 std::random_device rd; 110 int serial = rd(); 111 112 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); 113 114 // not before this moment 115 X509_gmtime_adj(X509_get_notBefore(x509), 0); 116 // Cert is valid for 10 years 117 X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * 365L * 10L); 118 119 // set the public key to the key we just generated 120 X509_set_pubkey(x509, pRsaPrivKey); 121 122 // Get the subject name 123 X509_NAME *name; 124 name = X509_get_subject_name(x509); 125 126 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"US", 127 -1, -1, 0); 128 X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, 129 (unsigned char *)"Intel BMC", -1, -1, 0); 130 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, 131 (unsigned char *)"testhost", -1, -1, 0); 132 // set the CSR options 133 X509_set_issuer_name(x509, name); 134 135 // Sign the certificate with our private key 136 X509_sign(x509, pRsaPrivKey, EVP_sha256()); 137 138 pFile = fopen(filepath.c_str(), "wt"); 139 140 if (pFile) { 141 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL); 142 143 PEM_write_X509(pFile, x509); 144 fclose(pFile); 145 pFile = NULL; 146 } 147 148 X509_free(x509); 149 } 150 151 EVP_PKEY_free(pRsaPrivKey); 152 pRsaPrivKey = NULL; 153 } 154 155 // cleanup_openssl(); 156 } 157 158 EVP_PKEY *create_rsa_key(void) { 159 RSA *pRSA = NULL; 160 EVP_PKEY *pKey = NULL; 161 pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL); 162 pKey = EVP_PKEY_new(); 163 if (pRSA && pKey && EVP_PKEY_assign_RSA(pKey, pRSA)) { 164 /* pKey owns pRSA from now */ 165 if (RSA_check_key(pRSA) <= 0) { 166 fprintf(stderr, "RSA_check_key failed.\n"); 167 handle_openssl_error(); 168 EVP_PKEY_free(pKey); 169 pKey = NULL; 170 } 171 } else { 172 handle_openssl_error(); 173 if (pRSA) { 174 RSA_free(pRSA); 175 pRSA = NULL; 176 } 177 if (pKey) { 178 EVP_PKEY_free(pKey); 179 pKey = NULL; 180 } 181 } 182 return pKey; 183 } 184 185 EVP_PKEY *create_ec_key(void) { 186 EVP_PKEY *pKey = NULL; 187 int eccgrp = 0; 188 eccgrp = OBJ_txt2nid("prime256v1"); 189 190 EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp); 191 if (myecc) { 192 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); 193 EC_KEY_generate_key(myecc); 194 EVP_PKEY *pKey = EVP_PKEY_new(); 195 if (pKey) { 196 if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) { 197 /* pKey owns pRSA from now */ 198 if (EC_KEY_check_key(myecc) <= 0) { 199 fprintf(stderr, "EC_check_key failed.\n"); 200 } 201 } 202 EVP_PKEY_free(pKey); 203 pKey = NULL; 204 } 205 EC_KEY_free(myecc); 206 myecc = NULL; 207 } 208 return pKey; 209 } 210 211 void init_openssl(void) { 212 if (SSL_library_init()) { 213 SSL_load_error_strings(); 214 OpenSSL_add_all_algorithms(); 215 RAND_load_file("/dev/urandom", 1024); 216 } else 217 exit(EXIT_FAILURE); 218 } 219 220 void cleanup_openssl(void) { 221 CRYPTO_cleanup_all_ex_data(); 222 ERR_free_strings(); 223 ERR_remove_thread_state(0); 224 EVP_cleanup(); 225 } 226 227 void handle_openssl_error(void) { ERR_print_errors_fp(stderr); } 228 inline void ensure_openssl_key_present_and_valid(const std::string &filepath) { 229 bool pem_file_valid = false; 230 231 pem_file_valid = verify_openssl_key_cert(filepath); 232 233 if (!pem_file_valid) { 234 LOG(WARNING) << "Error in verifying signature, regenerating"; 235 generate_ssl_certificate(filepath); 236 } 237 } 238 239 boost::asio::ssl::context get_ssl_context(std::string ssl_pem_file) { 240 boost::asio::ssl::context m_ssl_context{boost::asio::ssl::context::sslv23}; 241 m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds | 242 boost::asio::ssl::context::no_sslv2 | 243 boost::asio::ssl::context::no_sslv3 | 244 boost::asio::ssl::context::single_dh_use | 245 boost::asio::ssl::context::no_tlsv1 | 246 boost::asio::ssl::context::no_tlsv1_1); 247 248 // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); 249 m_ssl_context.use_certificate_file(ssl_pem_file, 250 boost::asio::ssl::context::pem); 251 m_ssl_context.use_private_key_file(ssl_pem_file, 252 boost::asio::ssl::context::pem); 253 254 // Set up EC curves to auto (boost asio doesn't have a method for this) 255 // There is a pull request to add this. Once this is included in an asio 256 // drop, use the right way 257 // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue 258 if (SSL_CTX_set_ecdh_auto(m_ssl_context.native_handle(), 1) != 1) { 259 CROW_LOG_ERROR << "Error setting tmp ecdh list\n"; 260 } 261 262 // From mozilla "compatibility" 263 std::string ciphers = 264 "ECDHE-ECDSA-CHACHA20-POLY1305:" 265 "ECDHE-RSA-CHACHA20-POLY1305:" 266 "ECDHE-ECDSA-AES128-GCM-SHA256:" 267 "ECDHE-RSA-AES128-GCM-SHA256:" 268 "ECDHE-ECDSA-AES256-GCM-SHA384:" 269 "ECDHE-RSA-AES256-GCM-SHA384:" 270 "DHE-RSA-AES128-GCM-SHA256:" 271 "DHE-RSA-AES256-GCM-SHA384:" 272 "ECDHE-ECDSA-AES128-SHA256:" 273 "ECDHE-RSA-AES128-SHA256:" 274 "ECDHE-ECDSA-AES128-SHA:" 275 "ECDHE-RSA-AES256-SHA384:" 276 "ECDHE-RSA-AES128-SHA:" 277 "ECDHE-ECDSA-AES256-SHA384:" 278 "ECDHE-ECDSA-AES256-SHA:" 279 "ECDHE-RSA-AES256-SHA:" 280 "DHE-RSA-AES128-SHA256:" 281 "DHE-RSA-AES128-SHA:" 282 "DHE-RSA-AES256-SHA256:" 283 "DHE-RSA-AES256-SHA:" 284 "ECDHE-ECDSA-DES-CBC3-SHA:" 285 "ECDHE-RSA-DES-CBC3-SHA:" 286 "EDH-RSA-DES-CBC3-SHA:" 287 "AES128-GCM-SHA256:" 288 "AES256-GCM-SHA384:" 289 "AES128-SHA256:" 290 "AES256-SHA256:" 291 "AES128-SHA:" 292 "AES256-SHA:" 293 "DES-CBC3-SHA:" 294 "!DSS"; 295 296 // From mozilla "modern" 297 std::string modern_ciphers = 298 "ECDHE-ECDSA-AES256-GCM-SHA384:" 299 "ECDHE-RSA-AES256-GCM-SHA384:" 300 "ECDHE-ECDSA-CHACHA20-POLY1305:" 301 "ECDHE-RSA-CHACHA20-POLY1305:" 302 "ECDHE-ECDSA-AES128-GCM-SHA256:" 303 "ECDHE-RSA-AES128-GCM-SHA256:" 304 "ECDHE-ECDSA-AES256-SHA384:" 305 "ECDHE-RSA-AES256-SHA384:" 306 "ECDHE-ECDSA-AES128-SHA256:" 307 "ECDHE-RSA-AES128-SHA256"; 308 309 std::string lighttp_ciphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL"; 310 311 if (SSL_CTX_set_cipher_list(m_ssl_context.native_handle(), ciphers.c_str()) != 312 1) { 313 CROW_LOG_ERROR << "Error setting cipher list\n"; 314 } 315 return m_ssl_context; 316 } 317 }