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