xref: /openbmc/bmcweb/include/ssl_key_handler.hpp (revision 911ac31759cb7b77a856af8806b4e064d50d7422)
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