]> granicus.if.org Git - icinga2/blob - lib/base/tlsutility.cpp
f6d73272e5cb2e10a08a6525ec9964f4f1edf77f
[icinga2] / lib / base / tlsutility.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/tlsutility.hpp"
21 #include "base/convert.hpp"
22 #include "base/logger.hpp"
23 #include "base/context.hpp"
24 #include "base/utility.hpp"
25 #include "base/application.hpp"
26 #include "base/exception.hpp"
27 #include <fstream>
28
29 namespace icinga
30 {
31
32 static bool l_SSLInitialized = false;
33 static boost::mutex *l_Mutexes;
34
35 #ifdef CRYPTO_LOCK
36 static void OpenSSLLockingCallback(int mode, int type, const char *, int)
37 {
38         if (mode & CRYPTO_LOCK)
39                 l_Mutexes[type].lock();
40         else
41                 l_Mutexes[type].unlock();
42 }
43
44 static unsigned long OpenSSLIDCallback(void)
45 {
46 #ifdef _WIN32
47         return (unsigned long)GetCurrentThreadId();
48 #else /* _WIN32 */
49         return (unsigned long)pthread_self();
50 #endif /* _WIN32 */
51 }
52 #endif /* CRYPTO_LOCK */
53
54 /**
55  * Initializes the OpenSSL library.
56  */
57 void InitializeOpenSSL(void)
58 {
59         if (l_SSLInitialized)
60                 return;
61
62         SSL_library_init();
63         SSL_load_error_strings();
64
65         SSL_COMP_get_compression_methods();
66
67 #ifdef CRYPTO_LOCK
68         l_Mutexes = new boost::mutex[CRYPTO_num_locks()];
69         CRYPTO_set_locking_callback(&OpenSSLLockingCallback);
70         CRYPTO_set_id_callback(&OpenSSLIDCallback);
71 #endif /* CRYPTO_LOCK */
72
73         l_SSLInitialized = true;
74 }
75
76 /**
77  * Initializes an SSL context using the specified certificates.
78  *
79  * @param pubkey The public key.
80  * @param privkey The matching private key.
81  * @param cakey CA certificate chain file.
82  * @returns An SSL context.
83  */
84 boost::shared_ptr<SSL_CTX> MakeSSLContext(const String& pubkey, const String& privkey, const String& cakey)
85 {
86         char errbuf[120];
87
88         InitializeOpenSSL();
89
90         boost::shared_ptr<SSL_CTX> sslContext = boost::shared_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_method()), SSL_CTX_free);
91
92         long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE;
93
94 #ifdef SSL_OP_NO_COMPRESSION
95         flags |= SSL_OP_NO_COMPRESSION;
96 #endif /* SSL_OP_NO_COMPRESSION */
97
98         SSL_CTX_set_options(sslContext.get(), flags);
99
100         SSL_CTX_set_mode(sslContext.get(), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
101         SSL_CTX_set_session_id_context(sslContext.get(), (const unsigned char *)"Icinga 2", 8);
102
103         if (!pubkey.IsEmpty()) {
104                 if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) {
105                         Log(LogCritical, "SSL")
106                             << "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
107                         BOOST_THROW_EXCEPTION(openssl_error()
108                             << boost::errinfo_api_function("SSL_CTX_use_certificate_chain_file")
109                             << errinfo_openssl_error(ERR_peek_error())
110                             << boost::errinfo_file_name(pubkey));
111                 }
112         }
113
114         if (!privkey.IsEmpty()) {
115                 if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) {
116                         Log(LogCritical, "SSL")
117                             << "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
118                         BOOST_THROW_EXCEPTION(openssl_error()
119                             << boost::errinfo_api_function("SSL_CTX_use_PrivateKey_file")
120                             << errinfo_openssl_error(ERR_peek_error())
121                             << boost::errinfo_file_name(privkey));
122                 }
123
124                 if (!SSL_CTX_check_private_key(sslContext.get())) {
125                         Log(LogCritical, "SSL")
126                             << "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
127                         BOOST_THROW_EXCEPTION(openssl_error()
128                             << boost::errinfo_api_function("SSL_CTX_check_private_key")
129                             << errinfo_openssl_error(ERR_peek_error()));
130                 }
131         }
132
133         if (!cakey.IsEmpty()) {
134                 if (!SSL_CTX_load_verify_locations(sslContext.get(), cakey.CStr(), NULL)) {
135                         Log(LogCritical, "SSL")
136                             << "Error loading and verifying locations in ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
137                         BOOST_THROW_EXCEPTION(openssl_error()
138                             << boost::errinfo_api_function("SSL_CTX_load_verify_locations")
139                             << errinfo_openssl_error(ERR_peek_error())
140                             << boost::errinfo_file_name(cakey));
141                 }
142
143                 STACK_OF(X509_NAME) *cert_names;
144
145                 cert_names = SSL_load_client_CA_file(cakey.CStr());
146                 if (cert_names == NULL) {
147                         Log(LogCritical, "SSL")
148                             << "Error loading client ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
149                         BOOST_THROW_EXCEPTION(openssl_error()
150                             << boost::errinfo_api_function("SSL_load_client_CA_file")
151                             << errinfo_openssl_error(ERR_peek_error())
152                             << boost::errinfo_file_name(cakey));
153                 }
154
155                 SSL_CTX_set_client_CA_list(sslContext.get(), cert_names);
156         }
157
158         return sslContext;
159 }
160
161 /**
162  * Set the cipher list to the specified SSL context.
163  * @param context The ssl context.
164  * @param cipherList The ciper list.
165  **/
166 void SetCipherListToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& cipherList)
167 {
168         char errbuf[256];
169
170         if (SSL_CTX_set_cipher_list(context.get(), cipherList.CStr()) == 0) {
171                 Log(LogCritical, "SSL")
172                     << "Cipher list '"
173                     << cipherList
174                     << "' does not specify any usable ciphers: "
175                     << ERR_peek_error() << ", \""
176                     << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
177
178                 BOOST_THROW_EXCEPTION(openssl_error()
179                     << boost::errinfo_api_function("SSL_CTX_set_cipher_list")
180                     << errinfo_openssl_error(ERR_peek_error()));
181         }
182 }
183
184 /**
185  * Set the minimum TLS protocol version to the specified SSL context.
186  *
187  * @param context The ssl context.
188  * @param tlsProtocolmin The minimum TLS protocol version.
189  */
190 void SetTlsProtocolminToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& tlsProtocolmin)
191 {
192         long flags = SSL_CTX_get_options(context.get());
193
194         flags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
195
196 #ifdef SSL_TXT_TLSV1_1
197         if (tlsProtocolmin == SSL_TXT_TLSV1_1)
198                 flags |= SSL_OP_NO_TLSv1;
199         else
200 #endif /* SSL_TXT_TLSV1_1 */
201 #ifdef SSL_TXT_TLSV1_2
202         if (tlsProtocolmin == SSL_TXT_TLSV1_2)
203                 flags |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
204         else
205 #endif /* SSL_TXT_TLSV1_2 */
206         if (tlsProtocolmin != SSL_TXT_TLSV1)
207                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid TLS protocol version specified."));
208
209         SSL_CTX_set_options(context.get(), flags);
210 }
211
212 /**
213  * Loads a CRL and appends its certificates to the specified SSL context.
214  *
215  * @param context The SSL context.
216  * @param crlPath The path to the CRL file.
217  */
218 void AddCRLToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& crlPath)
219 {
220         char errbuf[120];
221         X509_STORE *x509_store = SSL_CTX_get_cert_store(context.get());
222
223         X509_LOOKUP *lookup;
224         lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
225
226         if (!lookup) {
227                 Log(LogCritical, "SSL")
228                     << "Error adding X509 store lookup: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
229                 BOOST_THROW_EXCEPTION(openssl_error()
230                     << boost::errinfo_api_function("X509_STORE_add_lookup")
231                     << errinfo_openssl_error(ERR_peek_error()));
232         }
233
234         if (X509_LOOKUP_load_file(lookup, crlPath.CStr(), X509_FILETYPE_PEM) != 1) {
235                 Log(LogCritical, "SSL")
236                     << "Error loading crl file '" << crlPath << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
237                 BOOST_THROW_EXCEPTION(openssl_error()
238                     << boost::errinfo_api_function("X509_LOOKUP_load_file")
239                     << errinfo_openssl_error(ERR_peek_error())
240                     << boost::errinfo_file_name(crlPath));
241         }
242
243         X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
244         X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
245         X509_STORE_set1_param(x509_store, param);
246         X509_VERIFY_PARAM_free(param);
247 }
248
249 static String GetX509NameCN(X509_NAME *name)
250 {
251         char errbuf[120];
252         char buffer[256];
253
254         int rc = X509_NAME_get_text_by_NID(name, NID_commonName, buffer, sizeof(buffer));
255
256         if (rc == -1) {
257                 Log(LogCritical, "SSL")
258                     << "Error with x509 NAME getting text by NID: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
259                 BOOST_THROW_EXCEPTION(openssl_error()
260                     << boost::errinfo_api_function("X509_NAME_get_text_by_NID")
261                     << errinfo_openssl_error(ERR_peek_error()));
262         }
263
264         return buffer;
265 }
266
267 /**
268  * Retrieves the common name for an X509 certificate.
269  *
270  * @param certificate The X509 certificate.
271  * @returns The common name.
272  */
273 String GetCertificateCN(const boost::shared_ptr<X509>& certificate)
274 {
275         return GetX509NameCN(X509_get_subject_name(certificate.get()));
276 }
277
278 /**
279  * Retrieves an X509 certificate from the specified file.
280  *
281  * @param pemfile The filename.
282  * @returns An X509 certificate.
283  */
284 boost::shared_ptr<X509> GetX509Certificate(const String& pemfile)
285 {
286         char errbuf[120];
287         X509 *cert;
288         BIO *fpcert = BIO_new(BIO_s_file());
289
290         if (fpcert == NULL) {
291                 Log(LogCritical, "SSL")
292                     << "Error creating new BIO: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
293                 BOOST_THROW_EXCEPTION(openssl_error()
294                     << boost::errinfo_api_function("BIO_new")
295                     << errinfo_openssl_error(ERR_peek_error()));
296         }
297
298         if (BIO_read_filename(fpcert, pemfile.CStr()) < 0) {
299                 Log(LogCritical, "SSL")
300                     << "Error reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
301                 BOOST_THROW_EXCEPTION(openssl_error()
302                     << boost::errinfo_api_function("BIO_read_filename")
303                     << errinfo_openssl_error(ERR_peek_error())
304                     << boost::errinfo_file_name(pemfile));
305         }
306
307         cert = PEM_read_bio_X509_AUX(fpcert, NULL, NULL, NULL);
308         if (cert == NULL) {
309                 Log(LogCritical, "SSL")
310                     << "Error on bio X509 AUX reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
311                 BOOST_THROW_EXCEPTION(openssl_error()
312                     << boost::errinfo_api_function("PEM_read_bio_X509_AUX")
313                     << errinfo_openssl_error(ERR_peek_error())
314                     << boost::errinfo_file_name(pemfile));
315         }
316
317         BIO_free(fpcert);
318
319         return boost::shared_ptr<X509>(cert, X509_free);
320 }
321
322 int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile, bool ca)
323 {
324         char errbuf[120];
325
326         InitializeOpenSSL();
327
328         RSA *rsa = RSA_generate_key(4096, RSA_F4, NULL, NULL);
329
330         Log(LogInformation, "base")
331             << "Writing private key to '" << keyfile << "'.";
332
333         BIO *bio = BIO_new_file(const_cast<char *>(keyfile.CStr()), "w");
334
335         if (!bio) {
336                 Log(LogCritical, "SSL")
337                     << "Error while opening private RSA key file '" << keyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
338                 BOOST_THROW_EXCEPTION(openssl_error()
339                     << boost::errinfo_api_function("BIO_new_file")
340                     << errinfo_openssl_error(ERR_peek_error())
341                     << boost::errinfo_file_name(keyfile));
342         }
343
344         if (!PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL)) {
345                 Log(LogCritical, "SSL")
346                     << "Error while writing private RSA key to file '" << keyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
347                 BOOST_THROW_EXCEPTION(openssl_error()
348                     << boost::errinfo_api_function("PEM_write_bio_RSAPrivateKey")
349                     << errinfo_openssl_error(ERR_peek_error())
350                     << boost::errinfo_file_name(keyfile));
351         }
352
353         BIO_free(bio);
354
355 #ifndef _WIN32
356         chmod(keyfile.CStr(), 0600);
357 #endif /* _WIN32 */
358         
359         EVP_PKEY *key = EVP_PKEY_new();
360         EVP_PKEY_assign_RSA(key, rsa);
361         
362         if (!certfile.IsEmpty()) {
363                 X509_NAME *subject = X509_NAME_new();
364                 X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0);
365
366                 boost::shared_ptr<X509> cert = CreateCert(key, subject, subject, key, ca);
367
368                 X509_NAME_free(subject);
369
370                 Log(LogInformation, "base")
371                     << "Writing X509 certificate to '" << certfile << "'.";
372
373                 bio = BIO_new_file(const_cast<char *>(certfile.CStr()), "w");
374
375                 if (!bio) {
376                         Log(LogCritical, "SSL")
377                             << "Error while opening certificate file '" << certfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
378                         BOOST_THROW_EXCEPTION(openssl_error()
379                             << boost::errinfo_api_function("BIO_new_file")
380                             << errinfo_openssl_error(ERR_peek_error())
381                             << boost::errinfo_file_name(certfile));
382                 }
383
384                 if (!PEM_write_bio_X509(bio, cert.get())) {
385                         Log(LogCritical, "SSL")
386                             << "Error while writing certificate to file '" << certfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
387                         BOOST_THROW_EXCEPTION(openssl_error()
388                             << boost::errinfo_api_function("PEM_write_bio_X509")
389                             << errinfo_openssl_error(ERR_peek_error())
390                             << boost::errinfo_file_name(certfile));
391                 }
392
393                 BIO_free(bio);
394         }
395
396         if (!csrfile.IsEmpty()) {
397                 X509_REQ *req = X509_REQ_new();
398
399                 if (!req)
400                         return 0;
401
402                 X509_REQ_set_version(req, 0);
403                 X509_REQ_set_pubkey(req, key);
404         
405                 X509_NAME *name = X509_REQ_get_subject_name(req);
406                 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0);
407         
408                 X509_REQ_sign(req, key, EVP_sha256());
409         
410                 Log(LogInformation, "base")
411                     << "Writing certificate signing request to '" << csrfile << "'.";
412         
413                 bio = BIO_new_file(const_cast<char *>(csrfile.CStr()), "w");
414
415                 if (!bio) {
416                         Log(LogCritical, "SSL")
417                             << "Error while opening CSR file '" << csrfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
418                         BOOST_THROW_EXCEPTION(openssl_error()
419                             << boost::errinfo_api_function("BIO_new_file")
420                             << errinfo_openssl_error(ERR_peek_error())
421                             << boost::errinfo_file_name(csrfile));
422                 }
423
424                 if (!PEM_write_bio_X509_REQ(bio, req)) {
425                         Log(LogCritical, "SSL")
426                             << "Error while writing CSR to file '" << csrfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
427                         BOOST_THROW_EXCEPTION(openssl_error()
428                             << boost::errinfo_api_function("PEM_write_bio_X509")
429                             << errinfo_openssl_error(ERR_peek_error())
430                             << boost::errinfo_file_name(csrfile));
431                 }
432
433                 BIO_free(bio);
434         
435                 X509_REQ_free(req);
436         }
437
438         EVP_PKEY_free(key);
439
440         return 1;
441 }
442
443 boost::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca)
444 {
445         X509 *cert = X509_new();
446         X509_set_version(cert, 2);
447         X509_gmtime_adj(X509_get_notBefore(cert), 0);
448         X509_gmtime_adj(X509_get_notAfter(cert), 365 * 24 * 60 * 60 * 15);
449         X509_set_pubkey(cert, pubkey);
450
451         X509_set_subject_name(cert, subject);
452         X509_set_issuer_name(cert, issuer);
453
454         String id = Utility::NewUniqueID();
455
456         char errbuf[120];
457         SHA_CTX context;
458         unsigned char digest[SHA_DIGEST_LENGTH];
459
460         if (!SHA1_Init(&context)) {
461                 Log(LogCritical, "SSL")
462                     << "Error on SHA1 Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
463                 BOOST_THROW_EXCEPTION(openssl_error()
464                     << boost::errinfo_api_function("SHA1_Init")
465                     << errinfo_openssl_error(ERR_peek_error()));
466         }
467
468         if (!SHA1_Update(&context, (unsigned char*)id.CStr(), id.GetLength())) {
469                 Log(LogCritical, "SSL")
470                     << "Error on SHA1 Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
471                 BOOST_THROW_EXCEPTION(openssl_error()
472                     << boost::errinfo_api_function("SHA1_Update")
473                     << errinfo_openssl_error(ERR_peek_error()));
474         }
475
476         if (!SHA1_Final(digest, &context)) {
477                 Log(LogCritical, "SSL")
478                     << "Error on SHA1 Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
479                 BOOST_THROW_EXCEPTION(openssl_error()
480                     << boost::errinfo_api_function("SHA1_Final")
481                     << errinfo_openssl_error(ERR_peek_error()));
482         }
483
484         BIGNUM *bn = BN_new();
485         BN_bin2bn(digest, sizeof(digest), bn);
486         BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert));
487         BN_free(bn);
488
489         X509V3_CTX ctx;
490         X509V3_set_ctx_nodb(&ctx);
491         X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
492
493         const char *attr;
494
495         if (ca)
496                 attr = "critical,CA:TRUE";
497         else
498                 attr = "critical,CA:FALSE";
499
500         X509_EXTENSION *basicConstraintsExt = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast<char *>(attr));
501
502         if (basicConstraintsExt) {
503                 X509_add_ext(cert, basicConstraintsExt, -1);
504                 X509_EXTENSION_free(basicConstraintsExt);
505         }
506
507         String cn = GetX509NameCN(subject);
508
509         if (!cn.Contains(" ") && cn.Contains(".")) {
510                 String san = "DNS:" + cn;
511                 X509_EXTENSION *subjectAltNameExt = X509V3_EXT_conf_nid(NULL, &ctx, NID_subject_alt_name, const_cast<char *>(san.CStr()));
512                 if (subjectAltNameExt) {
513                         X509_add_ext(cert, subjectAltNameExt, -1);
514                         X509_EXTENSION_free(subjectAltNameExt);
515                 }
516         }
517
518         X509_sign(cert, cakey, EVP_sha256());
519
520         return boost::shared_ptr<X509>(cert, X509_free);
521 }
522
523 String GetIcingaCADir(void)
524 {
525         return Application::GetLocalStateDir() + "/lib/icinga2/ca";
526 }
527
528 boost::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject)
529 {
530         char errbuf[120];
531
532         String cadir = GetIcingaCADir();
533
534         String cakeyfile = cadir + "/ca.key";
535
536         RSA *rsa;
537
538         BIO *cakeybio = BIO_new_file(const_cast<char *>(cakeyfile.CStr()), "r");
539
540         if (!cakeybio) {
541                 Log(LogCritical, "SSL")
542                     << "Could not open CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
543                 return boost::shared_ptr<X509>();
544         }
545
546         rsa = PEM_read_bio_RSAPrivateKey(cakeybio, NULL, NULL, NULL);
547
548         if (!rsa) {
549                 Log(LogCritical, "SSL")
550                     << "Could not read RSA key from CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
551                 return boost::shared_ptr<X509>();
552         }
553
554         BIO_free(cakeybio);
555
556         String cacertfile = cadir + "/ca.crt";
557
558         boost::shared_ptr<X509> cacert = GetX509Certificate(cacertfile);
559
560         EVP_PKEY *privkey = EVP_PKEY_new();
561         EVP_PKEY_assign_RSA(privkey, rsa);
562
563         return CreateCert(pubkey, subject, X509_get_subject_name(cacert.get()), privkey, false);
564 }
565
566 String CertificateToString(const boost::shared_ptr<X509>& cert)
567 {
568         BIO *mem = BIO_new(BIO_s_mem());
569         PEM_write_bio_X509(mem, cert.get());
570
571         char *data;
572         long len = BIO_get_mem_data(mem, &data);
573
574         String result = String(data, data + len);
575
576         BIO_free(mem);
577
578         return result;
579 }
580
581 String PBKDF2_SHA1(const String& password, const String& salt, int iterations)
582 {
583         unsigned char digest[SHA_DIGEST_LENGTH];
584         PKCS5_PBKDF2_HMAC_SHA1(password.CStr(), password.GetLength(), reinterpret_cast<const unsigned char *>(salt.CStr()), salt.GetLength(),
585             iterations, sizeof(digest), digest);
586
587         char output[SHA_DIGEST_LENGTH*2+1];
588         for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
589                 sprintf(output + 2 * i, "%02x", digest[i]);
590
591         return output;
592 }
593
594 String SHA1(const String& s, bool binary)
595 {
596         char errbuf[120];
597         SHA_CTX context;
598         unsigned char digest[SHA_DIGEST_LENGTH];
599
600         if (!SHA1_Init(&context)) {
601                 Log(LogCritical, "SSL")
602                     << "Error on SHA Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
603                 BOOST_THROW_EXCEPTION(openssl_error()
604                     << boost::errinfo_api_function("SHA1_Init")
605                     << errinfo_openssl_error(ERR_peek_error()));
606         }
607
608         if (!SHA1_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
609                 Log(LogCritical, "SSL")
610                     << "Error on SHA Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
611                 BOOST_THROW_EXCEPTION(openssl_error()
612                     << boost::errinfo_api_function("SHA1_Update")
613                     << errinfo_openssl_error(ERR_peek_error()));
614         }
615
616         if (!SHA1_Final(digest, &context)) {
617                 Log(LogCritical, "SSL")
618                     << "Error on SHA Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
619                 BOOST_THROW_EXCEPTION(openssl_error()
620                     << boost::errinfo_api_function("SHA1_Final")
621                     << errinfo_openssl_error(ERR_peek_error()));
622         }
623
624         if (binary)
625                 return String(reinterpret_cast<const char*>(digest), reinterpret_cast<const char *>(digest + SHA_DIGEST_LENGTH));
626
627         char output[SHA_DIGEST_LENGTH*2+1];
628         for (int i = 0; i < 20; i++)
629                 sprintf(output + 2 * i, "%02x", digest[i]);
630
631         return output;
632 }
633
634 String SHA256(const String& s)
635 {
636         char errbuf[120];
637         SHA256_CTX context;
638         unsigned char digest[SHA256_DIGEST_LENGTH];
639
640         if (!SHA256_Init(&context)) {
641                 Log(LogCritical, "SSL")
642                     << "Error on SHA256 Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
643                 BOOST_THROW_EXCEPTION(openssl_error()
644                     << boost::errinfo_api_function("SHA256_Init")
645                     << errinfo_openssl_error(ERR_peek_error()));
646         }
647
648         if (!SHA256_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
649                 Log(LogCritical, "SSL")
650                     << "Error on SHA256 Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
651                 BOOST_THROW_EXCEPTION(openssl_error()
652                     << boost::errinfo_api_function("SHA256_Update")
653                     << errinfo_openssl_error(ERR_peek_error()));
654         }
655
656         if (!SHA256_Final(digest, &context)) {
657                 Log(LogCritical, "SSL")
658                     << "Error on SHA256 Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
659                 BOOST_THROW_EXCEPTION(openssl_error()
660                     << boost::errinfo_api_function("SHA256_Final")
661                     << errinfo_openssl_error(ERR_peek_error()));
662         }
663
664         char output[SHA256_DIGEST_LENGTH*2+1];
665         for (int i = 0; i < 32; i++)
666                 sprintf(output + 2 * i, "%02x", digest[i]);
667
668         return output;
669 }
670
671 String RandomString(int length)
672 {
673         unsigned char *bytes = new unsigned char[length];
674
675         if (!RAND_bytes(bytes, length)) {
676                 delete [] bytes;
677
678                 char errbuf[120];
679
680                 Log(LogCritical, "SSL")
681                         << "Error for RAND_bytes: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
682                 BOOST_THROW_EXCEPTION(openssl_error()
683                         << boost::errinfo_api_function("RAND_bytes")
684                         << errinfo_openssl_error(ERR_peek_error()));
685         }
686
687         char *output = new char[length * 2 + 1];
688         for (int i = 0; i < length; i++)
689                 sprintf(output + 2 * i, "%02x", bytes[i]);
690
691         String result = output;
692         delete [] bytes;
693         delete [] output;
694
695         return result;
696 }
697
698 }