1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "base/tlsutility.hpp"
4 #include "base/convert.hpp"
5 #include "base/logger.hpp"
6 #include "base/context.hpp"
7 #include "base/convert.hpp"
8 #include "base/utility.hpp"
9 #include "base/application.hpp"
10 #include "base/exception.hpp"
11 #include <boost/asio/ssl/context.hpp>
12 #include <openssl/opensslv.h>
13 #include <openssl/crypto.h>
14 #include <fstream>
15 
16 namespace icinga
17 {
18 
19 static bool l_SSLInitialized = false;
20 static std::mutex *l_Mutexes;
21 static std::mutex l_RandomMutex;
22 
GetOpenSSLVersion()23 String GetOpenSSLVersion()
24 {
25 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
26 	return OpenSSL_version(OPENSSL_VERSION);
27 #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
28 	return SSLeay_version(SSLEAY_VERSION);
29 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
30 }
31 
32 #ifdef CRYPTO_LOCK
OpenSSLLockingCallback(int mode,int type,const char *,int)33 static void OpenSSLLockingCallback(int mode, int type, const char *, int)
34 {
35 	if (mode & CRYPTO_LOCK)
36 		l_Mutexes[type].lock();
37 	else
38 		l_Mutexes[type].unlock();
39 }
40 
OpenSSLIDCallback()41 static unsigned long OpenSSLIDCallback()
42 {
43 #ifdef _WIN32
44 	return (unsigned long)GetCurrentThreadId();
45 #else /* _WIN32 */
46 	return (unsigned long)pthread_self();
47 #endif /* _WIN32 */
48 }
49 #endif /* CRYPTO_LOCK */
50 
51 /**
52  * Initializes the OpenSSL library.
53  */
InitializeOpenSSL()54 void InitializeOpenSSL()
55 {
56 	if (l_SSLInitialized)
57 		return;
58 
59 	SSL_library_init();
60 	SSL_load_error_strings();
61 
62 	SSL_COMP_get_compression_methods();
63 
64 #ifdef CRYPTO_LOCK
65 	l_Mutexes = new std::mutex[CRYPTO_num_locks()];
66 	CRYPTO_set_locking_callback(&OpenSSLLockingCallback);
67 	CRYPTO_set_id_callback(&OpenSSLIDCallback);
68 #endif /* CRYPTO_LOCK */
69 
70 	l_SSLInitialized = true;
71 }
72 
InitSslContext(const Shared<boost::asio::ssl::context>::Ptr & context,const String & pubkey,const String & privkey,const String & cakey)73 static void InitSslContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& pubkey, const String& privkey, const String& cakey)
74 {
75 	char errbuf[256];
76 
77 	// Enforce TLS v1.2 as minimum
78 	context->set_options(
79 		boost::asio::ssl::context::default_workarounds |
80 		boost::asio::ssl::context::no_compression |
81 		boost::asio::ssl::context::no_sslv2 |
82 		boost::asio::ssl::context::no_sslv3 |
83 		boost::asio::ssl::context::no_tlsv1 |
84 		boost::asio::ssl::context::no_tlsv1_1
85 	);
86 
87 	// Custom TLS flags
88 	SSL_CTX *sslContext = context->native_handle();
89 
90 	long flags = SSL_CTX_get_options(sslContext);
91 
92 	flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;
93 
94 	SSL_CTX_set_options(sslContext, flags);
95 
96 	SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
97 	SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)"Icinga 2", 8);
98 
99 	// Explicitly load ECC ciphers, required on el7 - https://github.com/Icinga/icinga2/issues/7247
100 	// SSL_CTX_set_ecdh_auto is deprecated and removed in OpenSSL 1.1.x - https://github.com/openssl/openssl/issues/1437
101 #if OPENSSL_VERSION_NUMBER < 0x10100000L
102 #	ifdef SSL_CTX_set_ecdh_auto
103 	SSL_CTX_set_ecdh_auto(sslContext, 1);
104 #	endif /* SSL_CTX_set_ecdh_auto */
105 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
106 
107 	if (!pubkey.IsEmpty()) {
108 		if (!SSL_CTX_use_certificate_chain_file(sslContext, pubkey.CStr())) {
109 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
110 			Log(LogCritical, "SSL")
111 				<< "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
112 			BOOST_THROW_EXCEPTION(openssl_error()
113 				<< boost::errinfo_api_function("SSL_CTX_use_certificate_chain_file")
114 				<< errinfo_openssl_error(ERR_peek_error())
115 				<< boost::errinfo_file_name(pubkey));
116 		}
117 	}
118 
119 	if (!privkey.IsEmpty()) {
120 		if (!SSL_CTX_use_PrivateKey_file(sslContext, privkey.CStr(), SSL_FILETYPE_PEM)) {
121 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
122 			Log(LogCritical, "SSL")
123 				<< "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
124 			BOOST_THROW_EXCEPTION(openssl_error()
125 				<< boost::errinfo_api_function("SSL_CTX_use_PrivateKey_file")
126 				<< errinfo_openssl_error(ERR_peek_error())
127 				<< boost::errinfo_file_name(privkey));
128 		}
129 
130 		if (!SSL_CTX_check_private_key(sslContext)) {
131 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
132 			Log(LogCritical, "SSL")
133 				<< "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
134 			BOOST_THROW_EXCEPTION(openssl_error()
135 				<< boost::errinfo_api_function("SSL_CTX_check_private_key")
136 				<< errinfo_openssl_error(ERR_peek_error()));
137 		}
138 	}
139 
140 	if (cakey.IsEmpty()) {
141 		if (!SSL_CTX_set_default_verify_paths(sslContext)) {
142 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
143 			Log(LogCritical, "SSL")
144 				<< "Error loading system's root CAs: " << ERR_peek_error() << ", \"" << errbuf << "\"";
145 			BOOST_THROW_EXCEPTION(openssl_error()
146 				<< boost::errinfo_api_function("SSL_CTX_set_default_verify_paths")
147 				<< errinfo_openssl_error(ERR_peek_error()));
148 		}
149 	} else {
150 		if (!SSL_CTX_load_verify_locations(sslContext, cakey.CStr(), nullptr)) {
151 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
152 			Log(LogCritical, "SSL")
153 				<< "Error loading and verifying locations in ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
154 			BOOST_THROW_EXCEPTION(openssl_error()
155 				<< boost::errinfo_api_function("SSL_CTX_load_verify_locations")
156 				<< errinfo_openssl_error(ERR_peek_error())
157 				<< boost::errinfo_file_name(cakey));
158 		}
159 
160 		STACK_OF(X509_NAME) *cert_names;
161 
162 		cert_names = SSL_load_client_CA_file(cakey.CStr());
163 		if (!cert_names) {
164 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
165 			Log(LogCritical, "SSL")
166 				<< "Error loading client ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
167 			BOOST_THROW_EXCEPTION(openssl_error()
168 				<< boost::errinfo_api_function("SSL_load_client_CA_file")
169 				<< errinfo_openssl_error(ERR_peek_error())
170 				<< boost::errinfo_file_name(cakey));
171 		}
172 
173 		SSL_CTX_set_client_CA_list(sslContext, cert_names);
174 	}
175 }
176 
177 /**
178  * Initializes an SSL context using the specified certificates.
179  *
180  * @param pubkey The public key.
181  * @param privkey The matching private key.
182  * @param cakey CA certificate chain file.
183  * @returns An SSL context.
184  */
MakeAsioSslContext(const String & pubkey,const String & privkey,const String & cakey)185 Shared<boost::asio::ssl::context>::Ptr MakeAsioSslContext(const String& pubkey, const String& privkey, const String& cakey)
186 {
187 	namespace ssl = boost::asio::ssl;
188 
189 	InitializeOpenSSL();
190 
191 	auto context (Shared<ssl::context>::Make(ssl::context::tls));
192 
193 	InitSslContext(context, pubkey, privkey, cakey);
194 
195 	return context;
196 }
197 
198 /**
199  * Set the cipher list to the specified SSL context.
200  * @param context The ssl context.
201  * @param cipherList The ciper list.
202  **/
SetCipherListToSSLContext(const Shared<boost::asio::ssl::context>::Ptr & context,const String & cipherList)203 void SetCipherListToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& cipherList)
204 {
205 	char errbuf[256];
206 
207 	if (SSL_CTX_set_cipher_list(context->native_handle(), cipherList.CStr()) == 0) {
208 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
209 		Log(LogCritical, "SSL")
210 			<< "Cipher list '"
211 			<< cipherList
212 			<< "' does not specify any usable ciphers: "
213 			<< ERR_peek_error() << ", \""
214 			<< errbuf << "\"";
215 
216 		BOOST_THROW_EXCEPTION(openssl_error()
217 			<< boost::errinfo_api_function("SSL_CTX_set_cipher_list")
218 			<< errinfo_openssl_error(ERR_peek_error()));
219 	}
220 
221 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
222 	//With OpenSSL 1.1.0, there might not be any returned 0.
223 	STACK_OF(SSL_CIPHER) *ciphers;
224 	Array::Ptr cipherNames = new Array();
225 
226 	ciphers = SSL_CTX_get_ciphers(context->native_handle());
227 	for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
228 		const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
229 		String cipher_name = SSL_CIPHER_get_name(cipher);
230 
231 		cipherNames->Add(cipher_name);
232 	}
233 
234 	Log(LogNotice, "TlsUtility")
235 		<< "Available TLS cipher list: " << cipherNames->Join(" ");
236 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
237 }
238 
239 /**
240  * Resolves a string describing a TLS protocol version to the value of a TLS*_VERSION macro of OpenSSL.
241  *
242  * Throws an exception if the version is unknown or not supported.
243  *
244  * @param version String of a TLS version, for example "TLSv1.2".
245  * @return The value of the corresponding TLS*_VERSION macro.
246  */
ResolveTlsProtocolVersion(const std::string & version)247 int ResolveTlsProtocolVersion(const std::string& version) {
248 	if (version == "TLSv1.2") {
249 		return TLS1_2_VERSION;
250 	} else if (version == "TLSv1.3") {
251 #if OPENSSL_VERSION_NUMBER >= 0x10101000L
252 		return TLS1_3_VERSION;
253 #else /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
254 		throw std::runtime_error("'" + version + "' is only supported with OpenSSL 1.1.1 or newer");
255 #endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
256 	} else {
257 		throw std::runtime_error("Unknown TLS protocol version '" + version + "'");
258 	}
259 }
260 
SetupSslContext(String certPath,String keyPath,String caPath,String crlPath,String cipherList,String protocolmin,DebugInfo di)261 Shared<boost::asio::ssl::context>::Ptr SetupSslContext(String certPath, String keyPath,
262 	String caPath, String crlPath, String cipherList, String protocolmin, DebugInfo di)
263 {
264 	namespace ssl = boost::asio::ssl;
265 
266 	Shared<ssl::context>::Ptr context;
267 
268 	try {
269 		context = MakeAsioSslContext(certPath, keyPath, caPath);
270 	} catch (const std::exception&) {
271 		BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '"
272 			+ certPath + "' key path: '" + keyPath + "' ca path: '" + caPath + "'.", di));
273 	}
274 
275 	if (!crlPath.IsEmpty()) {
276 		try {
277 			AddCRLToSSLContext(context, crlPath);
278 		} catch (const std::exception&) {
279 			BOOST_THROW_EXCEPTION(ScriptError("Cannot add certificate revocation list to SSL context for crl path: '"
280 				+ crlPath + "'.", di));
281 		}
282 	}
283 
284 	if (!cipherList.IsEmpty()) {
285 		try {
286 			SetCipherListToSSLContext(context, cipherList);
287 		} catch (const std::exception&) {
288 			BOOST_THROW_EXCEPTION(ScriptError("Cannot set cipher list to SSL context for cipher list: '"
289 				+ cipherList + "'.", di));
290 		}
291 	}
292 
293 	if (!protocolmin.IsEmpty()){
294 		try {
295 			SetTlsProtocolminToSSLContext(context, protocolmin);
296 		} catch (const std::exception&) {
297 			BOOST_THROW_EXCEPTION(ScriptError("Cannot set minimum TLS protocol version to SSL context with tls_protocolmin: '" + protocolmin + "'.", di));
298 		}
299 	}
300 
301 	return std::move(context);
302 }
303 
304 /**
305  * Set the minimum TLS protocol version to the specified SSL context.
306  *
307  * @param context The ssl context.
308  * @param tlsProtocolmin The minimum TLS protocol version.
309  */
SetTlsProtocolminToSSLContext(const Shared<boost::asio::ssl::context>::Ptr & context,const String & tlsProtocolmin)310 void SetTlsProtocolminToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& tlsProtocolmin)
311 {
312 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
313 	int ret = SSL_CTX_set_min_proto_version(context->native_handle(), ResolveTlsProtocolVersion(tlsProtocolmin));
314 
315 	if (ret != 1) {
316 		char errbuf[256];
317 
318 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
319 		Log(LogCritical, "SSL")
320 			<< "Error setting minimum TLS protocol version: " << ERR_peek_error() << ", \"" << errbuf << "\"";
321 		BOOST_THROW_EXCEPTION(openssl_error()
322 			<< boost::errinfo_api_function("SSL_CTX_set_min_proto_version")
323 			<< errinfo_openssl_error(ERR_peek_error()));
324 	}
325 #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
326 	// This should never happen. On this OpenSSL version, ResolveTlsProtocolVersion() should either return TLS 1.2
327 	// or throw an exception, as that's the only TLS version supported by both Icinga and ancient OpenSSL.
328 	VERIFY(ResolveTlsProtocolVersion(tlsProtocolmin) == TLS1_2_VERSION);
329 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
330 }
331 
332 /**
333  * Loads a CRL and appends its certificates to the specified Boost SSL context.
334  *
335  * @param context The SSL context.
336  * @param crlPath The path to the CRL file.
337  */
AddCRLToSSLContext(const Shared<boost::asio::ssl::context>::Ptr & context,const String & crlPath)338 void AddCRLToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& crlPath)
339 {
340 	X509_STORE *x509_store = SSL_CTX_get_cert_store(context->native_handle());
341 	AddCRLToSSLContext(x509_store, crlPath);
342 }
343 
344 /**
345  * Loads a CRL and appends its certificates to the specified OpenSSL X509 store.
346  *
347  * @param context The SSL context.
348  * @param crlPath The path to the CRL file.
349  */
AddCRLToSSLContext(X509_STORE * x509_store,const String & crlPath)350 void AddCRLToSSLContext(X509_STORE *x509_store, const String& crlPath)
351 {
352 	char errbuf[256];
353 
354 	X509_LOOKUP *lookup;
355 	lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
356 
357 	if (!lookup) {
358 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
359 		Log(LogCritical, "SSL")
360 			<< "Error adding X509 store lookup: " << ERR_peek_error() << ", \"" << errbuf << "\"";
361 		BOOST_THROW_EXCEPTION(openssl_error()
362 			<< boost::errinfo_api_function("X509_STORE_add_lookup")
363 			<< errinfo_openssl_error(ERR_peek_error()));
364 	}
365 
366 	if (X509_LOOKUP_load_file(lookup, crlPath.CStr(), X509_FILETYPE_PEM) != 1) {
367 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
368 		Log(LogCritical, "SSL")
369 			<< "Error loading crl file '" << crlPath << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
370 		BOOST_THROW_EXCEPTION(openssl_error()
371 			<< boost::errinfo_api_function("X509_LOOKUP_load_file")
372 			<< errinfo_openssl_error(ERR_peek_error())
373 			<< boost::errinfo_file_name(crlPath));
374 	}
375 
376 	X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
377 	X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
378 	X509_STORE_set1_param(x509_store, param);
379 	X509_VERIFY_PARAM_free(param);
380 }
381 
GetX509NameCN(X509_NAME * name)382 static String GetX509NameCN(X509_NAME *name)
383 {
384 	char errbuf[256];
385 	char buffer[256];
386 
387 	int rc = X509_NAME_get_text_by_NID(name, NID_commonName, buffer, sizeof(buffer));
388 
389 	if (rc == -1) {
390 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
391 		Log(LogCritical, "SSL")
392 			<< "Error with x509 NAME getting text by NID: " << ERR_peek_error() << ", \"" << errbuf << "\"";
393 		BOOST_THROW_EXCEPTION(openssl_error()
394 			<< boost::errinfo_api_function("X509_NAME_get_text_by_NID")
395 			<< errinfo_openssl_error(ERR_peek_error()));
396 	}
397 
398 	return buffer;
399 }
400 
401 /**
402  * Retrieves the common name for an X509 certificate.
403  *
404  * @param certificate The X509 certificate.
405  * @returns The common name.
406  */
GetCertificateCN(const std::shared_ptr<X509> & certificate)407 String GetCertificateCN(const std::shared_ptr<X509>& certificate)
408 {
409 	return GetX509NameCN(X509_get_subject_name(certificate.get()));
410 }
411 
412 /**
413  * Retrieves an X509 certificate from the specified file.
414  *
415  * @param pemfile The filename.
416  * @returns An X509 certificate.
417  */
GetX509Certificate(const String & pemfile)418 std::shared_ptr<X509> GetX509Certificate(const String& pemfile)
419 {
420 	char errbuf[256];
421 	X509 *cert;
422 	BIO *fpcert = BIO_new(BIO_s_file());
423 
424 	if (!fpcert) {
425 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
426 		Log(LogCritical, "SSL")
427 			<< "Error creating new BIO: " << ERR_peek_error() << ", \"" << errbuf << "\"";
428 		BOOST_THROW_EXCEPTION(openssl_error()
429 			<< boost::errinfo_api_function("BIO_new")
430 			<< errinfo_openssl_error(ERR_peek_error()));
431 	}
432 
433 	if (BIO_read_filename(fpcert, pemfile.CStr()) < 0) {
434 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
435 		Log(LogCritical, "SSL")
436 			<< "Error reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
437 		BOOST_THROW_EXCEPTION(openssl_error()
438 			<< boost::errinfo_api_function("BIO_read_filename")
439 			<< errinfo_openssl_error(ERR_peek_error())
440 			<< boost::errinfo_file_name(pemfile));
441 	}
442 
443 	cert = PEM_read_bio_X509_AUX(fpcert, nullptr, nullptr, nullptr);
444 	if (!cert) {
445 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
446 		Log(LogCritical, "SSL")
447 			<< "Error on bio X509 AUX reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
448 		BOOST_THROW_EXCEPTION(openssl_error()
449 			<< boost::errinfo_api_function("PEM_read_bio_X509_AUX")
450 			<< errinfo_openssl_error(ERR_peek_error())
451 			<< boost::errinfo_file_name(pemfile));
452 	}
453 
454 	BIO_free(fpcert);
455 
456 	return std::shared_ptr<X509>(cert, X509_free);
457 }
458 
MakeX509CSR(const String & cn,const String & keyfile,const String & csrfile,const String & certfile,bool ca)459 int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile, bool ca)
460 {
461 	char errbuf[256];
462 
463 	InitializeOpenSSL();
464 
465 	RSA *rsa = RSA_new();
466 	BIGNUM *e = BN_new();
467 
468 	if (!rsa || !e) {
469 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
470 		Log(LogCritical, "SSL")
471 			<< "Error while creating RSA key: " << ERR_peek_error() << ", \"" << errbuf << "\"";
472 		BOOST_THROW_EXCEPTION(openssl_error()
473 			<< boost::errinfo_api_function("RSA_generate_key")
474 			<< errinfo_openssl_error(ERR_peek_error()));
475 	}
476 
477 	BN_set_word(e, RSA_F4);
478 
479 	if (!RSA_generate_key_ex(rsa, 4096, e, nullptr)) {
480 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
481 		Log(LogCritical, "SSL")
482 			<< "Error while creating RSA key: " << ERR_peek_error() << ", \"" << errbuf << "\"";
483 		BOOST_THROW_EXCEPTION(openssl_error()
484 			<< boost::errinfo_api_function("RSA_generate_key")
485 			<< errinfo_openssl_error(ERR_peek_error()));
486 	}
487 
488 	BN_free(e);
489 
490 	Log(LogInformation, "base")
491 		<< "Writing private key to '" << keyfile << "'.";
492 
493 	BIO *bio = BIO_new_file(const_cast<char *>(keyfile.CStr()), "w");
494 
495 	if (!bio) {
496 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
497 		Log(LogCritical, "SSL")
498 			<< "Error while opening private RSA key file '" << keyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
499 		BOOST_THROW_EXCEPTION(openssl_error()
500 			<< boost::errinfo_api_function("BIO_new_file")
501 			<< errinfo_openssl_error(ERR_peek_error())
502 			<< boost::errinfo_file_name(keyfile));
503 	}
504 
505 	if (!PEM_write_bio_RSAPrivateKey(bio, rsa, nullptr, nullptr, 0, nullptr, nullptr)) {
506 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
507 		Log(LogCritical, "SSL")
508 			<< "Error while writing private RSA key to file '" << keyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
509 		BOOST_THROW_EXCEPTION(openssl_error()
510 			<< boost::errinfo_api_function("PEM_write_bio_RSAPrivateKey")
511 			<< errinfo_openssl_error(ERR_peek_error())
512 			<< boost::errinfo_file_name(keyfile));
513 	}
514 
515 	BIO_free(bio);
516 
517 #ifndef _WIN32
518 	chmod(keyfile.CStr(), 0600);
519 #endif /* _WIN32 */
520 
521 	EVP_PKEY *key = EVP_PKEY_new();
522 	EVP_PKEY_assign_RSA(key, rsa);
523 
524 	if (!certfile.IsEmpty()) {
525 		X509_NAME *subject = X509_NAME_new();
526 		X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0);
527 
528 		std::shared_ptr<X509> cert = CreateCert(key, subject, subject, key, ca);
529 
530 		X509_NAME_free(subject);
531 
532 		Log(LogInformation, "base")
533 			<< "Writing X509 certificate to '" << certfile << "'.";
534 
535 		bio = BIO_new_file(const_cast<char *>(certfile.CStr()), "w");
536 
537 		if (!bio) {
538 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
539 			Log(LogCritical, "SSL")
540 				<< "Error while opening certificate file '" << certfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
541 			BOOST_THROW_EXCEPTION(openssl_error()
542 				<< boost::errinfo_api_function("BIO_new_file")
543 				<< errinfo_openssl_error(ERR_peek_error())
544 				<< boost::errinfo_file_name(certfile));
545 		}
546 
547 		if (!PEM_write_bio_X509(bio, cert.get())) {
548 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
549 			Log(LogCritical, "SSL")
550 				<< "Error while writing certificate to file '" << certfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
551 			BOOST_THROW_EXCEPTION(openssl_error()
552 				<< boost::errinfo_api_function("PEM_write_bio_X509")
553 				<< errinfo_openssl_error(ERR_peek_error())
554 				<< boost::errinfo_file_name(certfile));
555 		}
556 
557 		BIO_free(bio);
558 	}
559 
560 	if (!csrfile.IsEmpty()) {
561 		X509_REQ *req = X509_REQ_new();
562 
563 		if (!req)
564 			return 0;
565 
566 		X509_REQ_set_version(req, 0);
567 		X509_REQ_set_pubkey(req, key);
568 
569 		X509_NAME *name = X509_REQ_get_subject_name(req);
570 		X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0);
571 
572 		if (!ca) {
573 			String san = "DNS:" + cn;
574 			X509_EXTENSION *subjectAltNameExt = X509V3_EXT_conf_nid(nullptr, nullptr, NID_subject_alt_name, const_cast<char *>(san.CStr()));
575 			if (subjectAltNameExt) {
576 				/* OpenSSL 0.9.8 requires STACK_OF(X509_EXTENSION), otherwise we would just use stack_st_X509_EXTENSION. */
577 				STACK_OF(X509_EXTENSION) *exts = sk_X509_EXTENSION_new_null();
578 				sk_X509_EXTENSION_push(exts, subjectAltNameExt);
579 				X509_REQ_add_extensions(req, exts);
580 				sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
581 			}
582 		}
583 
584 		X509_REQ_sign(req, key, EVP_sha256());
585 
586 		Log(LogInformation, "base")
587 			<< "Writing certificate signing request to '" << csrfile << "'.";
588 
589 		bio = BIO_new_file(const_cast<char *>(csrfile.CStr()), "w");
590 
591 		if (!bio) {
592 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
593 			Log(LogCritical, "SSL")
594 				<< "Error while opening CSR file '" << csrfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
595 			BOOST_THROW_EXCEPTION(openssl_error()
596 				<< boost::errinfo_api_function("BIO_new_file")
597 				<< errinfo_openssl_error(ERR_peek_error())
598 				<< boost::errinfo_file_name(csrfile));
599 		}
600 
601 		if (!PEM_write_bio_X509_REQ(bio, req)) {
602 			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
603 			Log(LogCritical, "SSL")
604 				<< "Error while writing CSR to file '" << csrfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
605 			BOOST_THROW_EXCEPTION(openssl_error()
606 				<< boost::errinfo_api_function("PEM_write_bio_X509")
607 				<< errinfo_openssl_error(ERR_peek_error())
608 				<< boost::errinfo_file_name(csrfile));
609 		}
610 
611 		BIO_free(bio);
612 
613 		X509_REQ_free(req);
614 	}
615 
616 	EVP_PKEY_free(key);
617 
618 	return 1;
619 }
620 
CreateCert(EVP_PKEY * pubkey,X509_NAME * subject,X509_NAME * issuer,EVP_PKEY * cakey,bool ca)621 std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca)
622 {
623 	X509 *cert = X509_new();
624 	X509_set_version(cert, 2);
625 	X509_gmtime_adj(X509_get_notBefore(cert), 0);
626 	X509_gmtime_adj(X509_get_notAfter(cert), 365 * 24 * 60 * 60 * 15);
627 	X509_set_pubkey(cert, pubkey);
628 
629 	X509_set_subject_name(cert, subject);
630 	X509_set_issuer_name(cert, issuer);
631 
632 	String id = Utility::NewUniqueID();
633 
634 	char errbuf[256];
635 	SHA_CTX context;
636 	unsigned char digest[SHA_DIGEST_LENGTH];
637 
638 	if (!SHA1_Init(&context)) {
639 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
640 		Log(LogCritical, "SSL")
641 			<< "Error on SHA1 Init: " << ERR_peek_error() << ", \"" << errbuf << "\"";
642 		BOOST_THROW_EXCEPTION(openssl_error()
643 			<< boost::errinfo_api_function("SHA1_Init")
644 			<< errinfo_openssl_error(ERR_peek_error()));
645 	}
646 
647 	if (!SHA1_Update(&context, (unsigned char*)id.CStr(), id.GetLength())) {
648 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
649 		Log(LogCritical, "SSL")
650 			<< "Error on SHA1 Update: " << ERR_peek_error() << ", \"" << errbuf << "\"";
651 		BOOST_THROW_EXCEPTION(openssl_error()
652 			<< boost::errinfo_api_function("SHA1_Update")
653 			<< errinfo_openssl_error(ERR_peek_error()));
654 	}
655 
656 	if (!SHA1_Final(digest, &context)) {
657 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
658 		Log(LogCritical, "SSL")
659 			<< "Error on SHA1 Final: " << ERR_peek_error() << ", \"" << errbuf << "\"";
660 		BOOST_THROW_EXCEPTION(openssl_error()
661 			<< boost::errinfo_api_function("SHA1_Final")
662 			<< errinfo_openssl_error(ERR_peek_error()));
663 	}
664 
665 	BIGNUM *bn = BN_new();
666 	BN_bin2bn(digest, sizeof(digest), bn);
667 	BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert));
668 	BN_free(bn);
669 
670 	X509V3_CTX ctx;
671 	X509V3_set_ctx_nodb(&ctx);
672 	X509V3_set_ctx(&ctx, cert, cert, nullptr, nullptr, 0);
673 
674 	const char *attr;
675 
676 	if (ca)
677 		attr = "critical,CA:TRUE";
678 	else
679 		attr = "critical,CA:FALSE";
680 
681 	X509_EXTENSION *basicConstraintsExt = X509V3_EXT_conf_nid(nullptr, &ctx, NID_basic_constraints, const_cast<char *>(attr));
682 
683 	if (basicConstraintsExt) {
684 		X509_add_ext(cert, basicConstraintsExt, -1);
685 		X509_EXTENSION_free(basicConstraintsExt);
686 	}
687 
688 	String cn = GetX509NameCN(subject);
689 
690 	if (!ca) {
691 		String san = "DNS:" + cn;
692 		X509_EXTENSION *subjectAltNameExt = X509V3_EXT_conf_nid(nullptr, &ctx, NID_subject_alt_name, const_cast<char *>(san.CStr()));
693 		if (subjectAltNameExt) {
694 			X509_add_ext(cert, subjectAltNameExt, -1);
695 			X509_EXTENSION_free(subjectAltNameExt);
696 		}
697 	}
698 
699 	X509_sign(cert, cakey, EVP_sha256());
700 
701 	return std::shared_ptr<X509>(cert, X509_free);
702 }
703 
GetIcingaCADir()704 String GetIcingaCADir()
705 {
706 	return Configuration::DataDir + "/ca";
707 }
708 
CreateCertIcingaCA(EVP_PKEY * pubkey,X509_NAME * subject)709 std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject)
710 {
711 	char errbuf[256];
712 
713 	String cadir = GetIcingaCADir();
714 
715 	String cakeyfile = cadir + "/ca.key";
716 
717 	RSA *rsa;
718 
719 	BIO *cakeybio = BIO_new_file(const_cast<char *>(cakeyfile.CStr()), "r");
720 
721 	if (!cakeybio) {
722 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
723 		Log(LogCritical, "SSL")
724 			<< "Could not open CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
725 		return std::shared_ptr<X509>();
726 	}
727 
728 	rsa = PEM_read_bio_RSAPrivateKey(cakeybio, nullptr, nullptr, nullptr);
729 
730 	if (!rsa) {
731 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
732 		Log(LogCritical, "SSL")
733 			<< "Could not read RSA key from CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
734 		return std::shared_ptr<X509>();
735 	}
736 
737 	BIO_free(cakeybio);
738 
739 	String cacertfile = cadir + "/ca.crt";
740 
741 	std::shared_ptr<X509> cacert = GetX509Certificate(cacertfile);
742 
743 	EVP_PKEY *privkey = EVP_PKEY_new();
744 	EVP_PKEY_assign_RSA(privkey, rsa);
745 
746 	return CreateCert(pubkey, subject, X509_get_subject_name(cacert.get()), privkey, false);
747 }
748 
CreateCertIcingaCA(const std::shared_ptr<X509> & cert)749 std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert)
750 {
751 	std::shared_ptr<EVP_PKEY> pkey = std::shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), EVP_PKEY_free);
752 	return CreateCertIcingaCA(pkey.get(), X509_get_subject_name(cert.get()));
753 }
754 
CertificateToString(const std::shared_ptr<X509> & cert)755 String CertificateToString(const std::shared_ptr<X509>& cert)
756 {
757 	BIO *mem = BIO_new(BIO_s_mem());
758 	PEM_write_bio_X509(mem, cert.get());
759 
760 	char *data;
761 	long len = BIO_get_mem_data(mem, &data);
762 
763 	String result = String(data, data + len);
764 
765 	BIO_free(mem);
766 
767 	return result;
768 }
769 
StringToCertificate(const String & cert)770 std::shared_ptr<X509> StringToCertificate(const String& cert)
771 {
772 	BIO *bio = BIO_new(BIO_s_mem());
773 	BIO_write(bio, (const void *)cert.CStr(), cert.GetLength());
774 
775 	X509 *rawCert = PEM_read_bio_X509_AUX(bio, nullptr, nullptr, nullptr);
776 
777 	BIO_free(bio);
778 
779 	if (!rawCert)
780 		BOOST_THROW_EXCEPTION(std::invalid_argument("The specified X509 certificate is invalid."));
781 
782 	return std::shared_ptr<X509>(rawCert, X509_free);
783 }
784 
PBKDF2_SHA1(const String & password,const String & salt,int iterations)785 String PBKDF2_SHA1(const String& password, const String& salt, int iterations)
786 {
787 	unsigned char digest[SHA_DIGEST_LENGTH];
788 	PKCS5_PBKDF2_HMAC_SHA1(password.CStr(), password.GetLength(), reinterpret_cast<const unsigned char *>(salt.CStr()), salt.GetLength(),
789 		iterations, sizeof(digest), digest);
790 
791 	char output[SHA_DIGEST_LENGTH*2+1];
792 	for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
793 		sprintf(output + 2 * i, "%02x", digest[i]);
794 
795 	return output;
796 }
797 
PBKDF2_SHA256(const String & password,const String & salt,int iterations)798 String PBKDF2_SHA256(const String& password, const String& salt, int iterations)
799 {
800 	unsigned char digest[SHA256_DIGEST_LENGTH];
801 	PKCS5_PBKDF2_HMAC(password.CStr(), password.GetLength(), reinterpret_cast<const unsigned char *>(salt.CStr()),
802 		salt.GetLength(), iterations, EVP_sha256(), SHA256_DIGEST_LENGTH, digest);
803 
804 	char output[SHA256_DIGEST_LENGTH*2+1];
805 	for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
806 		sprintf(output + 2 * i, "%02x", digest[i]);
807 
808 	return output;
809 }
810 
SHA1(const String & s,bool binary)811 String SHA1(const String& s, bool binary)
812 {
813 	char errbuf[256];
814 	SHA_CTX context;
815 	unsigned char digest[SHA_DIGEST_LENGTH];
816 
817 	if (!SHA1_Init(&context)) {
818 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
819 		Log(LogCritical, "SSL")
820 			<< "Error on SHA Init: " << ERR_peek_error() << ", \"" << errbuf << "\"";
821 		BOOST_THROW_EXCEPTION(openssl_error()
822 			<< boost::errinfo_api_function("SHA1_Init")
823 			<< errinfo_openssl_error(ERR_peek_error()));
824 	}
825 
826 	if (!SHA1_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
827 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
828 		Log(LogCritical, "SSL")
829 			<< "Error on SHA Update: " << ERR_peek_error() << ", \"" << errbuf << "\"";
830 		BOOST_THROW_EXCEPTION(openssl_error()
831 			<< boost::errinfo_api_function("SHA1_Update")
832 			<< errinfo_openssl_error(ERR_peek_error()));
833 	}
834 
835 	if (!SHA1_Final(digest, &context)) {
836 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
837 		Log(LogCritical, "SSL")
838 			<< "Error on SHA Final: " << ERR_peek_error() << ", \"" << errbuf << "\"";
839 		BOOST_THROW_EXCEPTION(openssl_error()
840 			<< boost::errinfo_api_function("SHA1_Final")
841 			<< errinfo_openssl_error(ERR_peek_error()));
842 	}
843 
844 	if (binary)
845 		return String(reinterpret_cast<const char*>(digest), reinterpret_cast<const char *>(digest + SHA_DIGEST_LENGTH));
846 
847 	return BinaryToHex(digest, SHA_DIGEST_LENGTH);
848 }
849 
SHA256(const String & s)850 String SHA256(const String& s)
851 {
852 	char errbuf[256];
853 	SHA256_CTX context;
854 	unsigned char digest[SHA256_DIGEST_LENGTH];
855 
856 	if (!SHA256_Init(&context)) {
857 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
858 		Log(LogCritical, "SSL")
859 			<< "Error on SHA256 Init: " << ERR_peek_error() << ", \"" << errbuf << "\"";
860 		BOOST_THROW_EXCEPTION(openssl_error()
861 			<< boost::errinfo_api_function("SHA256_Init")
862 			<< errinfo_openssl_error(ERR_peek_error()));
863 	}
864 
865 	if (!SHA256_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
866 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
867 		Log(LogCritical, "SSL")
868 			<< "Error on SHA256 Update: " << ERR_peek_error() << ", \"" << errbuf << "\"";
869 		BOOST_THROW_EXCEPTION(openssl_error()
870 			<< boost::errinfo_api_function("SHA256_Update")
871 			<< errinfo_openssl_error(ERR_peek_error()));
872 	}
873 
874 	if (!SHA256_Final(digest, &context)) {
875 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
876 		Log(LogCritical, "SSL")
877 			<< "Error on SHA256 Final: " << ERR_peek_error() << ", \"" << errbuf << "\"";
878 		BOOST_THROW_EXCEPTION(openssl_error()
879 			<< boost::errinfo_api_function("SHA256_Final")
880 			<< errinfo_openssl_error(ERR_peek_error()));
881 	}
882 
883 	char output[SHA256_DIGEST_LENGTH*2+1];
884 	for (int i = 0; i < 32; i++)
885 		sprintf(output + 2 * i, "%02x", digest[i]);
886 
887 	return output;
888 }
889 
RandomString(int length)890 String RandomString(int length)
891 {
892 	auto *bytes = new unsigned char[length];
893 
894 	/* Ensure that password generation is atomic. RAND_bytes is not thread-safe
895 	 * in OpenSSL < 1.1.0.
896 	 */
897 	std::unique_lock<std::mutex> lock(l_RandomMutex);
898 
899 	if (!RAND_bytes(bytes, length)) {
900 		delete [] bytes;
901 
902 		char errbuf[256];
903 		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
904 
905 		Log(LogCritical, "SSL")
906 			<< "Error for RAND_bytes: " << ERR_peek_error() << ", \"" << errbuf << "\"";
907 		BOOST_THROW_EXCEPTION(openssl_error()
908 			<< boost::errinfo_api_function("RAND_bytes")
909 			<< errinfo_openssl_error(ERR_peek_error()));
910 	}
911 
912 	lock.unlock();
913 
914 	auto *output = new char[length * 2 + 1];
915 	for (int i = 0; i < length; i++)
916 		sprintf(output + 2 * i, "%02x", bytes[i]);
917 
918 	String result = output;
919 	delete [] bytes;
920 	delete [] output;
921 
922 	return result;
923 }
924 
BinaryToHex(const unsigned char * data,size_t length)925 String BinaryToHex(const unsigned char* data, size_t length) {
926 	static const char hexdigits[] = "0123456789abcdef";
927 
928 	String output(2*length, 0);
929 	for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
930 		output[2 * i] = hexdigits[data[i] >> 4];
931 		output[2 * i + 1] = hexdigits[data[i] & 0xf];
932 	}
933 
934 	return output;
935 }
936 
VerifyCertificate(const std::shared_ptr<X509> & caCertificate,const std::shared_ptr<X509> & certificate,const String & crlFile)937 bool VerifyCertificate(const std::shared_ptr<X509> &caCertificate, const std::shared_ptr<X509> &certificate, const String& crlFile)
938 {
939 	X509_STORE *store = X509_STORE_new();
940 
941 	if (!store)
942 		return false;
943 
944 	X509_STORE_add_cert(store, caCertificate.get());
945 
946 	if (!crlFile.IsEmpty()) {
947 		AddCRLToSSLContext(store, crlFile);
948 	}
949 
950 	X509_STORE_CTX *csc = X509_STORE_CTX_new();
951 	X509_STORE_CTX_init(csc, store, certificate.get(), nullptr);
952 
953 	int rc = X509_verify_cert(csc);
954 
955 	X509_STORE_CTX_free(csc);
956 	X509_STORE_free(store);
957 
958 	if (rc == 0) {
959 		int err = X509_STORE_CTX_get_error(csc);
960 
961 		BOOST_THROW_EXCEPTION(openssl_error()
962 			<< boost::errinfo_api_function("X509_verify_cert")
963 			<< errinfo_openssl_error(err));
964 	}
965 
966 	return rc == 1;
967 }
968 
IsCa(const std::shared_ptr<X509> & cacert)969 bool IsCa(const std::shared_ptr<X509>& cacert)
970 {
971 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
972 	/* OpenSSL 1.1.x provides https://www.openssl.org/docs/man1.1.0/man3/X509_check_ca.html
973 	 *
974 	 * 0 if it is not CA certificate,
975 	 * 1 if it is proper X509v3 CA certificate with basicConstraints extension CA:TRUE,
976 	 * 3 if it is self-signed X509 v1 certificate
977 	 * 4 if it is certificate with keyUsage extension with bit keyCertSign set, but without basicConstraints,
978 	 * 5 if it has outdated Netscape Certificate Type extension telling that it is CA certificate.
979 	 */
980 	return (X509_check_ca(cacert.get()) == 1);
981 #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
982 	BOOST_THROW_EXCEPTION(std::invalid_argument("Not supported on this platform, OpenSSL version too old."));
983 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
984 }
985 
GetCertificateVersion(const std::shared_ptr<X509> & cert)986 int GetCertificateVersion(const std::shared_ptr<X509>& cert)
987 {
988 	return X509_get_version(cert.get()) + 1;
989 }
990 
GetSignatureAlgorithm(const std::shared_ptr<X509> & cert)991 String GetSignatureAlgorithm(const std::shared_ptr<X509>& cert)
992 {
993 	int alg;
994 	int sign_alg;
995 	X509_PUBKEY *key;
996 	X509_ALGOR *algor;
997 
998 	key = X509_get_X509_PUBKEY(cert.get());
999 
1000 	X509_PUBKEY_get0_param(nullptr, nullptr, 0, &algor, key); //TODO: Error handling
1001 
1002 	alg = OBJ_obj2nid (algor->algorithm);
1003 
1004 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1005 	sign_alg = OBJ_obj2nid((cert.get())->sig_alg->algorithm);
1006 #else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
1007 	sign_alg = X509_get_signature_nid(cert.get());
1008 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
1009 
1010 	return Convert::ToString((sign_alg == NID_undef) ? "Unknown" : OBJ_nid2ln(sign_alg));
1011 }
1012 
GetSubjectAltNames(const std::shared_ptr<X509> & cert)1013 Array::Ptr GetSubjectAltNames(const std::shared_ptr<X509>& cert)
1014 {
1015 	GENERAL_NAMES* subjectAltNames = (GENERAL_NAMES*)X509_get_ext_d2i(cert.get(), NID_subject_alt_name, nullptr, nullptr);
1016 
1017 	Array::Ptr sans = new Array();
1018 
1019 	for (int i = 0; i < sk_GENERAL_NAME_num(subjectAltNames); i++) {
1020 		GENERAL_NAME* gen = sk_GENERAL_NAME_value(subjectAltNames, i);
1021 		if (gen->type == GEN_URI || gen->type == GEN_DNS || gen->type == GEN_EMAIL) {
1022 			ASN1_IA5STRING *asn1_str = gen->d.uniformResourceIdentifier;
1023 
1024 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1025 			String san = Convert::ToString(ASN1_STRING_data(asn1_str));
1026 #else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
1027 			String san = Convert::ToString(ASN1_STRING_get0_data(asn1_str));
1028 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
1029 
1030 			sans->Add(san);
1031 		}
1032 	}
1033 
1034 	GENERAL_NAMES_free(subjectAltNames);
1035 
1036 	return sans;
1037 }
1038 
1039 }
1040