From c3f30bf1fa6414736750a5138214468c90fc45a5 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 14 Feb 2019 14:35:24 +0800 Subject: [PATCH] vhost info: add memory buffer cert support --- include/libwebsockets/lws-context-vhost.h | 42 +- lib/tls/mbedtls/mbedtls-server.c | 88 ++-- lib/tls/mbedtls/wrapper/platform/ssl_pm.c | 2 +- lib/tls/openssl/openssl-server.c | 174 ++++--- lib/tls/tls-network.c | 2 +- lib/tls/tls-server.c | 4 +- lib/tls/tls.c | 73 ++- minimal-examples/http-server/README.md | 1 + .../CMakeLists.txt | 79 +++ .../minimal-http-server-tls-mem/README.md | 60 +++ .../minimal-http-server-tls-mem.c | 465 ++++++++++++++++++ .../mount-origin/404.html | 11 + .../mount-origin/example.js | 22 + .../mount-origin/favicon.ico | Bin 0 -> 1406 bytes .../mount-origin/http2.png | Bin 0 -> 7563 bytes .../mount-origin/index.html | 17 + .../mount-origin/libwebsockets.org-logo.svg | 120 +++++ .../mount-origin/strict-csp.svg | 53 ++ 18 files changed, 1076 insertions(+), 137 deletions(-) create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/README.md create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/favicon.ico create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/http2.png create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/index.html create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg create mode 100644 minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 759100523..01d4da64a 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -260,7 +260,12 @@ struct lws_context_creation_info { * server cert from, otherwise NULL for unencrypted. (For backwards * compatibility, this can also be used to pass the client certificate * when setting up a vhost client SSL context, but it is preferred to - * use .client_ssl_cert_filepath for that.) */ + * use .client_ssl_cert_filepath for that.) + * + * Notice you can alternatively set a single DER or PEM from a memory + * buffer as the vhost tls cert using \p server_ssl_cert_mem and + * \p server_ssl_cert_mem_len. + */ const char *ssl_private_key_filepath; /**< VHOST: filepath to private key if wanting SSL mode; * if this is set to NULL but ssl_cert_filepath is set, the @@ -269,12 +274,21 @@ struct lws_context_creation_info { * library calls. (For backwards compatibility, this can also be used * to pass the client cert private key filepath when setting up a * vhost client SSL context, but it is preferred to use - * .client_ssl_private_key_filepath for that.) */ + * .client_ssl_private_key_filepath for that.) + * + * Notice you can alternatively set a DER or PEM private key from a + * memory buffer as the vhost tls private key using + * \p server_ssl_private_key_mem and \p server_ssl_private_key_mem_len. + */ const char *ssl_ca_filepath; /**< VHOST: CA certificate filepath or NULL. (For backwards * compatibility, this can also be used to pass the client CA * filepath when setting up a vhost client SSL context, - * but it is preferred to use .client_ssl_ca_filepath for that.) */ + * but it is preferred to use .client_ssl_ca_filepath for that.) + * + * Notice you can alternatively set a DER or PEM CA cert from a memory + * buffer using \p server_ssl_ca_mem and \p server_ssl_ca_mem_len. + */ const char *ssl_cipher_list; /**< VHOST: List of valid ciphers to use ON TLS1.2 AND LOWER ONLY (eg, * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" @@ -611,6 +625,28 @@ struct lws_context_creation_info { * the type of the user data to be known so its size can be given. */ + const void *server_ssl_cert_mem; + /**< VHOST: Alternative for \p ssl_cert_filepath that allows setting + * from memory instead of from a file. At most one of + * \p ssl_cert_filepath or \p server_ssl_cert_mem should be non-NULL. */ + unsigned int server_ssl_cert_mem_len; + /**< VHOST: Server SSL context init: length of server_ssl_cert_mem in + * bytes */ + const void *server_ssl_private_key_mem; + /**< VHOST: Alternative for \p ssl_private_key_filepath allowing + * init from a private key in memory instead of a file. At most one + * of \p ssl_private_key_filepath or \p server_ssl_private_key_mem + * should be non-NULL. */ + unsigned int server_ssl_private_key_mem_len; + /**< VHOST: length of \p server_ssl_private_key_mem in memory */ + const void *server_ssl_ca_mem; + /**< VHOST: Alternative for \p ssl_ca_filepath allowing + * init from a CA cert in memory instead of a file. At most one + * of \p ssl_ca_filepath or \p server_ssl_ca_mem should be non-NULL. */ + unsigned int server_ssl_ca_mem_len; + /**< VHOST: length of \p server_ssl_ca_mem in memory */ + + /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility * diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c index 943533d79..d369ea4b3 100644 --- a/lib/tls/mbedtls/mbedtls-server.c +++ b/lib/tls/mbedtls/mbedtls-server.c @@ -101,15 +101,13 @@ lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx, int lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, const char *cert, const char *private_key, - const char *mem_cert, size_t len_mem_cert, + const char *mem_cert, size_t mem_cert_len, const char *mem_privkey, size_t mem_privkey_len) { - int n, f = 0; - const char *filepath = private_key; - uint8_t *mem = NULL, *p = NULL; - size_t mem_len = 0; lws_filepos_t flen; + uint8_t *p = NULL; long err; + int n; if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) { lwsl_notice("%s: no usable input\n", __func__); @@ -142,58 +140,43 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, * The passed memory-buffer cert image is in DER, and the * memory-buffer private key image is PEM. */ - /* mem cert is already DER */ - p = (uint8_t *)mem_cert; - flen = len_mem_cert; - /* mem private key is PEM, so go through the motions */ - mem = (uint8_t *)mem_privkey; - mem_len = mem_privkey_len; - filepath = NULL; - } else { - if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, NULL, - 0, &p, &flen)) { - lwsl_err("couldn't find cert file %s\n", cert); + cert = NULL; + private_key = NULL; + if (!mem_cert) return 1; - } - f = 1; } + if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, + mem_cert_len, &p, &flen)) { + lwsl_err("couldn't find cert file %s\n", cert); + + return 1; + } + err = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, flen, p); + lws_free_set_NULL(p); if (!err) { - free(p); lwsl_err("Problem loading cert\n"); return 1; } - if (f) - free(p); - p = NULL; + if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, + (char *)mem_privkey, mem_privkey_len, + &p, &flen)) { + lwsl_err("couldn't find private key\n"); - if (private_key || n == LWS_TLS_EXTANT_ALTERNATIVE) { - if (lws_tls_alloc_pem_to_der_file(vhost->context, filepath, - (char *)mem, mem_len, &p, - &flen)) { - lwsl_err("couldn't find private key file %s\n", - private_key); - - return 1; - } - err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, p, flen); - if (!err) { - free(p); - lwsl_err("Problem loading key\n"); - - return 1; - } + return 1; } - if (p && !mem_privkey) { - free(p); - p = NULL; + err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, p, flen); + lws_free_set_NULL(p); + if (!err) { + lwsl_err("Problem loading key\n"); + + return 1; } - if (!private_key && !mem_privkey && - vhost->protocols[0].callback(wsi, + if (!private_key && !mem_privkey && vhost->protocols[0].callback(wsi, LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, vhost->tls.ssl_ctx, NULL, 0)) { lwsl_err("ssl private key not set\n"); @@ -221,7 +204,8 @@ lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, return 1; } - if (!vhost->tls.use_ssl || !info->ssl_cert_filepath) + if (!vhost->tls.use_ssl || + (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) return 0; if (info->ssl_ca_filepath) { @@ -242,11 +226,23 @@ lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, return 1; } free(p); + } else { + if (info->server_ssl_ca_mem && info->server_ssl_ca_mem_len && + SSL_CTX_add_client_CA_ASN1(vhost->tls.ssl_ctx, (int)flen, p) + != 1) { + lwsl_err("%s: mem SSL_CTX_add_client_CA_ASN1 unhappy\n", + __func__); + return 1; + } + lwsl_notice("%s: vh %s: mem CA OK\n", __func__, vhost->name); } n = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, - info->ssl_private_key_filepath, NULL, - 0, NULL, 0); + info->ssl_private_key_filepath, + info->server_ssl_cert_mem, + info->server_ssl_cert_mem_len, + info->server_ssl_private_key_mem, + info->server_ssl_private_key_mem_len); if (n) return n; diff --git a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c index 4e6b45742..917c6bd07 100755 --- a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c +++ b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c @@ -655,7 +655,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1); ssl_mem_free(load_buf); } else { - printf("parsing as der\n"); + // printf("parsing as der\n"); ret = mbedtls_x509_crt_parse_der(x509_pm->x509_crt, buffer, len); } diff --git a/lib/tls/openssl/openssl-server.c b/lib/tls/openssl/openssl-server.c index 96d3767cb..fee519012 100644 --- a/lib/tls/openssl/openssl-server.c +++ b/lib/tls/openssl/openssl-server.c @@ -149,7 +149,7 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) int lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, const char *cert, const char *private_key, - const char *mem_cert, size_t len_mem_cert, + const char *mem_cert, size_t mem_cert_len, const char *mem_privkey, size_t mem_privkey_len) { #if !defined(OPENSSL_NO_EC) @@ -166,9 +166,13 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, unsigned long error; lws_filepos_t flen; uint8_t *p; + int ret; int n = lws_tls_generic_cert_checks(vhost, cert, private_key), m; + if (!cert && !private_key) + n = LWS_TLS_EXTANT_ALTERNATIVE; + if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) return 0; @@ -184,83 +188,104 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, * the rights to read our own cert + key we saved. * * If we were passed copies in memory buffers, use those - * instead. - * - * The passed memory-buffer cert image is in DER, and the - * memory-buffer private key image is PEM. + * in favour of the filepaths we normally want. */ -#ifndef USE_WOLFSSL - if (SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, - (int)len_mem_cert, - (uint8_t *)mem_cert) != 1) { -#else - if (wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, - (uint8_t *)mem_cert, - (int)len_mem_cert, - WOLFSSL_FILETYPE_ASN1) != 1) { - -#endif - lwsl_err("Problem loading update cert\n"); - - return 1; - } - - if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, - mem_privkey, mem_privkey_len, - &p, &flen)) { - lwsl_notice("unable to convert memory privkey\n"); - - return 1; - } -#ifndef USE_WOLFSSL - if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, - vhost->tls.ssl_ctx, p, - (long)(long long)flen) != 1) { -#else - if (wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, - flen, WOLFSSL_FILETYPE_ASN1) != 1) { -#endif - lwsl_notice("unable to use memory privkey\n"); - - return 1; - } - - goto check_key; + cert = NULL; + private_key = NULL; } - /* set the local certificate from CertFile */ - m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); - if (m != 1) { - error = ERR_get_error(); - lwsl_err("problem getting cert '%s' %lu: %s\n", - cert, error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); + /* + * use the multi-cert interface for backwards compatibility in the + * both simple files case + */ + + if (n != LWS_TLS_EXTANT_ALTERNATIVE && cert) { + + /* set the local certificate from CertFile */ + m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); + if (m != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + cert, error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + + return 1; + } + + if (private_key) { + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + private_key, error, + ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + } else { + if (vhost->protocols[0].callback(wsi, + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, + vhost->tls.ssl_ctx, NULL, 0)) { + lwsl_err("ssl private key not set\n"); + + return 1; + } + } + + return 0; + } + + /* otherwise allow for DER or PEM, file or memory image */ + + if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, + mem_cert_len, &p, &flen)) { + lwsl_err("%s: couldn't read cert file\n", __func__); return 1; } - if (n != LWS_TLS_EXTANT_ALTERNATIVE && private_key) { - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1) { - error = ERR_get_error(); - lwsl_err("ssl problem getting key '%s' %lu: %s\n", - private_key, error, - ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - } else { - if (vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, - vhost->tls.ssl_ctx, NULL, 0)) { - lwsl_err("ssl private key not set\n"); +#if !defined(USE_WOLFSSL) + ret = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, (int)flen, p); +#else + ret = wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, + (uint8_t *)p, (int)flen, + WOLFSSL_FILETYPE_ASN1); +#endif + lws_free_set_NULL(p); + if (ret != 1) { + lwsl_err("%s: Problem loading cert\n", __func__); - return 1; - } + return 1; + } + + if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, + mem_privkey, mem_privkey_len, + &p, &flen)) { + lwsl_notice("unable to convert memory privkey\n"); + + return 1; + } + +#if !defined(USE_WOLFSSL) + ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, + (long)(long long)flen); + if (ret != 1) { + ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, + vhost->tls.ssl_ctx, p, + (long)(long long)flen); + } +#else + ret = wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, flen, + WOLFSSL_FILETYPE_ASN1); +#endif + lws_free_set_NULL(p); + if (ret != 1) { + lwsl_notice("unable to use memory privkey\n"); + + return 1; } -check_key: /* verify private key */ if (!SSL_CTX_check_private_key(vhost->tls.ssl_ctx)) { lwsl_err("Private SSL key doesn't match cert\n"); @@ -348,8 +373,7 @@ post_ecdh: int lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, - struct lws_vhost *vhost, - struct lws *wsi) + struct lws_vhost *vhost, struct lws *wsi) { unsigned long error; SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method(); @@ -416,14 +440,18 @@ lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, lwsl_info(" SSL options 0x%lX\n", (unsigned long)SSL_CTX_get_options(vhost->tls.ssl_ctx)); - if (!vhost->tls.use_ssl || !info->ssl_cert_filepath) + if (!vhost->tls.use_ssl || + (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) return 0; lws_ssl_bind_passphrase(vhost->tls.ssl_ctx, info); return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, info->ssl_private_key_filepath, - NULL, 0, NULL, 0); + info->server_ssl_cert_mem, + info->server_ssl_cert_mem_len, + info->server_ssl_private_key_mem, + info->server_ssl_private_key_mem_len); } int @@ -512,7 +540,7 @@ lws_tls_server_accept(struct lws *wsi) return LWS_SSL_CAPABLE_DONE; } - + lws_tls_err_describe(); m = lws_ssl_get_error(wsi, n); if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) diff --git a/lib/tls/tls-network.c b/lib/tls/tls-network.c index 410a55ea2..3da072fe8 100644 --- a/lib/tls/tls-network.c +++ b/lib/tls/tls-network.c @@ -89,7 +89,7 @@ lws_tls_check_cert_lifetime(struct lws_vhost *v) lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, (int)life); } else - lwsl_notice(" vhost %s: no cert\n", v->name); + lwsl_info(" vhost %s: no cert\n", v->name); memset(&caa, 0, sizeof(caa)); caa.vh = v; diff --git a/lib/tls/tls-server.c b/lib/tls/tls-server.c index a3591eee0..43d70c28b 100644 --- a/lib/tls/tls-server.c +++ b/lib/tls/tls-server.c @@ -110,14 +110,14 @@ lws_context_init_server_ssl(const struct lws_context_creation_info *info, } /* - * If he is giving a cert filepath, take it as a sign he wants to use + * If he is giving a server cert, take it as a sign he wants to use * it on this vhost. User code can leave the cert filepath NULL and * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in * which case he's expected to set up the cert himself at * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which * provides the vhost SSL_CTX * in the user parameter. */ - if (info->ssl_cert_filepath) + if (info->ssl_cert_filepath || info->server_ssl_cert_mem) vhost->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; if (info->port != CONTEXT_PORT_NO_LISTEN) { diff --git a/lib/tls/tls.c b/lib/tls/tls.c index 9c954c78a..9b5da2121 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -110,12 +110,23 @@ bail: } #endif +/* + * filename: NULL means use buffer inbuf length inlen directly, otherwise + * load the file "filename" into an allocated buffer. + * + * Allocates a separate DER output buffer if inbuf / inlen are the input, + * since the + * + * Contents may be PEM or DER: returns with buf pointing to DER and amount + * set to the DER length. + */ + int lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, const char *inbuf, lws_filepos_t inlen, uint8_t **buf, lws_filepos_t *amount) { - const uint8_t *pem, *p, *end; + uint8_t *pem = NULL, *p, *end, *opem; lws_filepos_t len; uint8_t *q; int n; @@ -125,28 +136,60 @@ lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, if (n) return n; } else { - pem = (const uint8_t *)inbuf; + pem = (uint8_t *)inbuf; len = inlen; } + opem = p = pem; + end = p + len; + + if (strncmp((char *)p, "-----", 5)) { + + /* take it as being already DER */ + + pem = lws_malloc(inlen, "alloc_der"); + if (!pem) + return 1; + + memcpy(pem, inbuf, inlen); + + *buf = pem; + *amount = inlen; + + return 0; + } + + /* PEM -> DER */ + + if (!filename) { + /* we don't know if it's in const memory... alloc the output */ + pem = lws_malloc((inlen * 3) / 4, "alloc_der"); + if (!pem) { + lwsl_err("a\n"); + return 1; + } + + + } /* else overwrite the allocated, b64 input with decoded DER */ + /* trim the first line */ - p = pem; - end = p + len; - if (strncmp((char *)p, "-----", 5)) - goto bail; p += 5; while (p < end && *p != '\n' && *p != '-') p++; - if (*p != '-') + if (*p != '-') { + lwsl_err("b\n"); goto bail; + } while (p < end && *p != '\n') p++; - if (p >= end) + if (p >= end) { + lwsl_err("c\n"); goto bail; + } p++; @@ -154,13 +197,19 @@ lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, q = (uint8_t *)end - 2; - while (q > pem && *q != '\n') + while (q > opem && *q != '\n') q--; - if (*q != '\n') + if (*q != '\n') { + lwsl_err("d\n"); goto bail; + } - *q = '\0'; + /* we can't write into the input buffer for mem, since it may be in RO + * const segment + */ + if (filename) + *q = '\0'; *amount = lws_b64_decode_string((char *)p, (char *)pem, (int)(long long)len); @@ -173,6 +222,8 @@ bail: return 4; } + + #endif #if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) diff --git a/minimal-examples/http-server/README.md b/minimal-examples/http-server/README.md index 82d29f456..a6d0b8e40 100644 --- a/minimal-examples/http-server/README.md +++ b/minimal-examples/http-server/README.md @@ -18,6 +18,7 @@ minimal-http-server-smp|Multiple service threads minimal-http-server-sse-ring|Server Side Events with ringbuffer and threaded event sources minimal-http-server-sse|Simple Server Side Events minimal-http-server-tls-80|Serves a directory over http/1 or http/2 with TLS (SSL), custom 404 handler, redirect to https on port 80 +minimal-http-server-tls-mem|Serves using TLS with the cert and key provided as memory buffers instead of files minimal-http-server-tls|Serves a directory over http/1 or http/2 with TLS (SSL), custom 404 handler minimal-http-server|Serves a directory over http/1, custom 404 handler diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt new file mode 100644 index 000000000..e6ea90aab --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-http-server-tls-mem) +set(SRCS minimal-http-server-tls-mem.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) +require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/README.md b/minimal-examples/http-server/minimal-http-server-tls-mem/README.md new file mode 100644 index 000000000..e139c54a2 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/README.md @@ -0,0 +1,60 @@ +# lws minimal http server with tls and certs from memory + +This is the same as the minimal-http-server-tls example, but shows how +to init the vhost with both PEM or DER certs from memory instead of files. + +The server listens on port 7681 (initialized with PEM in-memory certs) and +port 7682 (initialized with DER in-memory certs). + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-http-server-tls-mem +[2019/02/14 14:46:40:9783] USER: LWS minimal http server TLS | visit https://localhost:7681 +[2019/02/14 14:46:40:9784] NOTICE: Using SSL mode +[2019/02/14 14:46:40:9784] NOTICE: lws_tls_server_vhost_backend_init: vh first: mem CA OK +parsing as der +[2019/02/14 14:46:40:9849] NOTICE: no client cert required +[2019/02/14 14:46:40:9849] NOTICE: created client ssl context for first +[2019/02/14 14:46:40:9849] NOTICE: Using SSL mode +[2019/02/14 14:46:40:9850] NOTICE: lws_tls_server_vhost_backend_init: vh second: mem CA OK +parsing as der +[2019/02/14 14:46:40:9894] NOTICE: no client cert required +[2019/02/14 14:46:40:9894] NOTICE: created client ssl context for second +[2019/02/14 14:46:40:9894] NOTICE: vhost first: cert expiry: 36167d +[2019/02/14 14:46:40:9894] NOTICE: vhost second: cert expiry: 36167d +[2018/03/20 13:23:14:0207] NOTICE: vhost default: cert expiry: 730459d +``` + +Visit https://127.0.0.1:7681 and https://127.0.0.1:7682 + +Because it uses a selfsigned certificate, you will have to make an exception for it in your browser. + +## Certificate creation + +The selfsigned certs provided were created with + +``` +echo -e "GB\nErewhon\nAll around\nlibwebsockets-test\n\nlocalhost\nnone@invalid.org\n" | openssl req -new -newkey rsa:4096 -days 36500 -nodes -x509 -keyout "localhost-100y.key" -out "localhost-100y.cert" +``` + +they cover "localhost" and last 100 years from 2018-03-20. + +You can replace them with commercial certificates matching your hostname. + +The der content was made from PEM like this + +``` + $ cat ../minimal-http-server-tls/localhost-100y.key | grep -v ^- | base64 -d | hexdump -C | tr -s ' ' | cut -d' ' -f2- | cut -d' ' -f-16 | sed "s/|.*//g" | sed "s/0000.*//g" | sed "s/^/0x/g" | sed "s/\ /\,\ 0x/g" | sed "s/\$/,/g" | sed "s/0x,//g" +``` + +## HTTP/2 + +If you built lws with `-DLWS_WITH_HTTP2=1` at cmake, this simple server is also http/2 capable +out of the box. If the index.html was loaded over http/2, it will display an HTTP 2 png. diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c b/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c new file mode 100644 index 000000000..55c8c10cd --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c @@ -0,0 +1,465 @@ +/* + * lws-minimal-http-server-tls + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the most minimal http server you can make with lws, + * with three extra lines giving it tls (ssl) capabilities, which in + * turn allow operation with HTTP/2 if lws was configured for it. + * + * To keep it simple, it serves stuff from the subdirectory + * "./mount-origin" of the directory it was started in. + * + * You can change that by changing mount.origin below. + */ + +#include +#include +#include + +static int interrupted; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +/* the cert and key as PEM */ + +static const char *cert_pem = + "-----BEGIN CERTIFICATE-----\n" + "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\n" + "e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=\n" + "-----END CERTIFICATE-----\n", + + *key_pem = + "-----BEGIN PRIVATE KEY-----\n" + "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\n" + "fBrpEY1IATtPq1taBZZogRqI3rOkkPk=\n" + "-----END PRIVATE KEY-----\n" + ; + +static const uint8_t cert_der[] = { + 0x30, 0x82, 0x05, 0xe6, 0x30, 0x82, 0x03, 0xce, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xda, 0xb9, 0xd0, 0x8b, 0xb0, 0x3c, 0x52, 0xa0, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x07, 0x45, 0x72, 0x65, 0x77, 0x68, 0x6f, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x0a, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x31, 0x1b, + 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x12, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x31, + 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, + 0x6e, 0x6f, 0x6e, 0x65, 0x40, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, 0x6f, 0x72, 0x67, + 0x30, 0x20, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33, 0x32, 0x30, 0x30, 0x34, 0x31, 0x36, 0x30, 0x37, + 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x38, 0x30, 0x32, 0x32, 0x34, 0x30, 0x34, 0x31, 0x36, 0x30, + 0x37, 0x5a, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x47, 0x42, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07, 0x45, 0x72, 0x65, + 0x77, 0x68, 0x6f, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0a, 0x41, + 0x6c, 0x6c, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x12, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x73, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x6e, 0x6f, 0x6e, 0x65, 0x40, + 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x02, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xa3, 0x62, 0xdb, 0x96, + 0x68, 0x80, 0x82, 0x63, 0x4b, 0x49, 0x3e, 0xe6, 0xf1, 0xa4, 0x88, 0x08, 0x2f, 0xe5, 0x96, 0x9b, + 0x3f, 0xdf, 0x98, 0xaf, 0x08, 0x42, 0xbd, 0x75, 0x5a, 0xd7, 0x9e, 0xeb, 0xf2, 0x14, 0xc9, 0x49, + 0x68, 0xe4, 0x8e, 0xb4, 0xda, 0x6a, 0xb5, 0xa9, 0xc2, 0xe1, 0x4f, 0xf9, 0x26, 0xa6, 0x84, 0x7c, + 0x0e, 0x2d, 0xc3, 0x02, 0x61, 0xca, 0x9d, 0x25, 0x9d, 0x3d, 0x6b, 0x67, 0xd4, 0x1b, 0x57, 0x2c, + 0x4a, 0xcb, 0x95, 0x48, 0x87, 0x81, 0x90, 0xeb, 0x65, 0x62, 0x27, 0x98, 0x40, 0x63, 0x28, 0xcd, + 0x43, 0x65, 0xff, 0x82, 0xbc, 0xd1, 0x99, 0xf8, 0x4c, 0xcf, 0x80, 0x1b, 0xf9, 0x9d, 0x37, 0xa4, + 0x2d, 0x67, 0x1f, 0x23, 0x96, 0x59, 0xb6, 0x81, 0xae, 0x20, 0xfd, 0x43, 0x97, 0xf2, 0x24, 0x34, + 0x3c, 0x3c, 0xcc, 0x5c, 0xf8, 0x72, 0x98, 0x8c, 0x7b, 0xf0, 0x45, 0x19, 0xe9, 0xb2, 0xc5, 0xd1, + 0xe1, 0x2e, 0xb2, 0x87, 0x4a, 0x6f, 0x04, 0xa3, 0xe9, 0xd3, 0xef, 0x7e, 0x2d, 0x22, 0xd9, 0xc7, + 0x29, 0x3f, 0xe6, 0xe8, 0x34, 0x94, 0xd3, 0x19, 0x59, 0xd7, 0x77, 0x7a, 0x7a, 0x12, 0xd1, 0x9b, + 0xbf, 0xfe, 0x37, 0x1e, 0x3b, 0x33, 0x75, 0xcc, 0x4d, 0x11, 0xf9, 0xa8, 0xa3, 0xff, 0xed, 0x34, + 0xc4, 0xda, 0xcd, 0x14, 0xeb, 0xe3, 0x34, 0xb6, 0xc1, 0x88, 0xdb, 0x3a, 0x51, 0x8b, 0xe9, 0xba, + 0x8f, 0x38, 0x4d, 0xc8, 0xc0, 0x53, 0x27, 0x5b, 0xb9, 0xf2, 0xa0, 0x1e, 0xdd, 0x95, 0xb9, 0xff, + 0xe6, 0x00, 0x8a, 0xe6, 0x58, 0x00, 0x1e, 0xa7, 0xe5, 0xb8, 0x54, 0xa7, 0x8a, 0x05, 0xb8, 0x1e, + 0x70, 0x61, 0xb7, 0x01, 0xcb, 0x05, 0x51, 0xf2, 0xe8, 0xc8, 0x9e, 0x91, 0x7c, 0x6e, 0xe5, 0x90, + 0x52, 0x3c, 0xb9, 0x37, 0xca, 0x52, 0x36, 0x9e, 0xec, 0xcd, 0xd6, 0x2c, 0x9c, 0xb2, 0x69, 0xbc, + 0x07, 0x74, 0xb2, 0x26, 0xeb, 0x34, 0xf8, 0xc2, 0xd0, 0x54, 0x02, 0x36, 0xba, 0x4d, 0x8e, 0x02, + 0x66, 0x20, 0xad, 0xfe, 0x98, 0xa9, 0x38, 0x91, 0x75, 0xfb, 0x65, 0x3c, 0x1e, 0x7e, 0x80, 0x33, + 0x4c, 0xae, 0x25, 0xda, 0x91, 0xcd, 0xb8, 0x2e, 0x77, 0x41, 0x57, 0x3f, 0x10, 0x5f, 0xbe, 0x18, + 0x12, 0xc0, 0xc6, 0x6b, 0xc2, 0x0e, 0xaf, 0x59, 0xa4, 0xc2, 0x18, 0x8b, 0xb3, 0xa6, 0xce, 0x49, + 0x00, 0x28, 0xa0, 0xbd, 0x51, 0xee, 0x84, 0x7f, 0x6d, 0x7b, 0x2c, 0x54, 0x02, 0x14, 0x80, 0x4a, + 0x23, 0x3b, 0xfd, 0x72, 0x08, 0xbd, 0x7f, 0x03, 0xcc, 0x2e, 0x1a, 0xca, 0x95, 0xea, 0x15, 0x44, + 0xdb, 0x1e, 0x70, 0x1b, 0x02, 0x3f, 0x9e, 0xbd, 0x5a, 0x02, 0x57, 0x85, 0x49, 0xf0, 0x7f, 0x69, + 0x68, 0x9f, 0x87, 0xc4, 0x66, 0xbd, 0xfe, 0xbd, 0x1b, 0x9c, 0xf6, 0xc8, 0x5f, 0xaa, 0x75, 0x74, + 0x9c, 0xf3, 0x75, 0x20, 0xc4, 0xa7, 0xcd, 0x70, 0x9a, 0xb2, 0xde, 0xc8, 0xd9, 0xf8, 0xae, 0x45, + 0x77, 0x48, 0xcf, 0xde, 0x8a, 0x8e, 0x51, 0x90, 0xa4, 0xfe, 0x17, 0x7c, 0xd5, 0x40, 0xf9, 0x11, + 0x8b, 0xed, 0xa3, 0x27, 0x58, 0xe1, 0x48, 0x69, 0x5a, 0xca, 0x58, 0xbc, 0xc0, 0xb6, 0x0c, 0xe8, + 0x18, 0xc4, 0xef, 0x3f, 0xf0, 0x2e, 0x7a, 0x12, 0x97, 0x9d, 0xc0, 0x49, 0x85, 0x8b, 0x56, 0xd2, + 0x5b, 0x53, 0x8a, 0x85, 0x71, 0xfb, 0x9c, 0x93, 0x61, 0x20, 0x19, 0x5a, 0x5f, 0x88, 0xb2, 0xc9, + 0x97, 0x8d, 0xe7, 0xf1, 0x26, 0xa6, 0x22, 0xdb, 0xfe, 0xd0, 0x5a, 0x6b, 0xf5, 0x40, 0x2f, 0x69, + 0xb0, 0xd7, 0x23, 0x4c, 0xc6, 0x81, 0x40, 0xb3, 0x74, 0xdd, 0x3d, 0x50, 0x7a, 0x56, 0xec, 0xed, + 0x8d, 0xbb, 0xb3, 0x17, 0x44, 0x9c, 0xd5, 0x2d, 0x87, 0x89, 0x08, 0xfb, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xf6, 0x66, 0x14, 0xdb, 0x7b, 0x56, 0xdb, 0x3b, 0x28, 0x9a, 0x42, 0x93, 0x01, 0x76, 0xab, 0x8e, + 0xbd, 0xaf, 0x8e, 0xeb, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xf6, 0x66, 0x14, 0xdb, 0x7b, 0x56, 0xdb, 0x3b, 0x28, 0x9a, 0x42, 0x93, 0x01, 0x76, 0xab, + 0x8e, 0xbd, 0xaf, 0x8e, 0xeb, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x36, 0x32, 0x01, 0x32, 0xba, 0x30, + 0x60, 0xd0, 0x9b, 0x84, 0x02, 0x5d, 0x3f, 0xb7, 0x61, 0x96, 0x14, 0xf6, 0x45, 0x41, 0x51, 0x75, + 0xe4, 0x54, 0x24, 0x3d, 0x08, 0xc6, 0xb1, 0xff, 0x86, 0x4b, 0xdb, 0xea, 0x6c, 0x87, 0x1e, 0x72, + 0xbc, 0x9c, 0xe6, 0x1e, 0xcc, 0x53, 0xe3, 0x52, 0x59, 0x91, 0x29, 0x48, 0x0d, 0x10, 0x3b, 0x80, + 0xc5, 0xb9, 0xd7, 0x67, 0x33, 0xdd, 0x09, 0x13, 0x55, 0xf5, 0x5d, 0xa6, 0x4a, 0x16, 0xd7, 0xbc, + 0x2c, 0xa2, 0x0d, 0x8e, 0xd6, 0x09, 0x01, 0x36, 0x06, 0x7e, 0x38, 0xcf, 0x6e, 0x8e, 0xd2, 0xe5, + 0x95, 0x93, 0xee, 0xc3, 0x34, 0xd2, 0xc7, 0xf4, 0x19, 0xe4, 0xc1, 0x4b, 0x4e, 0x9c, 0xcf, 0x4f, + 0xc2, 0xd9, 0x83, 0xf6, 0x98, 0x56, 0x7b, 0x19, 0xb8, 0xab, 0x61, 0xa7, 0x4e, 0xc8, 0x8b, 0xe9, + 0x49, 0x7a, 0x73, 0x2d, 0x10, 0x95, 0x32, 0x56, 0x29, 0x52, 0xc4, 0x51, 0x04, 0x3a, 0xc9, 0xd6, + 0xb9, 0xf3, 0x67, 0xb6, 0xdc, 0x9d, 0x40, 0x5e, 0xab, 0x6a, 0x15, 0xca, 0x5f, 0xa0, 0x4d, 0xf8, + 0x1f, 0x76, 0x9f, 0x12, 0x21, 0xb2, 0xf3, 0xcd, 0x9b, 0xf9, 0x90, 0x62, 0xc2, 0x47, 0x95, 0xfa, + 0x8a, 0xba, 0x5d, 0x51, 0x7c, 0xb0, 0x5c, 0xab, 0xf7, 0x36, 0x2b, 0xbf, 0xd0, 0xaf, 0x59, 0x36, + 0x25, 0x92, 0x94, 0xd0, 0x7c, 0xb4, 0xd9, 0x4a, 0xc8, 0x0f, 0x74, 0x41, 0xd8, 0x55, 0xc8, 0xef, + 0xc5, 0x0d, 0x83, 0xf9, 0x7c, 0x83, 0x47, 0x46, 0x91, 0x2d, 0x19, 0x6f, 0xc5, 0x46, 0xbd, 0x74, + 0x71, 0x85, 0x1c, 0xb2, 0x02, 0x1b, 0x7e, 0x09, 0xba, 0xae, 0x40, 0x8b, 0xa9, 0x4c, 0xd4, 0x4b, + 0x28, 0x0f, 0xc1, 0xd2, 0xb0, 0x9a, 0x4c, 0x72, 0x6a, 0xc7, 0xec, 0xc5, 0xb0, 0xd9, 0xc2, 0xa4, + 0xba, 0x30, 0xb7, 0xac, 0xc7, 0x45, 0x4e, 0xdb, 0x5e, 0xf3, 0x7c, 0x05, 0xd6, 0xeb, 0x85, 0xe0, + 0x58, 0xd4, 0x0b, 0xbd, 0xbe, 0x4a, 0x67, 0x10, 0x37, 0xb0, 0x37, 0xf3, 0xa0, 0x42, 0xfe, 0x79, + 0x36, 0x4d, 0x3b, 0x09, 0x6b, 0x04, 0xc3, 0xce, 0xac, 0x0e, 0xbb, 0xf5, 0x5d, 0x66, 0xfd, 0xa0, + 0xd5, 0x6a, 0x53, 0x1e, 0x5b, 0xa6, 0x94, 0x29, 0x59, 0x78, 0xff, 0x86, 0xfe, 0x39, 0x12, 0xc8, + 0x3c, 0x2a, 0x36, 0x74, 0xee, 0xd5, 0xaa, 0x1d, 0x0e, 0x65, 0x1a, 0xe3, 0x16, 0x68, 0x75, 0xf8, + 0x4f, 0xd4, 0x75, 0x8f, 0xc1, 0x42, 0x85, 0x72, 0xaf, 0x28, 0x42, 0xbd, 0x78, 0xf1, 0x06, 0x00, + 0x00, 0xe9, 0x5b, 0x50, 0xe2, 0x50, 0x53, 0xb4, 0x30, 0x45, 0x67, 0x75, 0x55, 0xb9, 0xf0, 0x84, + 0x3b, 0x50, 0x59, 0x70, 0xbd, 0xd8, 0x0d, 0xb0, 0xd6, 0x7f, 0xf1, 0x91, 0x94, 0x91, 0xd4, 0x13, + 0x3f, 0x35, 0x44, 0x83, 0x86, 0x40, 0x52, 0x51, 0x4d, 0x56, 0x8c, 0xc6, 0xd6, 0x83, 0xa1, 0xa0, + 0x9a, 0x72, 0x19, 0x2d, 0x17, 0xab, 0x40, 0x2b, 0xb5, 0x3a, 0x8c, 0xeb, 0xf3, 0xba, 0xce, 0x42, + 0xa4, 0x1a, 0x90, 0xf9, 0x32, 0xb7, 0xc0, 0x54, 0x48, 0xd2, 0xb7, 0x2b, 0x8d, 0xa3, 0xda, 0xa7, + 0x1f, 0x84, 0x03, 0x8d, 0x75, 0x19, 0x7c, 0x1e, 0xaf, 0x10, 0xb3, 0x9a, 0x6e, 0xa7, 0x2f, 0xac, + 0xf2, 0xc7, 0x42, 0x18, 0x39, 0x70, 0x47, 0x72, 0x4d, 0x08, 0xcb, 0xfa, 0xbb, 0x8f, 0x0e, 0x2b, + 0xce, 0xc5, 0xe2, 0x67, 0x08, 0xc6, 0x19, 0x12, 0x79, 0xf1, 0x49, 0x50, 0x52, 0x08, 0xdb, 0x9a, + 0x42, 0x18, 0xde, 0x56, 0xb4, 0x4e, 0x29, 0xe6, 0x5f, 0xbd, 0x72, 0x73, 0xb5, 0x1a, 0xb2, 0x17, + 0x7b, 0x61, 0xe5, 0xff, 0xb3, 0x34, 0x73, 0xf9, 0x5b, 0x67, 0x81, 0x6f, 0x5e, 0x00, 0x11, 0x95, + 0xec, 0x76, 0xae, 0x48, 0x12, 0xd0, 0xa6, 0xb4, 0xe8, 0x71, +}, key_der[] = { + 0x30, 0x82, 0x09, 0x43, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x09, 0x2d, 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, + 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, 0xa3, 0x62, 0xdb, 0x96, 0x68, 0x80, 0x82, 0x63, 0x4b, 0x49, + 0x3e, 0xe6, 0xf1, 0xa4, 0x88, 0x08, 0x2f, 0xe5, 0x96, 0x9b, 0x3f, 0xdf, 0x98, 0xaf, 0x08, 0x42, + 0xbd, 0x75, 0x5a, 0xd7, 0x9e, 0xeb, 0xf2, 0x14, 0xc9, 0x49, 0x68, 0xe4, 0x8e, 0xb4, 0xda, 0x6a, + 0xb5, 0xa9, 0xc2, 0xe1, 0x4f, 0xf9, 0x26, 0xa6, 0x84, 0x7c, 0x0e, 0x2d, 0xc3, 0x02, 0x61, 0xca, + 0x9d, 0x25, 0x9d, 0x3d, 0x6b, 0x67, 0xd4, 0x1b, 0x57, 0x2c, 0x4a, 0xcb, 0x95, 0x48, 0x87, 0x81, + 0x90, 0xeb, 0x65, 0x62, 0x27, 0x98, 0x40, 0x63, 0x28, 0xcd, 0x43, 0x65, 0xff, 0x82, 0xbc, 0xd1, + 0x99, 0xf8, 0x4c, 0xcf, 0x80, 0x1b, 0xf9, 0x9d, 0x37, 0xa4, 0x2d, 0x67, 0x1f, 0x23, 0x96, 0x59, + 0xb6, 0x81, 0xae, 0x20, 0xfd, 0x43, 0x97, 0xf2, 0x24, 0x34, 0x3c, 0x3c, 0xcc, 0x5c, 0xf8, 0x72, + 0x98, 0x8c, 0x7b, 0xf0, 0x45, 0x19, 0xe9, 0xb2, 0xc5, 0xd1, 0xe1, 0x2e, 0xb2, 0x87, 0x4a, 0x6f, + 0x04, 0xa3, 0xe9, 0xd3, 0xef, 0x7e, 0x2d, 0x22, 0xd9, 0xc7, 0x29, 0x3f, 0xe6, 0xe8, 0x34, 0x94, + 0xd3, 0x19, 0x59, 0xd7, 0x77, 0x7a, 0x7a, 0x12, 0xd1, 0x9b, 0xbf, 0xfe, 0x37, 0x1e, 0x3b, 0x33, + 0x75, 0xcc, 0x4d, 0x11, 0xf9, 0xa8, 0xa3, 0xff, 0xed, 0x34, 0xc4, 0xda, 0xcd, 0x14, 0xeb, 0xe3, + 0x34, 0xb6, 0xc1, 0x88, 0xdb, 0x3a, 0x51, 0x8b, 0xe9, 0xba, 0x8f, 0x38, 0x4d, 0xc8, 0xc0, 0x53, + 0x27, 0x5b, 0xb9, 0xf2, 0xa0, 0x1e, 0xdd, 0x95, 0xb9, 0xff, 0xe6, 0x00, 0x8a, 0xe6, 0x58, 0x00, + 0x1e, 0xa7, 0xe5, 0xb8, 0x54, 0xa7, 0x8a, 0x05, 0xb8, 0x1e, 0x70, 0x61, 0xb7, 0x01, 0xcb, 0x05, + 0x51, 0xf2, 0xe8, 0xc8, 0x9e, 0x91, 0x7c, 0x6e, 0xe5, 0x90, 0x52, 0x3c, 0xb9, 0x37, 0xca, 0x52, + 0x36, 0x9e, 0xec, 0xcd, 0xd6, 0x2c, 0x9c, 0xb2, 0x69, 0xbc, 0x07, 0x74, 0xb2, 0x26, 0xeb, 0x34, + 0xf8, 0xc2, 0xd0, 0x54, 0x02, 0x36, 0xba, 0x4d, 0x8e, 0x02, 0x66, 0x20, 0xad, 0xfe, 0x98, 0xa9, + 0x38, 0x91, 0x75, 0xfb, 0x65, 0x3c, 0x1e, 0x7e, 0x80, 0x33, 0x4c, 0xae, 0x25, 0xda, 0x91, 0xcd, + 0xb8, 0x2e, 0x77, 0x41, 0x57, 0x3f, 0x10, 0x5f, 0xbe, 0x18, 0x12, 0xc0, 0xc6, 0x6b, 0xc2, 0x0e, + 0xaf, 0x59, 0xa4, 0xc2, 0x18, 0x8b, 0xb3, 0xa6, 0xce, 0x49, 0x00, 0x28, 0xa0, 0xbd, 0x51, 0xee, + 0x84, 0x7f, 0x6d, 0x7b, 0x2c, 0x54, 0x02, 0x14, 0x80, 0x4a, 0x23, 0x3b, 0xfd, 0x72, 0x08, 0xbd, + 0x7f, 0x03, 0xcc, 0x2e, 0x1a, 0xca, 0x95, 0xea, 0x15, 0x44, 0xdb, 0x1e, 0x70, 0x1b, 0x02, 0x3f, + 0x9e, 0xbd, 0x5a, 0x02, 0x57, 0x85, 0x49, 0xf0, 0x7f, 0x69, 0x68, 0x9f, 0x87, 0xc4, 0x66, 0xbd, + 0xfe, 0xbd, 0x1b, 0x9c, 0xf6, 0xc8, 0x5f, 0xaa, 0x75, 0x74, 0x9c, 0xf3, 0x75, 0x20, 0xc4, 0xa7, + 0xcd, 0x70, 0x9a, 0xb2, 0xde, 0xc8, 0xd9, 0xf8, 0xae, 0x45, 0x77, 0x48, 0xcf, 0xde, 0x8a, 0x8e, + 0x51, 0x90, 0xa4, 0xfe, 0x17, 0x7c, 0xd5, 0x40, 0xf9, 0x11, 0x8b, 0xed, 0xa3, 0x27, 0x58, 0xe1, + 0x48, 0x69, 0x5a, 0xca, 0x58, 0xbc, 0xc0, 0xb6, 0x0c, 0xe8, 0x18, 0xc4, 0xef, 0x3f, 0xf0, 0x2e, + 0x7a, 0x12, 0x97, 0x9d, 0xc0, 0x49, 0x85, 0x8b, 0x56, 0xd2, 0x5b, 0x53, 0x8a, 0x85, 0x71, 0xfb, + 0x9c, 0x93, 0x61, 0x20, 0x19, 0x5a, 0x5f, 0x88, 0xb2, 0xc9, 0x97, 0x8d, 0xe7, 0xf1, 0x26, 0xa6, + 0x22, 0xdb, 0xfe, 0xd0, 0x5a, 0x6b, 0xf5, 0x40, 0x2f, 0x69, 0xb0, 0xd7, 0x23, 0x4c, 0xc6, 0x81, + 0x40, 0xb3, 0x74, 0xdd, 0x3d, 0x50, 0x7a, 0x56, 0xec, 0xed, 0x8d, 0xbb, 0xb3, 0x17, 0x44, 0x9c, + 0xd5, 0x2d, 0x87, 0x89, 0x08, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x02, 0x00, 0x55, + 0x9e, 0xf0, 0xc4, 0x19, 0x6f, 0x7e, 0xe4, 0xda, 0x07, 0x40, 0x57, 0x76, 0x3a, 0x6a, 0xaf, 0x1f, + 0xaa, 0x89, 0x0a, 0x42, 0xa6, 0xc2, 0x34, 0xb7, 0x77, 0x82, 0x21, 0x85, 0xc1, 0x89, 0x1e, 0xcc, + 0x75, 0xe8, 0x25, 0xf8, 0x3a, 0x0e, 0x2e, 0xe8, 0x67, 0x13, 0x5c, 0x2b, 0x2c, 0x37, 0xe4, 0xb1, + 0x44, 0x82, 0x19, 0x20, 0xb5, 0x0a, 0x84, 0xad, 0x0a, 0xa8, 0xdf, 0x95, 0x4f, 0x22, 0x81, 0xfe, + 0xbd, 0x75, 0x29, 0x58, 0xe8, 0xe7, 0x0a, 0x63, 0x38, 0x9a, 0xe1, 0x40, 0xf7, 0xf7, 0x17, 0xea, + 0x66, 0x0c, 0x73, 0xc4, 0xe6, 0x26, 0xc8, 0x34, 0x7b, 0x02, 0xdd, 0x04, 0x23, 0x99, 0x57, 0x0f, + 0xb0, 0x3c, 0x00, 0x65, 0x6a, 0xac, 0xfe, 0xd1, 0x43, 0xa2, 0x48, 0xc3, 0x1f, 0xb6, 0x99, 0x3d, + 0x7f, 0x3f, 0x49, 0xc0, 0x67, 0x7c, 0x11, 0x1c, 0x81, 0xb1, 0x3f, 0xad, 0x93, 0x74, 0x22, 0xe8, + 0x3d, 0x2f, 0x3d, 0x95, 0x6c, 0x0b, 0x52, 0xaa, 0xc7, 0x12, 0xff, 0x73, 0x02, 0x05, 0x77, 0x71, + 0xdf, 0xd9, 0x90, 0x6d, 0x25, 0x77, 0xb4, 0x28, 0x19, 0xf5, 0xa6, 0x4b, 0x56, 0x86, 0xde, 0x40, + 0x2a, 0xac, 0x7d, 0x9a, 0x57, 0x76, 0x3a, 0xf9, 0x7b, 0x36, 0x38, 0x22, 0x0b, 0x51, 0x71, 0xf6, + 0xbf, 0x9f, 0x67, 0x0f, 0xe2, 0x39, 0xa6, 0xc5, 0x17, 0x04, 0x00, 0xe1, 0xda, 0xfe, 0x47, 0xc9, + 0x84, 0x30, 0xaf, 0xfb, 0x6d, 0xde, 0x15, 0x5d, 0xf4, 0x35, 0xa3, 0xf4, 0x06, 0x19, 0xb3, 0x13, + 0x1b, 0xeb, 0xa5, 0x16, 0xbb, 0x22, 0x0f, 0x23, 0xfe, 0xac, 0x12, 0x00, 0x68, 0x60, 0xb4, 0x8b, + 0xb8, 0x03, 0x8c, 0xb0, 0x08, 0x05, 0x07, 0x83, 0x84, 0xfe, 0x34, 0xf5, 0x98, 0x6c, 0xc0, 0x81, + 0x1c, 0xfc, 0x60, 0x6d, 0x38, 0x35, 0x37, 0xef, 0x66, 0xb6, 0x09, 0x02, 0xbf, 0xbb, 0x84, 0x3f, + 0x1c, 0x14, 0x2f, 0xb8, 0x1b, 0x4a, 0x14, 0xd9, 0x06, 0x52, 0x8a, 0x0b, 0x80, 0x20, 0x9b, 0x17, + 0x1c, 0xe0, 0x35, 0x41, 0x9c, 0xf3, 0x71, 0x81, 0xff, 0xa2, 0x30, 0x6c, 0x43, 0x3b, 0x47, 0x9b, + 0x97, 0xaa, 0xc1, 0x62, 0x13, 0xbd, 0x4b, 0xa6, 0x6a, 0xe8, 0x0f, 0x28, 0xca, 0x4e, 0x54, 0x3c, + 0x61, 0x99, 0x29, 0x21, 0xc2, 0xcd, 0x54, 0xbc, 0x34, 0xba, 0xca, 0x06, 0x60, 0x71, 0x66, 0xda, + 0xbb, 0xc2, 0xc8, 0x45, 0x65, 0x7e, 0xc1, 0x37, 0x51, 0xbf, 0x1c, 0x17, 0x24, 0xc5, 0x93, 0x9d, + 0x12, 0x78, 0xe7, 0x05, 0xd9, 0x02, 0xf6, 0xc7, 0x32, 0xa6, 0x99, 0xb6, 0x44, 0xa5, 0x78, 0x25, + 0xc4, 0x11, 0xd1, 0xd2, 0x18, 0xe0, 0xa2, 0x7d, 0x08, 0x28, 0x90, 0xc6, 0x7e, 0x8a, 0xf8, 0x6c, + 0x73, 0xbb, 0x36, 0xdf, 0xb5, 0x11, 0xc7, 0xbc, 0xbb, 0x6a, 0x13, 0x10, 0xab, 0xe9, 0xcf, 0x96, + 0x88, 0x9f, 0x8e, 0x0e, 0x78, 0x2e, 0x66, 0x02, 0x94, 0x46, 0xcb, 0xcd, 0xff, 0xd1, 0xbb, 0xec, + 0x7a, 0xc9, 0xd6, 0x8c, 0x31, 0x3f, 0x6c, 0x6a, 0x68, 0x4f, 0xca, 0x85, 0xbb, 0x2f, 0xb4, 0xba, + 0xb0, 0xc4, 0x3c, 0xd2, 0x1d, 0xe3, 0x85, 0xdc, 0x26, 0x6d, 0x48, 0x44, 0x89, 0x46, 0xe7, 0xa1, + 0x2b, 0xc4, 0x2d, 0xe5, 0xd2, 0xcd, 0x75, 0xc2, 0xb2, 0x29, 0x4e, 0x65, 0xd7, 0x72, 0x4a, 0xb0, + 0xcc, 0x54, 0x7d, 0xb3, 0x6c, 0xfb, 0x7f, 0x4c, 0xe3, 0x7b, 0x2c, 0x6a, 0x66, 0x0e, 0x0d, 0x4c, + 0xf2, 0x3b, 0xc2, 0x43, 0x37, 0x33, 0xc0, 0x57, 0x96, 0xfa, 0x76, 0x19, 0x30, 0x48, 0x7a, 0x8c, + 0x6b, 0x58, 0x1e, 0x15, 0xdd, 0x80, 0x2b, 0xc2, 0xef, 0x10, 0x17, 0xcd, 0x10, 0x06, 0x05, 0x73, + 0x9a, 0x01, 0xe5, 0xdb, 0x89, 0xd3, 0x83, 0x4d, 0x14, 0x1f, 0x53, 0xa3, 0x66, 0xc0, 0x01, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xce, 0xc5, 0xfb, 0x52, 0x0d, 0xb4, 0xaa, 0x1b, 0x2b, 0x5c, 0x5a, 0xa3, + 0xd8, 0x3f, 0x74, 0x99, 0x1c, 0x05, 0x83, 0x03, 0x43, 0xb8, 0x00, 0x21, 0x0c, 0xf9, 0xe0, 0xb0, + 0x6a, 0xef, 0x40, 0x4a, 0xeb, 0x65, 0xd0, 0x80, 0xe5, 0x34, 0x33, 0x09, 0xf2, 0x70, 0xb6, 0xa6, + 0x1d, 0xb9, 0x04, 0xc7, 0xb9, 0x84, 0x70, 0xd6, 0xa7, 0x67, 0x06, 0x40, 0x9a, 0x20, 0xee, 0x96, + 0x7f, 0xde, 0xa4, 0x28, 0x81, 0x08, 0x68, 0xda, 0x05, 0x27, 0x88, 0xa0, 0xe2, 0x7c, 0xde, 0xfb, + 0xe3, 0x44, 0x1d, 0xca, 0x49, 0x65, 0x4f, 0x34, 0xd5, 0x44, 0xea, 0xa6, 0x3f, 0xcf, 0x9e, 0x7e, + 0xb7, 0x88, 0xbe, 0xa9, 0x73, 0x1e, 0x6b, 0xaa, 0x68, 0x67, 0xc6, 0xb3, 0x9a, 0x13, 0x91, 0x96, + 0x96, 0x8f, 0x9b, 0x2e, 0xf8, 0x1f, 0x9b, 0x4f, 0xef, 0x6b, 0x23, 0x06, 0x5c, 0xc1, 0xfb, 0x39, + 0x61, 0x12, 0x0d, 0x85, 0x04, 0x71, 0xd7, 0xba, 0x9a, 0xfb, 0xec, 0x61, 0xe6, 0x67, 0xc4, 0xdb, + 0x97, 0x3e, 0x33, 0xd7, 0xe2, 0x20, 0x14, 0xe2, 0x35, 0x2a, 0x38, 0x95, 0x3c, 0x56, 0x30, 0x14, + 0xa1, 0x9c, 0xaf, 0x31, 0xac, 0x66, 0x8c, 0x12, 0x63, 0x7b, 0x5b, 0x4a, 0x93, 0x31, 0xb1, 0x47, + 0x3e, 0x04, 0x33, 0xe4, 0x57, 0x31, 0x46, 0x30, 0x82, 0xab, 0x01, 0xe2, 0x97, 0x03, 0x41, 0x78, + 0xb0, 0xd3, 0xa7, 0xf6, 0x44, 0x08, 0x40, 0x7b, 0xcb, 0x7e, 0x24, 0x85, 0x58, 0x79, 0xdf, 0x59, + 0x81, 0x13, 0x69, 0x8d, 0xcd, 0x25, 0x48, 0x41, 0xc1, 0x99, 0x3f, 0x52, 0x3f, 0x0e, 0xf5, 0xe3, + 0x5b, 0xb5, 0x14, 0x35, 0xd8, 0x05, 0xc2, 0x28, 0xbf, 0x19, 0x6f, 0xba, 0x33, 0x4b, 0x94, 0x0f, + 0x2d, 0xb7, 0x51, 0x54, 0x29, 0x6c, 0x5c, 0xdc, 0x57, 0xca, 0x35, 0x0b, 0x69, 0xd9, 0x73, 0x81, + 0x5b, 0xe3, 0x3c, 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x48, 0x99, 0x05, 0xc3, 0x0b, 0x91, + 0x9d, 0xa5, 0x49, 0x4b, 0xa5, 0xb1, 0x38, 0xa8, 0xd7, 0xf0, 0xc0, 0xae, 0xf7, 0xf7, 0x0a, 0x3e, + 0x7c, 0x01, 0xbf, 0x69, 0xa6, 0x23, 0x68, 0xe0, 0x1b, 0x11, 0xd3, 0xc3, 0x9b, 0x2b, 0xdd, 0xa8, + 0x66, 0x17, 0x97, 0x93, 0x6f, 0xc6, 0x68, 0xd7, 0xd0, 0x68, 0xc3, 0x2b, 0x4d, 0xfa, 0xda, 0xfa, + 0xd9, 0x91, 0x68, 0x20, 0x10, 0x3d, 0x51, 0xb7, 0x3d, 0x7a, 0xc1, 0x00, 0x53, 0xc9, 0x77, 0x7e, + 0x08, 0x1d, 0x7c, 0xcf, 0x36, 0x72, 0xe4, 0x7d, 0xb0, 0x67, 0x1f, 0x41, 0x5a, 0x02, 0x87, 0xcb, + 0x4c, 0x83, 0xa0, 0x4f, 0xf0, 0x80, 0x4b, 0x3a, 0x66, 0xd2, 0x52, 0x13, 0x77, 0x3c, 0x6d, 0xa6, + 0xdf, 0xd2, 0x3c, 0xd3, 0x6b, 0xb4, 0x7c, 0x53, 0x55, 0x40, 0x22, 0x4a, 0x87, 0x1d, 0x66, 0xd4, + 0xc1, 0x45, 0x2c, 0xeb, 0xbb, 0x95, 0x57, 0x03, 0x4b, 0xd2, 0x4d, 0xfa, 0x86, 0x15, 0x3d, 0xbe, + 0x8c, 0x0d, 0xf0, 0x4b, 0x9b, 0x98, 0xce, 0x88, 0xfb, 0x98, 0x90, 0x56, 0x78, 0x80, 0x7e, 0xfd, + 0x27, 0xb8, 0x17, 0x23, 0x4f, 0xd8, 0x2a, 0x16, 0x89, 0xef, 0x25, 0xed, 0x85, 0x85, 0x64, 0x76, + 0xb4, 0x85, 0xe8, 0x4a, 0x28, 0x7a, 0xbe, 0x11, 0x66, 0x09, 0x9a, 0xeb, 0x60, 0xdd, 0xd5, 0x53, + 0x73, 0x4a, 0xad, 0xc9, 0x06, 0x8e, 0xab, 0x62, 0x31, 0x7b, 0x2e, 0xf7, 0x7e, 0x47, 0x00, 0xc2, + 0x47, 0x5b, 0x61, 0x1e, 0xb9, 0x9f, 0xfc, 0x85, 0xe9, 0x97, 0x1a, 0x4d, 0x56, 0x4a, 0x0c, 0x57, + 0x1b, 0x73, 0x6e, 0xba, 0xdb, 0x82, 0x70, 0xb6, 0xe5, 0x09, 0xaf, 0x45, 0x87, 0x34, 0xae, 0x54, + 0xbf, 0x92, 0xf3, 0x38, 0xc9, 0x08, 0x4c, 0x1f, 0x77, 0x80, 0xec, 0x8c, 0x9c, 0x0d, 0x93, 0x29, + 0x63, 0xed, 0x31, 0x9b, 0xb2, 0x3b, 0x8d, 0x34, 0xfb, 0x02, 0x82, 0x01, 0x00, 0x62, 0xb3, 0x28, + 0x83, 0x03, 0x5d, 0xd0, 0xb1, 0x05, 0x62, 0xa1, 0x35, 0x82, 0x7c, 0xcf, 0xb8, 0x62, 0x22, 0xd3, + 0x65, 0xd4, 0x86, 0x59, 0x31, 0x6d, 0x93, 0x3d, 0x48, 0x98, 0xd2, 0xb9, 0x7a, 0xc9, 0xa0, 0xa1, + 0x05, 0x55, 0xe3, 0x33, 0xd5, 0xb4, 0xaf, 0x4e, 0xd0, 0x3e, 0x71, 0xd9, 0xb1, 0x48, 0x81, 0xca, + 0xa6, 0xfb, 0xe3, 0x76, 0x9d, 0x91, 0xb4, 0xd4, 0x8e, 0x6c, 0x5d, 0x27, 0x38, 0xda, 0x56, 0xdc, + 0x4d, 0xed, 0x95, 0xf0, 0x66, 0xf3, 0x95, 0xad, 0x8e, 0xc8, 0xed, 0xf3, 0xd6, 0x62, 0x70, 0x84, + 0x7d, 0x70, 0xab, 0xe3, 0xe2, 0x15, 0xa5, 0x92, 0x3f, 0x64, 0x76, 0x56, 0xa4, 0x65, 0xfa, 0x08, + 0x64, 0xa0, 0x4f, 0xa1, 0x0e, 0x8c, 0x26, 0x79, 0x21, 0x4b, 0x9f, 0x22, 0xf1, 0x29, 0xa9, 0x54, + 0xa6, 0xb4, 0x5f, 0x0c, 0xa9, 0xf5, 0xce, 0xf6, 0x8f, 0x6e, 0x21, 0x82, 0xe8, 0x92, 0xb5, 0x90, + 0xc7, 0x57, 0x41, 0x97, 0x95, 0x27, 0xb9, 0x32, 0xc3, 0xab, 0x0f, 0x1b, 0x0a, 0x1a, 0xbb, 0x3b, + 0x9c, 0xba, 0xc9, 0xfb, 0x96, 0x68, 0xe5, 0xaf, 0x2f, 0xb9, 0xf1, 0x23, 0xc3, 0x6f, 0x4a, 0xc7, + 0xe3, 0xe3, 0x2e, 0xb7, 0xe6, 0x02, 0x1a, 0xff, 0x47, 0x45, 0x78, 0x16, 0x19, 0x11, 0xf1, 0xc8, + 0x52, 0x51, 0x9d, 0x35, 0x5a, 0x26, 0xc1, 0x7c, 0x18, 0x13, 0x38, 0x04, 0xfd, 0xcd, 0x7d, 0xae, + 0xe2, 0x28, 0xc1, 0x7e, 0xc7, 0x53, 0xf3, 0x60, 0xc4, 0xc5, 0x93, 0x31, 0x98, 0x69, 0x6b, 0x39, + 0x71, 0x81, 0xeb, 0x17, 0xc9, 0xb7, 0xa5, 0xf9, 0x83, 0x5c, 0x7c, 0x34, 0x38, 0x7b, 0x74, 0x4c, + 0x38, 0xcc, 0xf7, 0x64, 0x58, 0x9a, 0x31, 0xa2, 0x6c, 0x18, 0x63, 0x5f, 0xe3, 0xef, 0x9d, 0xf5, + 0x39, 0x8c, 0x82, 0x4e, 0x0d, 0xb3, 0xaa, 0x03, 0xb3, 0xa4, 0xdb, 0xf4, 0x01, 0x02, 0x82, 0x01, + 0x01, 0x00, 0x96, 0x33, 0x77, 0xe4, 0x8e, 0x62, 0x8d, 0xba, 0x88, 0x1b, 0xb7, 0x9f, 0x0d, 0xcb, + 0xeb, 0x9b, 0x84, 0x7a, 0x1e, 0xb1, 0xa2, 0xef, 0x29, 0x5c, 0x7d, 0x13, 0xbb, 0x88, 0x10, 0xac, + 0xf4, 0x13, 0x45, 0x96, 0x7f, 0x9d, 0x3d, 0xe2, 0x36, 0x03, 0xb0, 0xaa, 0xed, 0x60, 0x46, 0xec, + 0x5c, 0xab, 0xb4, 0xce, 0x8e, 0xde, 0x35, 0x51, 0xda, 0x88, 0x28, 0xef, 0x2f, 0x37, 0xbf, 0xc0, + 0x68, 0x96, 0xaf, 0x0a, 0x96, 0x8a, 0xa0, 0x83, 0x28, 0xc3, 0x2f, 0xda, 0x18, 0x26, 0xef, 0x02, + 0xf8, 0xcd, 0x3e, 0x95, 0x37, 0xba, 0x75, 0x3c, 0x8d, 0xd9, 0x7f, 0xb7, 0x4f, 0x04, 0x5e, 0xce, + 0xfd, 0x4b, 0x92, 0x0a, 0x3d, 0xc8, 0x00, 0xc7, 0xce, 0xec, 0x4d, 0x38, 0xbb, 0x28, 0x33, 0x79, + 0x49, 0x8b, 0x78, 0xb6, 0xbd, 0xae, 0x3c, 0x47, 0xb9, 0xdc, 0xd4, 0xd7, 0xb9, 0x26, 0xad, 0x8a, + 0x51, 0xb9, 0x40, 0x2c, 0x84, 0xc4, 0x81, 0x0b, 0x3a, 0xec, 0xd6, 0x00, 0xc2, 0xb3, 0x83, 0xb0, + 0x80, 0x88, 0x89, 0x4d, 0x4b, 0xd7, 0xe8, 0x59, 0xe2, 0xf2, 0x56, 0x40, 0x60, 0x09, 0x0e, 0x92, + 0x99, 0xef, 0xcb, 0xf2, 0xd6, 0xbe, 0x99, 0x40, 0xf2, 0xdf, 0xb2, 0xba, 0xbc, 0x2d, 0xf8, 0x8e, + 0x1f, 0x6f, 0x2b, 0xdc, 0xab, 0xc0, 0x5e, 0x97, 0xe3, 0x82, 0x2d, 0x46, 0x83, 0x89, 0x69, 0xf0, + 0x9a, 0x55, 0xf1, 0x88, 0xfb, 0x5e, 0xf9, 0xab, 0xf7, 0x96, 0x72, 0xa4, 0xd7, 0xe2, 0xaf, 0x88, + 0x1b, 0x8b, 0x4a, 0x96, 0xce, 0x2c, 0x2f, 0x89, 0xa0, 0x38, 0x92, 0xea, 0xfa, 0xb6, 0xb9, 0xd1, + 0xa6, 0x0c, 0xc5, 0xb7, 0x2e, 0xa2, 0x69, 0x9c, 0xb4, 0xf3, 0x17, 0x53, 0xa0, 0xab, 0xad, 0x8c, + 0x90, 0xa4, 0xf4, 0xc7, 0x30, 0xd5, 0x43, 0x43, 0x2d, 0xad, 0xb4, 0x57, 0x6c, 0xab, 0xd8, 0x8a, + 0x4e, 0x77, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc9, 0xad, 0xff, 0xcc, 0xaf, 0x3d, 0xf9, 0x52, 0xfb, + 0x1b, 0xf7, 0x92, 0x0f, 0xd9, 0x06, 0xf4, 0x7d, 0x24, 0x1d, 0x48, 0x9f, 0x69, 0xf7, 0xad, 0x40, + 0x98, 0x60, 0x3e, 0x3b, 0x45, 0xe2, 0x85, 0xa8, 0x9d, 0x37, 0x56, 0x6a, 0xb9, 0x0b, 0xd9, 0xd8, + 0xe7, 0xab, 0x3d, 0xc3, 0xb3, 0x94, 0x3b, 0xca, 0x5e, 0xac, 0x15, 0xe5, 0x25, 0x89, 0x8a, 0x65, + 0x08, 0x4e, 0xe3, 0x6f, 0x77, 0x96, 0xfc, 0x59, 0x0f, 0x62, 0x2a, 0xe0, 0xd7, 0x19, 0x6d, 0x54, + 0x82, 0x32, 0x81, 0xc0, 0x53, 0x38, 0x73, 0x63, 0x76, 0xeb, 0x76, 0x0b, 0x52, 0x23, 0x16, 0xb6, + 0x80, 0x6b, 0xde, 0x18, 0x07, 0xb3, 0x67, 0x7f, 0x2a, 0x28, 0x85, 0x36, 0xe9, 0xd9, 0x33, 0xed, + 0xd7, 0x84, 0x09, 0x8e, 0x2f, 0xae, 0xc4, 0x64, 0xc2, 0x1a, 0x53, 0x5b, 0x42, 0xc6, 0x54, 0x2a, + 0x63, 0x71, 0x0a, 0x1a, 0x2a, 0xfc, 0xa6, 0x02, 0x80, 0xa6, 0x02, 0xcf, 0x15, 0xda, 0x83, 0x2b, + 0x66, 0x2c, 0x35, 0x61, 0x0f, 0x6e, 0x39, 0x4a, 0x16, 0xc0, 0xea, 0xa6, 0xd7, 0x06, 0x6a, 0x99, + 0x57, 0x0e, 0x5e, 0xf3, 0xc8, 0x4b, 0x68, 0x16, 0x02, 0xcd, 0xdf, 0x42, 0x55, 0xa3, 0x1f, 0xd8, + 0x64, 0x71, 0x04, 0xcc, 0xb1, 0x46, 0x97, 0x40, 0x33, 0x83, 0xd1, 0xaa, 0xa4, 0x49, 0x8d, 0xc4, + 0x36, 0xa3, 0xaf, 0x6c, 0x25, 0x75, 0xfe, 0x85, 0x29, 0x46, 0x2d, 0xf4, 0xef, 0xa9, 0x21, 0x0a, + 0x80, 0x17, 0x23, 0x56, 0xca, 0x4a, 0x7f, 0xc0, 0xbd, 0x1d, 0xca, 0x0c, 0xfd, 0x78, 0x07, 0x9b, + 0x68, 0x1c, 0x8f, 0xc5, 0xe4, 0xe4, 0xd2, 0x12, 0x21, 0xa1, 0x84, 0x77, 0xac, 0x81, 0x1a, 0xec, + 0x7c, 0x1a, 0xe9, 0x11, 0x8d, 0x48, 0x01, 0x3b, 0x4f, 0xab, 0x5b, 0x5a, 0x05, 0x96, 0x68, 0x81, + 0x1a, 0x88, 0xde, 0xb3, 0xa4, 0x90, 0xf9, + +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + const char *p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, + * lws must have been configured and built with + * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */, ret = 1; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal http server TLS | visit https://localhost:7681\n"); + + signal(SIGINT, sigint_handler); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS | + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + + if (lws_cmdline_option(argc, argv, "-h")) + info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + info.port = 7681; + info.mounts = &mount; + info.error_document_404 = "/404.html"; + info.server_ssl_cert_mem = cert_pem; + info.server_ssl_cert_mem_len = strlen(cert_pem); + info.server_ssl_private_key_mem = key_pem; + info.server_ssl_private_key_mem_len = strlen(key_pem); + info.vhost_name = "first"; + + if (!lws_create_vhost(context, &info)) { + lwsl_err("Failed to create first vhost\n"); + goto bail; + } + + info.port = 7682; + info.mounts = &mount; + info.error_document_404 = "/404.html"; + info.server_ssl_cert_mem = cert_der; + info.server_ssl_cert_mem_len = sizeof(cert_der); + info.server_ssl_private_key_mem = key_der; + info.server_ssl_private_key_mem_len = sizeof(key_der); + info.vhost_name = "second"; + + if (!lws_create_vhost(context, &info)) { + lwsl_err("Failed to create second vhost\n"); + goto bail; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + ret = 0; + +bail: + lws_context_destroy(context); + + return ret; +} diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html new file mode 100644 index 000000000..6fdd6bf33 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html @@ -0,0 +1,11 @@ + + + + +
+ +

404

+ Sorry, that file doesn't exist. + + + diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js new file mode 100644 index 000000000..1606ea03a --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js @@ -0,0 +1,22 @@ +document.addEventListener("DOMContentLoaded", function() { + + var transport_protocol = ""; + + if ( performance && performance.timing.nextHopProtocol ) { + transport_protocol = performance.timing.nextHopProtocol; + } else if ( window.chrome && window.chrome.loadTimes ) { + transport_protocol = window.chrome.loadTimes().connectionInfo; + } else { + + var p = performance.getEntriesByType("resource"); + for (var i=0; i < p.length; i++) { + var value = "nextHopProtocol" in p[i]; + if (value) + transport_protocol = p[i].nextHopProtocol; + } + } + + if (transport_protocol == "h2") + document.getElementById("transport").innerHTML = ""; + } +}, false); \ No newline at end of file diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/favicon.ico b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0cc2e3dff34012ba3d4a7848a7ed17579788ec5 GIT binary patch literal 1406 zcmZQzU<5(|0R}M0U}azs1F|%L7$l?s#Ec9aKoZP=&`9i!<^REA8>%80(yxAC$j<-A zkb5S8;qL6446ipNFl>5#fuVR6L=8goC~GtXMnhmYga9MSfQgBTk&TUw5$JocUP63y z3phA97+G0a8QIy{!BT|y==xb$SQt4uIT@LmnZZ(o_~`mk`Tv1M8w?+DXJCL~kQj^& JqOtKoVgQl$ETjMc literal 0 HcmV?d00001 diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/http2.png b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/http2.png new file mode 100644 index 0000000000000000000000000000000000000000..439bfa482fa00e69af2d562f17a6e89453eb98cc GIT binary patch literal 7563 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 z + + + + + + +
+ + Hello from the minimal https server example. +
+ You can confirm the 404 page handler by going to this + nonexistant page. +
+
+ + diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg new file mode 100644 index 000000000..7baea649f --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg new file mode 100644 index 000000000..cd128f1d2 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +