xref: /openbmc/bmcweb/include/ssl_key_handler.hpp (revision 9b65f1fdcfcaaa22719a83be26dbcb5dbe855b0c)
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 
14 #include <g3log/g3log.hpp>
15 
16 namespace ensuressl {
17 static void init_openssl(void);
18 static void cleanup_openssl(void);
19 static EVP_PKEY *create_rsa_key(void);
20 static EVP_PKEY *create_ec_key(void);
21 static void handle_openssl_error(void);
22 
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 " << ERR_get_error();
79           }
80         }
81       }
82 
83       EVP_PKEY_free(pkey);
84     }
85     fclose(file);
86   }
87   return cert_valid;
88 }
89 
90 inline void generate_ssl_certificate(const std::string &filepath) {
91   FILE *pFile = NULL;
92   LOG(WARNING) << "Generating new keys";
93   init_openssl();
94 
95   LOG(WARNING) << "Generating RSA key";
96   EVP_PKEY *pRsaPrivKey = create_rsa_key();
97 
98   //LOG(WARNING) << "Generating EC key";
99   //EVP_PKEY *pRsaPrivKey = create_ec_key();
100 
101   LOG(WARNING) << "Generating x509 Certificate";
102   // Use this code to directly generate a certificate
103   X509 *x509;
104   x509 = X509_new();
105   if (x509) {
106 
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", -1, -1, 0);
127     X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"Intel BMC", -1, -1, 0);
128     X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"testhost", -1, -1, 0);
129     // set the CSR options
130     X509_set_issuer_name(x509, name);
131 
132     // Sign the certificate with our private key
133     X509_sign(x509, pRsaPrivKey, EVP_sha256());
134 
135     pFile = fopen(filepath.c_str(), "wt");
136 
137     if (pFile) {
138       PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL);
139 
140       PEM_write_X509(pFile, x509);
141       fclose(pFile);
142       pFile = NULL;
143     }
144 
145     X509_free(x509);
146   }
147 
148   if (pRsaPrivKey) {
149     EVP_PKEY_free(pRsaPrivKey);
150     pRsaPrivKey = NULL;
151   }
152 
153   // cleanup_openssl();
154 }
155 
156 EVP_PKEY *create_rsa_key(void) {
157   RSA *pRSA = NULL;
158   EVP_PKEY *pKey = NULL;
159   pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
160   pKey = EVP_PKEY_new();
161   if (pRSA && pKey && EVP_PKEY_assign_RSA(pKey, pRSA)) {
162     /* pKey owns pRSA from now */
163     if (RSA_check_key(pRSA) <= 0) {
164       fprintf(stderr, "RSA_check_key failed.\n");
165       handle_openssl_error();
166       EVP_PKEY_free(pKey);
167       pKey = NULL;
168     }
169   } else {
170     handle_openssl_error();
171     if (pRSA) {
172       RSA_free(pRSA);
173       pRSA = NULL;
174     }
175     if (pKey) {
176       EVP_PKEY_free(pKey);
177       pKey = NULL;
178     }
179   }
180   return pKey;
181 }
182 
183 EVP_PKEY *create_ec_key(void) {
184   EC_KEY *myecc = NULL;
185   EVP_PKEY *pKey = NULL;
186   int eccgrp = 0;
187   eccgrp = OBJ_txt2nid("prime256v1");
188 
189   // TODO handle errors
190   myecc = EC_KEY_new_by_curve_name(eccgrp);
191   EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
192   EC_KEY_generate_key(myecc);
193   pKey = EVP_PKEY_new();
194 
195   if (myecc && pKey && EVP_PKEY_assign_EC_KEY(pKey, myecc)) {
196     /* pKey owns pRSA from now */
197     if (EC_KEY_check_key(myecc) <= 0) {
198       fprintf(stderr, "EC_check_key failed.\n");
199       handle_openssl_error();
200       EVP_PKEY_free(pKey);
201       pKey = NULL;
202     }
203   } else {
204     handle_openssl_error();
205     if (myecc) {
206       EC_KEY_free(myecc);
207       myecc = NULL;
208     }
209     if (pKey) {
210       EVP_PKEY_free(pKey);
211       pKey = NULL;
212     }
213   }
214   return pKey;
215 }
216 
217 void init_openssl(void) {
218   if (SSL_library_init()) {
219     SSL_load_error_strings();
220     OpenSSL_add_all_algorithms();
221     RAND_load_file("/dev/urandom", 1024);
222   } else
223     exit(EXIT_FAILURE);
224 }
225 
226 void cleanup_openssl(void) {
227   CRYPTO_cleanup_all_ex_data();
228   ERR_free_strings();
229   ERR_remove_thread_state(0);
230   EVP_cleanup();
231 }
232 
233 void handle_openssl_error(void) { ERR_print_errors_fp(stderr); }
234 inline void ensure_openssl_key_present_and_valid(const std::string &filepath) {
235   bool pem_file_valid = false;
236 
237   pem_file_valid = verify_openssl_key_cert(filepath);
238 
239   if (!pem_file_valid) {
240     LOG(WARNING) << "Error in verifying signature, regenerating";
241     generate_ssl_certificate(filepath);
242   }
243 }
244 }