nghttpx: Rewrite CertLookupTree using Router

This commit is contained in:
Tatsuhiro Tsujikawa 2016-06-25 00:28:15 +09:00
parent 2a4733857f
commit f7c0d48152
24 changed files with 629 additions and 295 deletions

View File

@ -166,7 +166,7 @@ if(ENABLE_APP)
) )
target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS}) target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS})
target_compile_definitions(nghttpx-unittest target_compile_definitions(nghttpx-unittest
PRIVATE "-DNGHTTP2_TESTS_DIR=\"${CMAKE_SOURCE_DIR}/tests\"" PRIVATE "-DNGHTTP2_SRC_DIR=\"${CMAKE_SOURCE_DIR}/src\""
) )
target_link_libraries(nghttpx-unittest nghttpx_static ${CUNIT_LIBRARIES}) target_link_libraries(nghttpx-unittest nghttpx_static ${CUNIT_LIBRARIES})
if(HAVE_MRUBY) if(HAVE_MRUBY)

View File

@ -22,7 +22,10 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = includes SUBDIRS = includes
EXTRA_DIST = CMakeLists.txt EXTRA_DIST = \
CMakeLists.txt \
test.example.com.pem \
test.nghttp2.org.pem
bin_PROGRAMS = bin_PROGRAMS =
check_PROGRAMS = check_PROGRAMS =
@ -187,7 +190,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
template_test.cc template_test.h \ template_test.cc template_test.h \
base64_test.cc base64_test.h base64_test.cc base64_test.h
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \ nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" -DNGHTTP2_SRC_DIR=\"$(top_srcdir)/src\"
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@
if HAVE_MRUBY if HAVE_MRUBY

17
src/ca-config.json Normal file
View File

@ -0,0 +1,17 @@
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"server": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth"
]
}
}
}
}

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA1kVkF8QSUwW/HV9EFRPSMoiOVmYwB8vqKDtT0d6MFiKAM8/Y
JFUq2uKlUydgT4IPE7PATvVcIj3GtL9XzPhscqYO/S0Y7scyTE2VAPmtz+StPWf2
wZ1IQR09HrnDTc44KvYGZpefBZkD9UjbmJ9a1ZmJjJiMr3hTnKE/sxZ2+dMsnMZX
N822cfaHyTN+T0+Tyw5vBBboCDsZzxmf+9FFIDJNs3NL34cR8EZRhpfaegapH8bt
OJ+D+RZ2kg7E/YYkGcS6NodvTjSUFCFHpWjHCfTFhn/owBIAooCdWorh6dc8Q72l
AodwNLXS8uuPgPqM5s4Cz57m7Zgs4OilNmIdawIDAQABAoIBAQCwqLtygLye6KD+
RXorapEmCsJX5553/x6Klwdvg+25ni5XCWjp47IWj0DBQzi7tL5bfxrxvod8z7QR
d6SbIMLA77px8Ima7G7CzEAqcrBkM+TFOP8P+G4HCWVH/N5SOtDCUt9KHH4Grna9
95jdx5yreRAX8/oh/bHp9GRBcicbpwYMVWOnjTE2seEUYQOpdpYdP4bOPUvAju0l
mwmy2/dDGmbibktN3sdHEhDodKu+Znv7nFZo0jzhlyoXse653WcvaQeZZYuojvSe
Sr92DvPp7UaYrb4KvT7ujXiPavSV2m/4EmGtyqevUf2dZ6sfMXZjmXsjWz9txhWp
4BgbHyHRAoGBAPqyuNj2CDD3FE7N3Hxyba8d+ZtsVUNawjq2gwOvT9NLsMstOGyH
OCc1v4W6Sq4w1wo4nIJyY8kNZwtReaTHOPZlDgBhVvk/x8eLBu+QTMRyocRt1LoD
8HyKxWSAnYTtCh/GUEQ37amIqvOJ5GNL+25WDzevLa5kMYWG743uxEupAoGBANrN
c/fVxepvP0GISlLpL3aZCFGAjMrq3xUYcf/w4wPoMq6AdpIPeRVBmJ1/Uqw1FkV8
NRKJNPE2YcMuv8iMeQlacoPd34KT9ob80EYVlMwAkeC0NK+FfiM/UteR0wB49gmi
ugX9YlJytOP9aUgPvEGT6l+XtgGC44W1TQWe62zzAoGBAKZenNU+0UjNb6isbToZ
Jjkkh1Vhm2PLg0I7hM6ZNTxf6r+rDtrXEajTvnocmxrmRo796r+W8immv09/jl6P
53l8rsIJ1xIqBYai+MNa29cyy6/zw0x++MVtwnlj8SUZubJEhVgAVbRAglKEnBBZ
iE48xnSJyKMG0uZuGePzJEmhAoGBAIOHJcNBumum3DuklikpC+MbMyjrQbdpYRjp
TP4x7AWZO34ysxQyQPNKL1feBfCHKRA0DiNKX4zwx+vw2lDQQKIiwNwMMCPqljOn
HfxDVOMdJJQTP+iTMrQ1iLMVceXC0QQR0glvu/8b/SlgWD19WAmDxUwZgst9xw/F
YLuUQKmJAoGAREeTugd4hc0U/YV/BQQjSCLhl11EtVry/oQMHj8KZpIJhP7tj8lw
hSE0+z04oMhiTeq55PYKQkTo5l6V4PW0zfpEwlKEEm0erab1G9Ddh7us47XFcKLl
Rmk192EVZ0lQuzftsYv7dzRLiAR7yDFXwD1ELIK/uPkwBtu7wtHlq+M=
-----END RSA PRIVATE KEY-----

17
src/ca.nghttp2.org.csr Normal file
View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICwjCCAaoCAQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2Eu
bmdodHRwMi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWRWQX
xBJTBb8dX0QVE9IyiI5WZjAHy+ooO1PR3owWIoAzz9gkVSra4qVTJ2BPgg8Ts8BO
9VwiPca0v1fM+Gxypg79LRjuxzJMTZUA+a3P5K09Z/bBnUhBHT0eucNNzjgq9gZm
l58FmQP1SNuYn1rVmYmMmIyveFOcoT+zFnb50yycxlc3zbZx9ofJM35PT5PLDm8E
FugIOxnPGZ/70UUgMk2zc0vfhxHwRlGGl9p6Bqkfxu04n4P5FnaSDsT9hiQZxLo2
h29ONJQUIUelaMcJ9MWGf+jAEgCigJ1aiuHp1zxDvaUCh3A0tdLy64+A+ozmzgLP
nubtmCzg6KU2Yh1rAgMBAAGgHzAdBgkqhkiG9w0BCQ4xEDAOMAwGA1UdEwQFMAMB
Af8wDQYJKoZIhvcNAQELBQADggEBACI5v8GbOXKv38h9/tuGEwJ9uxpYEljgGt8h
QL5lwfEifh/7A8b39b9JEzWk5hnMRCOb8J6Jc3/6nmVgtKkQ+Mceupqpwsp1gT/v
uUoAkJE03Iuja9zLhHmy74oZ7LWOQrZ1T7Z0eGQ+5u+LBZiPKnKxmkLCQoUPTbc4
NQ9BbKhr8OaoJ4DDvJnszcL7to6kih7SkdoNZsq4zB0/ai/cPhvoVgkYfbLH2++D
Tcs7TqU2L7gKzqXUtHeAKM2y81ewL7QTrcYzgiW86s3NmquxZG5pq0mjD+P4BYLc
MOdnCxKbBuE/1R29pa6+JKgc46jOa2yRgv5+8rXkkpu53Ke3FGc=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,17 @@
{
"CN": "ca.nghttp2.org",
"key": {
"algo": "rsa",
"size": 2048
},
"ca": {
"expiry": "87600h"
},
"names": [
{
"C": "AU",
"ST": "Some-State",
"O": "Internet Widgits Pty Ltd"
}
]
}

22
src/ca.nghttp2.org.pem Normal file
View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDrTCCApWgAwIBAgIUe4dvx8haIjsT3ZpNCMrl62Xk6E0wDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v
cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBeMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRcwFQYDVQQDEw5jYS5uZ2h0dHAyLm9yZzCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBANZFZBfEElMFvx1fRBUT0jKIjlZmMAfL6ig7U9He
jBYigDPP2CRVKtripVMnYE+CDxOzwE71XCI9xrS/V8z4bHKmDv0tGO7HMkxNlQD5
rc/krT1n9sGdSEEdPR65w03OOCr2BmaXnwWZA/VI25ifWtWZiYyYjK94U5yhP7MW
dvnTLJzGVzfNtnH2h8kzfk9Pk8sObwQW6Ag7Gc8Zn/vRRSAyTbNzS9+HEfBGUYaX
2noGqR/G7Tifg/kWdpIOxP2GJBnEujaHb040lBQhR6Voxwn0xYZ/6MASAKKAnVqK
4enXPEO9pQKHcDS10vLrj4D6jObOAs+e5u2YLODopTZiHWsCAwEAAaNjMGEwDgYD
VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNA5xVR1Zcax
RJL9VC6pzuLmvduGMB8GA1UdIwQYMBaAFNA5xVR1ZcaxRJL9VC6pzuLmvduGMA0G
CSqGSIb3DQEBCwUAA4IBAQCmdVfn/hUyEdvkKG7svg5d8o6BENOj8695KtWmzJjK
zxH8J5Vy3mn89XrHQ+BOYXCDPyhs0aDS8aq3Z+HY0n9z1oAicyGzlVwZQQNX3YId
Y2vcf7qu/2ATm/1S+mebE1/EXMUlWISKKUYXjggCwFgjDhH87Ai+A8MKScVdmqgL
Hf+fRSzH3ToW7BCXlRl5bPAq2g+v1ALYc8wU9cT1MYm4dqAXh870LGFyUpaSWmFr
TtX1DXBTgLp62syNlDthAvGigYFDtCa4cDM2vdTD9wpec2V9EKpfVqiRDDuYjUVX
UXl27MvkNWnEBKCIoNv5abWXpZVG2zQdEMmUOkVuAXUC
-----END CERTIFICATE-----

View File

@ -72,8 +72,8 @@ int main(int argc, char *argv[]) {
// add the tests to the suite // add the tests to the suite
if (!CU_add_test(pSuite, "ssl_create_lookup_tree", if (!CU_add_test(pSuite, "ssl_create_lookup_tree",
shrpx::test_shrpx_ssl_create_lookup_tree) || shrpx::test_shrpx_ssl_create_lookup_tree) ||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file", !CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_x509",
shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) || shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509) ||
!CU_add_test(pSuite, "ssl_tls_hostname_match", !CU_add_test(pSuite, "ssl_tls_hostname_match",
shrpx::test_shrpx_ssl_tls_hostname_match) || shrpx::test_shrpx_ssl_tls_hostname_match) ||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) || !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||

View File

@ -878,4 +878,8 @@ void ConnectionHandler::send_serial_event(SerialEvent ev) {
ev_async_send(loop_, &serial_event_asyncev_); ev_async_send(loop_, &serial_event_asyncev_);
} }
SSL_CTX *ConnectionHandler::get_ssl_ctx(size_t idx) const {
return all_ssl_ctx_[idx];
}
} // namespace shrpx } // namespace shrpx

View File

@ -146,6 +146,9 @@ public:
ev_timer *w); ev_timer *w);
void schedule_next_tls_ticket_key_memcached_get(ev_timer *w); void schedule_next_tls_ticket_key_memcached_get(ev_timer *w);
SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx(); SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx();
// Returns the SSL_CTX at all_ssl_ctx_[idx]. This does not perform
// array bound checking.
SSL_CTX *get_ssl_ctx(size_t idx) const;
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
void set_neverbleed(std::unique_ptr<neverbleed_t> nb); void set_neverbleed(std::unique_ptr<neverbleed_t> nb);

View File

@ -279,6 +279,22 @@ ssize_t Router::match(const StringRef &host, const StringRef &path) const {
return node->index; return node->index;
} }
ssize_t Router::match(const StringRef &s) const {
const RNode *node;
size_t offset;
node = match_complete(&offset, &root_, std::begin(s), std::end(s));
if (node == nullptr) {
return -1;
}
if (node->len != offset) {
return -1;
}
return node->index;
}
namespace { namespace {
const RNode *match_prefix(size_t *nread, const RNode *node, const char *first, const RNode *match_prefix(size_t *nread, const RNode *node, const char *first,
const char *last) { const char *last) {

View File

@ -67,6 +67,9 @@ public:
bool add_route(const StringRef &pattern, size_t index); bool add_route(const StringRef &pattern, size_t index);
// Returns the matched index of pattern. -1 if there is no match. // Returns the matched index of pattern. -1 if there is no match.
ssize_t match(const StringRef &host, const StringRef &path) const; ssize_t match(const StringRef &host, const StringRef &path) const;
// Returns the matched index of pattern |s|. -1 if there is no
// match.
ssize_t match(const StringRef &s) const;
// Returns the matched index of pattern if a pattern is a suffix of // Returns the matched index of pattern if a pattern is a suffix of
// |s|, otherwise -1. If |*last_node| is not nullptr, it specifies // |s|, otherwise -1. If |*last_node| is not nullptr, it specifies
// the first node to start matching. If it is nullptr, match will // the first node to start matching. If it is nullptr, match will

View File

@ -38,6 +38,8 @@
#include <string> #include <string>
#include <iomanip> #include <iomanip>
#include <iostream>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
@ -58,6 +60,7 @@
#include "shrpx_http2_session.h" #include "shrpx_http2_session.h"
#include "shrpx_memcached_request.h" #include "shrpx_memcached_request.h"
#include "shrpx_memcached_dispatcher.h" #include "shrpx_memcached_dispatcher.h"
#include "shrpx_connection_handler.h"
#include "util.h" #include "util.h"
#include "ssl.h" #include "ssl.h"
#include "template.h" #include "template.h"
@ -143,16 +146,39 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
auto handler = static_cast<ClientHandler *>(conn->data); auto handler = static_cast<ClientHandler *>(conn->data);
auto worker = handler->get_worker(); auto worker = handler->get_worker();
auto cert_tree = worker->get_cert_lookup_tree(); auto cert_tree = worker->get_cert_lookup_tree();
if (cert_tree) { if (!cert_tree) {
const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); return SSL_TLSEXT_ERR_OK;
if (hostname) {
auto len = strlen(hostname);
auto ssl_ctx = cert_tree->lookup(StringRef{hostname, len});
if (ssl_ctx) {
SSL_set_SSL_CTX(ssl, ssl_ctx);
}
}
} }
std::array<uint8_t, NI_MAXHOST> buf;
auto rawhost = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (rawhost == nullptr) {
return SSL_TLSEXT_ERR_OK;
}
auto len = strlen(rawhost);
// NI_MAXHOST includes terminal NULL.
if (len == 0 || len + 1 > buf.size()) {
return SSL_TLSEXT_ERR_OK;
}
auto end_buf = std::copy_n(rawhost, len, std::begin(buf));
util::inp_strlower(std::begin(buf), end_buf);
auto hostname = StringRef{std::begin(buf), end_buf};
auto idx = cert_tree->lookup(hostname);
if (idx == -1) {
return SSL_TLSEXT_ERR_OK;
}
auto conn_handler = worker->get_connection_handler();
auto ssl_ctx = conn_handler->get_ssl_ctx(idx);
SSL_set_SSL_CTX(ssl, ssl_ctx);
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
} }
} // namespace } // namespace
@ -1075,181 +1101,129 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) {
return check_cert(ssl, &addr->addr, hostname); return check_cert(ssl, &addr->addr, hostname);
} }
CertLookupTree::CertLookupTree() { CertLookupTree::CertLookupTree() {}
root_.ssl_ctx = nullptr;
root_.str = nullptr;
root_.first = root_.last = 0;
}
namespace { void CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
// The |offset| is the index in the hostname we are examining. We are std::array<uint8_t, NI_MAXHOST> buf;
// going to scan from |offset| in backwards.
void cert_lookup_tree_add_cert(CertNode *node, SSL_CTX *ssl_ctx, // NI_MAXHOST includes terminal NULL byte
const char *hostname, size_t len, int offset) { if (hostname.empty() || hostname.size() + 1 > buf.size()) {
int i, next_len = node->next.size(); return;
char c = hostname[offset];
CertNode *cn = nullptr;
for (i = 0; i < next_len; ++i) {
cn = node->next[i].get();
if (cn->str[cn->first] == c) {
break;
}
} }
if (i == next_len) {
if (c == '*') {
// We assume hostname as wildcard hostname when first '*' is
// encountered. Note that as per RFC 6125 (6.4.3), there are
// some restrictions for wildcard hostname. We just ignore
// these rules here but do the proper check when we do the
// match.
node->wildcard_certs.push_back({ssl_ctx, hostname, len});
return;
}
int j; auto wildcard_it = std::find(std::begin(hostname), std::end(hostname), '*');
auto new_node = make_unique<CertNode>(); if (wildcard_it != std::end(hostname) &&
new_node->str = hostname; wildcard_it + 1 != std::end(hostname)) {
new_node->first = offset; auto wildcard_prefix = StringRef{std::begin(hostname), wildcard_it};
// If wildcard is found, set the region before it because we auto wildcard_suffix = StringRef{wildcard_it + 1, std::end(hostname)};
// don't include it in [first, last).
for (j = offset; j >= 0 && hostname[j] != '*'; --j) auto rev_suffix = StringRef{std::begin(buf),
; std::reverse_copy(std::begin(wildcard_suffix),
new_node->last = j; std::end(wildcard_suffix),
if (j == -1) { std::begin(buf))};
new_node->ssl_ctx = ssl_ctx;
WildcardPattern *wpat;
if (!rev_wildcard_router_.add_route(rev_suffix,
wildcard_patterns_.size())) {
auto wcidx = rev_wildcard_router_.match(rev_suffix);
assert(wcidx != -1);
wpat = &wildcard_patterns_[wcidx];
} else { } else {
new_node->ssl_ctx = nullptr; wildcard_patterns_.emplace_back();
new_node->wildcard_certs.push_back({ssl_ctx, hostname, len}); wpat = &wildcard_patterns_.back();
} }
node->next.push_back(std::move(new_node));
auto rev_prefix = StringRef{std::begin(buf),
std::reverse_copy(std::begin(wildcard_prefix),
std::end(wildcard_prefix),
std::begin(buf))};
wpat->rev_prefix.emplace_back(rev_prefix, idx);
return; return;
} }
int j; router_.add_route(hostname, idx);
for (i = cn->first, j = offset; }
i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j)
; ssize_t CertLookupTree::lookup(const StringRef &hostname) {
if (i == cn->last) { std::array<uint8_t, NI_MAXHOST> buf;
if (j == -1) {
// If the same hostname already exists, we don't overwrite // NI_MAXHOST includes terminal NULL byte
// exiting ssl_ctx if (hostname.empty() || hostname.size() + 1 > buf.size()) {
if (!cn->ssl_ctx) { return -1;
cn->ssl_ctx = ssl_ctx; }
// Always prefer exact match
auto idx = router_.match(hostname);
if (idx != -1) {
return idx;
}
if (wildcard_patterns_.empty()) {
return -1;
}
ssize_t best_idx = -1;
size_t best_prefixlen = 0;
const RNode *last_node = nullptr;
auto rev_host = StringRef{
std::begin(buf), std::reverse_copy(std::begin(hostname),
std::end(hostname), std::begin(buf))};
for (;;) {
size_t nread = 0;
auto wcidx =
rev_wildcard_router_.match_prefix(&nread, &last_node, rev_host);
if (wcidx == -1) {
return best_idx;
}
// '*' must match at least one byte
if (nread == rev_host.size()) {
return best_idx;
}
rev_host = StringRef{std::begin(rev_host) + nread, std::end(rev_host)};
auto rev_prefix = StringRef{std::begin(rev_host) + 1, std::end(rev_host)};
auto &wpat = wildcard_patterns_[wcidx];
for (auto &wprefix : wpat.rev_prefix) {
if (!util::ends_with(rev_prefix, wprefix.prefix)) {
continue;
} }
return;
}
// The existing hostname is a suffix of this hostname. Continue auto prefixlen =
// matching at potion j. wprefix.prefix.size() +
cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); (reinterpret_cast<const uint8_t *>(&rev_host[0]) - &buf[0]);
return;
}
{ // Breaking a tie with longer suffix
auto new_node = make_unique<CertNode>(); if (prefixlen < best_prefixlen) {
new_node->ssl_ctx = cn->ssl_ctx; continue;
new_node->str = cn->str; }
new_node->first = i;
new_node->last = cn->last;
new_node->wildcard_certs.swap(cn->wildcard_certs);
new_node->next.swap(cn->next);
cn->next.push_back(std::move(new_node)); best_idx = wprefix.idx;
} best_prefixlen = prefixlen;
cn->last = i;
if (j == -1) {
// This hostname is a suffix of the existing hostname.
cn->ssl_ctx = ssl_ctx;
return;
}
// This hostname and existing one share suffix.
cn->ssl_ctx = nullptr;
cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j);
}
} // namespace
void CertLookupTree::add_cert(SSL_CTX *ssl_ctx, const StringRef &hostname) {
if (hostname.empty()) {
return;
}
// Copy hostname
auto host_copy = make_unique<char[]>(hostname.size() + 1);
std::copy(std::begin(hostname), std::end(hostname), host_copy.get());
host_copy[hostname.size()] = '\0';
util::inp_strlower(&host_copy[0], &host_copy[0] + hostname.size());
cert_lookup_tree_add_cert(&root_, ssl_ctx, host_copy.get(), hostname.size(),
hostname.size() - 1);
hosts_.push_back(std::move(host_copy));
}
namespace {
SSL_CTX *cert_lookup_tree_lookup(CertNode *node, const StringRef &hostname,
int offset) {
int i, j;
for (i = node->first, j = offset;
i > node->last && j >= 0 && node->str[i] == util::lowcase(hostname[j]);
--i, --j)
;
if (i != node->last) {
return nullptr;
}
if (j == -1) {
if (node->ssl_ctx) {
// exact match
return node->ssl_ctx;
}
// Do not perform wildcard-match because '*' must match at least
// one character.
return nullptr;
}
for (const auto &wildcert : node->wildcard_certs) {
if (tls_hostname_match(StringRef{wildcert.hostname, wildcert.hostnamelen},
hostname)) {
return wildcert.ssl_ctx;
} }
} }
auto c = util::lowcase(hostname[j]);
for (const auto &next_node : node->next) {
if (next_node->str[next_node->first] == c) {
return cert_lookup_tree_lookup(next_node.get(), hostname, j);
}
}
return nullptr;
}
} // namespace
SSL_CTX *CertLookupTree::lookup(const StringRef &hostname) {
if (hostname.empty()) {
return nullptr;
}
return cert_lookup_tree_lookup(&root_, hostname, hostname.size() - 1);
} }
int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, void CertLookupTree::dump() const {
const char *certfile) { std::cerr << "exact:" << std::endl;
auto bio = BIO_new(BIO_s_file()); router_.dump();
if (!bio) { std::cerr << "wildcard suffix (reversed):" << std::endl;
LOG(ERROR) << "BIO_new failed"; rev_wildcard_router_.dump();
return -1; }
}
auto bio_deleter = defer(BIO_vfree, bio); int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
if (!BIO_read_filename(bio, certfile)) { X509 *cert) {
LOG(ERROR) << "Could not read certificate file '" << certfile << "'"; std::array<uint8_t, NI_MAXHOST> buf;
return -1;
}
auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
if (!cert) {
LOG(ERROR) << "Could not read X509 structure from file '" << certfile
<< "'";
return -1;
}
auto cert_deleter = defer(X509_free, cert);
auto altnames = static_cast<GENERAL_NAMES *>( auto altnames = static_cast<GENERAL_NAMES *>(
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
@ -1285,7 +1259,15 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,
} }
dns_found = true; dns_found = true;
lt->add_cert(ssl_ctx, StringRef{name, static_cast<size_t>(len)});
if (static_cast<size_t>(len) + 1 > buf.size()) {
continue;
}
auto end_buf = std::copy_n(name, len, std::begin(buf));
util::inp_strlower(std::begin(buf), end_buf);
lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
} }
// Don't bother CN if we have dNSName. // Don't bother CN if we have dNSName.
@ -1309,10 +1291,14 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,
cn = StringRef{cn.c_str(), cn.size() - 1}; cn = StringRef{cn.c_str(), cn.size() - 1};
} }
lt->add_cert(ssl_ctx, cn); auto end_buf = std::copy(std::begin(cn), std::end(cn), std::begin(buf));
OPENSSL_free(const_cast<char *>(cn.c_str())); OPENSSL_free(const_cast<char *>(cn.c_str()));
util::inp_strlower(std::begin(buf), end_buf);
lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
return 0; return 0;
} }
@ -1365,6 +1351,13 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
return ssl_ctx; return ssl_ctx;
} }
if (ssl::cert_lookup_tree_add_cert_from_x509(
cert_tree, all_ssl_ctx.size() - 1,
SSL_CTX_get0_certificate(ssl_ctx)) == -1) {
LOG(FATAL) << "Failed to add default certificate.";
DIE();
}
for (auto &keycert : tlsconf.subcerts) { for (auto &keycert : tlsconf.subcerts) {
auto ssl_ctx = auto ssl_ctx =
ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str() ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str()
@ -1374,19 +1367,14 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); );
all_ssl_ctx.push_back(ssl_ctx); all_ssl_ctx.push_back(ssl_ctx);
if (ssl::cert_lookup_tree_add_cert_from_file( if (ssl::cert_lookup_tree_add_cert_from_x509(
cert_tree, ssl_ctx, keycert.second.c_str()) == -1) { cert_tree, all_ssl_ctx.size() - 1,
SSL_CTX_get0_certificate(ssl_ctx)) == -1) {
LOG(FATAL) << "Failed to add sub certificate."; LOG(FATAL) << "Failed to add sub certificate.";
DIE(); DIE();
} }
} }
if (ssl::cert_lookup_tree_add_cert_from_file(
cert_tree, ssl_ctx, tlsconf.cert_file.c_str()) == -1) {
LOG(FATAL) << "Failed to add default certificate.";
DIE();
}
return ssl_ctx; return ssl_ctx;
} }

View File

@ -40,6 +40,7 @@
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
#include "network.h" #include "network.h"
#include "shrpx_router.h"
namespace shrpx { namespace shrpx {
@ -103,73 +104,69 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr);
void get_altnames(X509 *cert, std::vector<std::string> &dns_names, void get_altnames(X509 *cert, std::vector<std::string> &dns_names,
std::vector<std::string> &ip_addrs, std::string &common_name); std::vector<std::string> &ip_addrs, std::string &common_name);
// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or struct WildcardRevPrefix {
// commonName matches hostname in query. The tree is patricia trie WildcardRevPrefix(const StringRef &prefix, size_t idx)
// data structure formed from the tail of the hostname pattern. Each : prefix(std::begin(prefix), std::end(prefix)), idx(idx) {}
// CertNode contains part of hostname str member in range [first,
// last) member and the next member contains the following CertNode
// pointers ('following' means character before the current one). The
// CertNode where a hostname pattern ends contains its SSL_CTX pointer
// in the ssl_ctx member. For wildcard hostname pattern, we store the
// its pattern and SSL_CTX in CertNode one before first "*" found from
// the tail.
//
// When querying SSL_CTX with particular hostname, we match from its
// tail in our lookup tree. If the query goes to the first character
// of the hostname and current CertNode has non-NULL ssl_ctx member,
// then it is the exact match. The ssl_ctx member is returned. Along
// the way, if CertNode which contains non-empty wildcard_certs member
// is encountered, wildcard hostname matching is performed against
// them. If there is a match, its SSL_CTX is returned. If none
// matches, query is continued to the next character.
struct WildcardCert { // "Prefix" of wildcard pattern. It is reversed from original form.
SSL_CTX *ssl_ctx; // For example, if the original wildcard is "test*.nghttp2.org",
const char *hostname; // prefix would be "tset".
size_t hostnamelen; ImmutableString prefix;
// The index of SSL_CTX. See ConnectionHandler::get_ssl_ctx().
size_t idx;
}; };
struct CertNode { struct WildcardPattern {
// list of wildcard domain name and its SSL_CTX pair, the wildcard // Wildcard host sharing only suffix is probably rare, so we just do
// '*' appears in this position. // linear search.
std::vector<WildcardCert> wildcard_certs; std::vector<WildcardRevPrefix> rev_prefix;
// Next CertNode index of CertLookupTree::nodes
std::vector<std::unique_ptr<CertNode>> next;
// SSL_CTX for exact match
SSL_CTX *ssl_ctx;
const char *str;
// [first, last) in the reverse direction in str, first >=
// last. This indices only work for str member.
int first, last;
}; };
class CertLookupTree { class CertLookupTree {
public: public:
CertLookupTree(); CertLookupTree();
// Adds |ssl_ctx| with hostname pattern |hostname| to the lookup // Adds hostname pattern |hostname| to the lookup tree, associating
// tree. // value |index|. When the queried host matches this pattern,
void add_cert(SSL_CTX *ssl_ctx, const StringRef &hostname); // |index| is returned. We support wildcard pattern. The left most
// '*' is considered as wildcard character, and it must match at
// least one character. If the same pattern has been already added,
// this function is noop.
//
// The caller should lower-case |hostname| since this function does
// do that, and lookup function performs case-sensitive match.
//
// TODO Treat wildcard pattern described as RFC 6125.
void add_cert(const StringRef &hostname, size_t index);
// Looks up SSL_CTX using the given |hostname|. If more than one // Looks up index using the given |hostname|. The exact match takes
// SSL_CTX which matches the query, it is undefined which one is // precedence over wildcard match. For wildcard match, longest
// returned. The |hostname| must be NULL-terminated. If no // match (sum of matched suffix and prefix length in bytes) is
// matching SSL_CTX found, returns NULL. // preferred, breaking a tie with longer suffix.
SSL_CTX *lookup(const StringRef &hostname); //
// The caller should lower-case |hostname| since this function
// performs case-sensitive match.
ssize_t lookup(const StringRef &hostname);
// Dumps the contents of this lookup tree to stderr.
void dump() const;
private: private:
CertNode root_; // Exact match
// Stores pointers to copied hostname when adding hostname and Router router_;
// ssl_ctx pair. // Wildcard reversed suffix match. The returned index is into
std::vector<std::unique_ptr<char[]>> hosts_; // wildcard_patterns_.
Router rev_wildcard_router_;
// Stores wildcard suffix patterns.
std::vector<WildcardPattern> wildcard_patterns_;
}; };
// Adds |ssl_ctx| to lookup tree |lt| using hostnames read from // Adds hostnames in |cert| to lookup tree |lt|. The subjectAltNames
// |certfile|. The subjectAltNames and commonName are considered as // and commonName are considered as eligible hostname. If there is at
// eligible hostname. This function returns 0 if it succeeds, or -1. // least one dNSName in subjectAltNames, commonName is not considered.
// Even if no ssl_ctx is added to tree, this function returns 0. // This function returns 0 if it succeeds, or -1.
int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
const char *certfile); X509 *cert);
// Returns true if |proto| is included in the // Returns true if |proto| is included in the
// protocol list |protos|. // protocol list |protos|.

View File

@ -36,83 +36,133 @@ namespace shrpx {
void test_shrpx_ssl_create_lookup_tree(void) { void test_shrpx_ssl_create_lookup_tree(void) {
auto tree = make_unique<ssl::CertLookupTree>(); auto tree = make_unique<ssl::CertLookupTree>();
SSL_CTX *ctxs[] = {
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())};
constexpr StringRef hostnames[] = { constexpr StringRef hostnames[] = {
StringRef::from_lit("example.com"), StringRef::from_lit("example.com"), // 0
StringRef::from_lit("www.example.org"), StringRef::from_lit("www.example.org"), // 1
StringRef::from_lit("*www.example.org"), StringRef::from_lit("*www.example.org"), // 2
StringRef::from_lit("x*.host.domain"), StringRef::from_lit("xy*.host.domain"), // 3
StringRef::from_lit("*yy.host.domain"), StringRef::from_lit("*yy.host.domain"), // 4
StringRef::from_lit("nghttp2.sourceforge.net"), StringRef::from_lit("nghttp2.sourceforge.net"), // 5
StringRef::from_lit("sourceforge.net"), StringRef::from_lit("sourceforge.net"), // 6
StringRef::from_lit("sourceforge.net"), // duplicate StringRef::from_lit("sourceforge.net"), // 7, duplicate
StringRef::from_lit("*.foo.bar"), // oo.bar is suffix of *.foo.bar StringRef::from_lit("*.foo.bar"), // 8, oo.bar is suffix of *.foo.bar
StringRef::from_lit("oo.bar")}; StringRef::from_lit("oo.bar") // 9
auto num = array_size(ctxs); };
for (size_t i = 0; i < num; ++i) { auto num = array_size(hostnames);
tree->add_cert(ctxs[i], hostnames[i]);
for (size_t idx = 0; idx < num; ++idx) {
tree->add_cert(hostnames[idx], idx);
} }
CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0])); tree->dump();
CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1]));
CU_ASSERT(ctxs[2] == tree->lookup(StringRef::from_lit("2www.example.org"))); CU_ASSERT(0 == tree->lookup(hostnames[0]));
CU_ASSERT(nullptr == tree->lookup(StringRef::from_lit("www2.example.org"))); CU_ASSERT(1 == tree->lookup(hostnames[1]));
CU_ASSERT(ctxs[3] == tree->lookup(StringRef::from_lit("x1.host.domain"))); CU_ASSERT(2 == tree->lookup(StringRef::from_lit("2www.example.org")));
CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("www2.example.org")));
CU_ASSERT(3 == tree->lookup(StringRef::from_lit("xy1.host.domain")));
// Does not match *yy.host.domain, because * must match at least 1 // Does not match *yy.host.domain, because * must match at least 1
// character. // character.
CU_ASSERT(nullptr == tree->lookup(StringRef::from_lit("yy.Host.domain"))); CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("yy.host.domain")));
CU_ASSERT(ctxs[4] == tree->lookup(StringRef::from_lit("zyy.host.domain"))); CU_ASSERT(4 == tree->lookup(StringRef::from_lit("xyy.host.domain")));
CU_ASSERT(nullptr == tree->lookup(StringRef{})); CU_ASSERT(-1 == tree->lookup(StringRef{}));
CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5])); CU_ASSERT(5 == tree->lookup(hostnames[5]));
CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6])); CU_ASSERT(6 == tree->lookup(hostnames[6]));
constexpr char h6[] = "pdylay.sourceforge.net"; constexpr char h6[] = "pdylay.sourceforge.net";
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
CU_ASSERT(0 == tree->lookup(StringRef{h6 + i, str_size(h6) - i})); CU_ASSERT(-1 == tree->lookup(StringRef{h6 + i, str_size(h6) - i}));
} }
CU_ASSERT(ctxs[8] == tree->lookup(StringRef::from_lit("x.foo.bar"))); CU_ASSERT(8 == tree->lookup(StringRef::from_lit("x.foo.bar")));
CU_ASSERT(ctxs[9] == tree->lookup(hostnames[9])); CU_ASSERT(9 == tree->lookup(hostnames[9]));
for (size_t i = 0; i < num; ++i) {
SSL_CTX_free(ctxs[i]);
}
SSL_CTX *ctxs2[] = {
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())};
constexpr StringRef names[] = { constexpr StringRef names[] = {
StringRef::from_lit("rab"), StringRef::from_lit("zab"), StringRef::from_lit("rab"), // 1
StringRef::from_lit("zzub"), StringRef::from_lit("ab")}; StringRef::from_lit("zab"), // 2
num = array_size(ctxs2); StringRef::from_lit("zzub"), // 3
StringRef::from_lit("ab") // 4
};
num = array_size(names);
tree = make_unique<ssl::CertLookupTree>(); tree = make_unique<ssl::CertLookupTree>();
for (size_t i = 0; i < num; ++i) { for (size_t idx = 0; idx < num; ++idx) {
tree->add_cert(ctxs2[i], names[i]); tree->add_cert(names[idx], idx);
} }
for (size_t i = 0; i < num; ++i) { for (size_t i = 0; i < num; ++i) {
CU_ASSERT(ctxs2[i] == tree->lookup(names[i])); CU_ASSERT(i == tree->lookup(names[i]));
}
for (size_t i = 0; i < num; ++i) {
SSL_CTX_free(ctxs2[i]);
} }
} }
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) { namespace {
int rv; X509 *load_certificate(const char *filename) {
ssl::CertLookupTree tree; auto bio = BIO_new(BIO_s_file());
auto ssl_ctx = SSL_CTX_new(SSLv23_method()); if (!bio) {
const char certfile[] = NGHTTP2_TESTS_DIR "/testdata/cacert.pem"; fprintf(stderr, "BIO_new() failed\n");
rv = ssl::cert_lookup_tree_add_cert_from_file(&tree, ssl_ctx, certfile); return nullptr;
CU_ASSERT(0 == rv); }
CU_ASSERT(ssl_ctx == tree.lookup(StringRef::from_lit("localhost"))); auto bio_deleter = defer(BIO_vfree, bio);
if (!BIO_read_filename(bio, filename)) {
fprintf(stderr, "Could not read certificate file '%s'\n", filename);
return nullptr;
}
auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
if (!cert) {
fprintf(stderr, "Could not read X509 structure from file '%s'\n", filename);
return nullptr;
}
SSL_CTX_free(ssl_ctx); return cert;
}
} // namespace
// We use cfssl to generate key pairs.
//
// CA self-signed key pairs generation:
//
// $ cfssl genkey -initca ca.nghttp2.org.csr.json | \
// cfssljson -bare ca.nghttp2.org
//
// Create CSR:
//
// $ cfssl genkey test.nghttp2.org.csr.json | cfssljson -bare test.nghttp2.org
// $ cfssl genkey test.example.com.csr.json | cfssljson -bare test.example.com
//
// Sign CSR:
//
// $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem \
// -config=ca-config.json -profile=server test.nghttp2.org.csr | \
// cfssljson -bare test.nghttp2.org
//
// $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem \
// -config=ca-config.json -profile=server test.example.com.csr | \
// cfssljson -bare test.example.com
//
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509(void) {
int rv;
constexpr char nghttp2_certfile[] = NGHTTP2_SRC_DIR "/test.nghttp2.org.pem";
auto nghttp2_cert = load_certificate(nghttp2_certfile);
auto nghttp2_cert_deleter = defer(X509_free, nghttp2_cert);
constexpr char examples_certfile[] = NGHTTP2_SRC_DIR "/test.example.com.pem";
auto examples_cert = load_certificate(examples_certfile);
auto examples_cert_deleter = defer(X509_free, examples_cert);
ssl::CertLookupTree tree;
rv = ssl::cert_lookup_tree_add_cert_from_x509(&tree, 0, nghttp2_cert);
CU_ASSERT(0 == rv);
rv = ssl::cert_lookup_tree_add_cert_from_x509(&tree, 1, examples_cert);
CU_ASSERT(0 == rv);
CU_ASSERT(-1 == tree.lookup(StringRef::from_lit("not-used.nghttp2.org")));
CU_ASSERT(0 == tree.lookup(StringRef::from_lit("test.nghttp2.org")));
CU_ASSERT(0 == tree.lookup(StringRef::from_lit("w.test.nghttp2.org")));
CU_ASSERT(0 == tree.lookup(StringRef::from_lit("www.test.nghttp2.org")));
CU_ASSERT(1 == tree.lookup(StringRef::from_lit("test.example.com")));
} }
template <size_t N, size_t M> template <size_t N, size_t M>

View File

@ -32,7 +32,7 @@
namespace shrpx { namespace shrpx {
void test_shrpx_ssl_create_lookup_tree(void); void test_shrpx_ssl_create_lookup_tree(void);
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void); void test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509(void);
void test_shrpx_ssl_tls_hostname_match(void); void test_shrpx_ssl_tls_hostname_match(void);
} // namespace shrpx } // namespace shrpx

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2FroLDwqVUYRlxp
U/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpowUxvTDv/GNIHH
XK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmIsYsO0Y4iBtwB
M/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvcVzvSgZIzMLqS
zvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7G/ZbX/4kUU5b
PabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABAoIBAQCjUL69iFOs7muK
CZGFe/iU1uxQM6XuGPN7mso3z15W07UxdlS3o6ZUSoLTONWcBxP/N4knulHJjZSY
0LivbDYz3htic3t0kdGmxOxPVnLKRXN6ncbQTaeAE8tlBMAcWd/UH2Tlylz+Ac//
6cV3gNJMShwUmhb3l4v3Rml0nZ6PO1pFc/Chk5L9REAV8G6rNtc9bzgmgoFucRO/
8ce/uJrENt1Pu3vBvmz42DTGfG48v5RZ0OY4qEPawZJ7p+QYiTf6h3Eilss/AllW
PPfQ0thdyB+yrZ3p6qb+ZUYphpGxgg6YlQxLfDKAikuo+EXwjPBPfeHhTO4kAj+h
opDCroZhAoGBANyVFbagCWqwguE6nVPmnCaiNQUIh8b7L2CnkkLfdbPQr/KvyIjg
Ua125bTJhe9Uk+ZBWsobQkjA0Baidiylx51pWYaxPVn5araVmkh2dqMluk2QE82X
AWemBgKhAqCLLLMVXbrRYlxpKUm1Fc/lJ8Ig2R/MJSntTMpQhJtIejUbAoGBAMnt
XMvlFABCoFbI9GMcteI/KkvNGQUy3OKEln/QCssnE4/XIu7LCxy6P+1lycbFy/mQ
0bnp525sPEIIkMpi6LeAbSzYN2O3BRjNrjPcbx6Khz9DweNhRIo5qTFRszZ+pHbV
N+9Oc9JVenwPw6EuW7uZRFKFhCHtsBFdUrWLJoSVAoGAQ3ytdwGBwA2fDW/UgL32
mm9YT2DrwbpKJYU/X4xkw44ett6HOTGAa9ULtINPogi7c2AdeeZbIk0znSk5hLF3
4DZCOM5zWdrQhmpBGNh9ta6uUFq7ZFRGDsMh5Z4DYsER/PyVf7neIS3ffviTYtbW
kjNgmrTnzesXanK2D5heI28CgYEAhl+qjRTYhoPP53C7EOmeL/0QzHij2c3LKAJL
lKqBREewwNvNp1L/BhL7T6OY7unZny48IpgBJn5oaxkAIW5IpzSTcnBAC99TSPo2
ntRmLdDJx9PzRrkHv2Q3r1ZLCEymbV3eZyWx9ZpkdAKZkL0k1mZcDP5Eu79Ml4Ge
9Kiw7TECgYEAh+nTKwrCUFGbe4RIVCj/QG7FVPbq5PdxJ3gILZ3/1XkhPcNRFKJS
u5qPfA02tYEALz9KXATK1uRB/GlBM7Eap/g2GFiHpVxrw6wPpybLywJmNyNTwqiq
eJxQ0FRzW9Kwwn1ThPY38LdFe/wvXZFOcNvGD8hHCLQRdlBR4zuTsBk=
-----END RSA PRIVATE KEY-----

17
src/test.example.com.csr Normal file
View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAxMQdGVz
dC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK39
lAbBIf8cHf+FmX1RX+bDMlnJ7P9YnRdha6Cw8KlVGEZcaVPym6yBvF/L98OHFT/8
Tl3dcvUPlvTiytmEnAHh3hPe5h2V23KaMFMb0w7/xjSBx1yv8r2DPEdhBJ2Ue9al
3aF7AmpL/2htnsJJLwNdPHhG5NLu0cTZiLGLDtGOIgbcATPzEpAGD31na3R4WPc9
JeDPBgMlDrmXNLeo9M8N385I6plYBhCb3Fc70oGSMzC6ks7wvPb4l7Y9acs0IzC4
Jrg27NJeO7kR6WV4DBlchd1LkIqT874Tuxv2W1/+JFFOWz2m3bbz2447u3ROT3xq
yjk4TOh0vd2O0L2E1LcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBMAUqwty7R
/YWRrC8NuvrbSsW0r7Z7FXWxny5w5ImONCgVffc2wVydtBVQ0rfd3pDZLyu0P4sx
4bJ/KBz67t2MsOKbCMDS7SJuFwHu9AUzaYNh455HeBOVwb6LemJDNnCtMG9DgcRv
2BpwKqekUVTGDuUQmLibjE8qwDHw/p9k4gjQBxlfJe2sIZGs6oA/JGFJUU6ZIn8Y
M6aazrbjWexbWCnjhiXkNa8kfKiSHzU+2ct+GY5QxI221+63bXRiAi2/LK0gaY+p
+3vYu75F7+8oPZOfsGmYEyPz7c1jPqcwPgVDk+sdvl1MO1TGFRaFNIlRP1DhpHkj
fuJ/id6oUHhj
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,14 @@
{
"CN": "test.example.com",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "AU",
"ST": "Some-State",
"O": "Internet Widgits Pty Ltd"
}
]
}

23
src/test.example.com.pem Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDwTCCAqmgAwIBAgIUDhKNhGRUq1TSHD6aG2k4TRR8iA0wDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v
cmcwHhcNMTYwNjI1MDkzNzAwWhcNMjYwNjIzMDkzNzAwWjBgMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRkwFwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2Fr
oLDwqVUYRlxpU/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpow
UxvTDv/GNIHHXK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmI
sYsO0Y4iBtwBM/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvc
VzvSgZIzMLqSzvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7
G/ZbX/4kUU5bPabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABo3UwczAO
BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw
ADAdBgNVHQ4EFgQUm8jn1FICope9qUce6ORQ0CtbmhYwHwYDVR0jBBgwFoAU0DnF
VHVlxrFEkv1ULqnO4ua924YwDQYJKoZIhvcNAQELBQADggEBAD7RPz/5rAnS1MNP
JfAj1TXZSBwlYgtmJL65yaFB6a1SNSTo15deAm/1Vl10LbmYdV4sVnGKeZjhKNk+
bvVzetUSUS7Rh1fHtxlivJFkG1VrvPu9b416l2aKftBiaNyAWXbyjqXwLYli6Ehk
uu6jZd0040Ggh7bY+KMSnDFDrp7Rar7OvGu9Iovs+sPdkc/iEbvwEiXdMjf3gwkT
Wqx6br1VDLzhD83HAsFA9tt5fv6KTf91UgJnCmOi81Uo6fSEJG84g32T25gwwmCK
q4U049aGF/f4u3QuWDsfYqNePycurAg3m5PC0wCoqvpY2u/q+PGbjWMi2PfZsF8U
imgl/L0=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA7p6KKa3ctS+Sr/nf2uTKNtTshuDVzTsBTbaGydj8q0YDmT3n
CnOPWXvvG1N+jJv5pcAXN2ZnV9UpGh3N5g/CaRcFTgQQ8o+NlCXYBdPIXAJ+Kkbx
limDw3n9xIXfeL6+V2QuPNrqh6n23xwDg5boKaNkpf7X5OrjT1Ph57SEfX1op3GX
bwkAP2+3WlxxYYs0htRq2gH97q9J4MlhHPDapi+uKGs+2b1y6Uxgf4nD5jEWdPmy
VqeKs+fT4ja2n+3gujpdOo2lg504p50gL4zP8zhAlcqlQCmeJGL1xFzCtm2wHQo7
6XHSWca4pJ7rxf2oIdtE7ikvgFlTVXnG1T3TEQIDAQABAoIBAQDlX/UD96MPcDmb
e6EZ85AGgUsUpJAhBjVMlMagxTqtEVJoPj8XptoHdMD2DZ66XzztfedTU9bHcZpf
BoNkQYXqKzzoL7Ry1leML4ymnVweRi8tSKD2bdXBVEUCYoXctc6WhzCDQxTrcBBl
i7I9DhUB4ZTglEbIQJpdKQ8hAj/Rt55KWSxc+8X7ItSdtMrq+uz+pqg4PkysVAFS
3aDybOqiI/2hzOvwQU4HaB48uUQwpOU6EGidt0C5nAdWOQMbS8kkCJz6UODiUfdM
mLIyA4ygkQ45QthzrddKauUMhUd/y1SAFJOambR4ZiyA+bItomlbNq018sFx3FDr
Uvg7nz2ZAoGBAPC6aO4W0U7vsWyL/lgC7ybFbtPJ4emj4wK/W87qx3LW1/dRgl7Q
h6oblZTFK/oV6xA7J2/Foocz/s1ntnyIdxrtrZUYAIiBXlrWhDg9+MnnmErfXx7H
CkRyWH6i9JbTRUeiWRNBGQ9yMkwQPc2Ckytxrh7w9M+RVCpyzUh8lX+XAoGBAP3B
4V8cF3bVEUOk0tHshR5m2kcJ22qmUv8WUG+IdRUDb4tRpzVFC8HcKedEjk3jxkXR
UInRSD+hLhx0HIhjZKWqJffSZI/G3U8AqoKu9+eh/xHZCah/8KW1YWNsn4rROcyZ
5XFRiMn7psWTjLEZ17zQS4rk9g65SKc9u1wtTxeXAoGAIY3qOF2n2Tfh5D5zOnNW
QHI+q3i1a6qzZtujgWkKWgCGY+vRn0Oz1Us5A16kbZyGgmGscpD6wZvGxXzSW/Nt
nqxIiMKquFxH+aNzFJ/WwNXuTWlrSc/2p2nE2gn+y9MxEfYYMm3df2CskBuncbDk
sKaM3bU6eoBIWg5cfOEYuYsCgYACB2bR59uYK6PzsoGtBAMcdx4Pq1iBxcqsF3WV
LrYg8OIXbxOzLVYmuqfrHXU10jhnnoDSWUYGnDdOKu9/d6v6Vx3umVQMgj6Kvyqd
2OBKjdUIQ3/8ROmbqZOZw+iSp5GavTBEc65wTv7KXZ+mWtqKu++esK32+CxIignR
dttHCQKBgQDZUt94wj9s5p7H5LH6hxyqNr6P9JYEuYPao5l/3mOJ8N330wKuN2G+
GUg7p/AhtQHwdoyErlsQavKZa791oCvfJiOURYa8gYU03sYsyI1tV45UexCwl40f
oS+VQYgU16UdYo9B2petecEPNpM+mgpne3qzVUwJ5NUNURgmWpyiQw==
-----END RSA PRIVATE KEY-----

19
src/test.nghttp2.org.csr Normal file
View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIDATCCAekCAQAwZDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEdMBsGA1UEAxMUbm90
LXVzZWQubmdodHRwMi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDunooprdy1L5Kv+d/a5Mo21OyG4NXNOwFNtobJ2PyrRgOZPecKc49Ze+8bU36M
m/mlwBc3ZmdX1SkaHc3mD8JpFwVOBBDyj42UJdgF08hcAn4qRvGWKYPDef3Ehd94
vr5XZC482uqHqfbfHAODlugpo2Sl/tfk6uNPU+HntIR9fWincZdvCQA/b7daXHFh
izSG1GraAf3ur0ngyWEc8NqmL64oaz7ZvXLpTGB/icPmMRZ0+bJWp4qz59PiNraf
7eC6Ol06jaWDnTinnSAvjM/zOECVyqVAKZ4kYvXEXMK2bbAdCjvpcdJZxriknuvF
/agh20TuKS+AWVNVecbVPdMRAgMBAAGgWDBWBgkqhkiG9w0BCQ4xSTBHMEUGA1Ud
EQQ+MDyCEFRFU1QuTkdIVFRQMi5PUkeCEioudGVzdC5uZ2h0dHAyLm9yZ4IUdyp3
LnRlc3QubmdodHRwMi5vcmcwDQYJKoZIhvcNAQELBQADggEBAIAEwnoM5moRwO5U
eaeVCuzpxw1qQsB769GyQu+ey1aa+2BYflirv/FW+8x/uzQpCWGEgHqd5w+MXyXA
PsyucHgKh5Ia6MUW6xxlHkkOtVtmZiH7lXWv90RNtdfHHGWnBzw8iGsk5WfEaNho
NlPiuYLiFqA7W6jR/c4kOg3zziDlwTXaH6SWLCuDzLTb7E7nGcrWkN6moYj+QlSx
viA4GsqDBoFgXT7cSfUzS8ZwIjrqbx7C1xkzPEt5jAiCD/UBX9ot0G+lEgCv3UQj
Q1KkY+TO3bzMkt/kQSX2Q6plKj8D77tlDfFCjd77VC2lL3Qmzaz+M6T7uF+wyl9W
AQJvoUg=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,19 @@
{
"CN": "not-used.nghttp2.org",
"hosts": [
"TEST.NGHTTP2.ORG",
"*.test.nghttp2.org",
"w*w.test.nghttp2.org"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "AU",
"ST": "Some-State",
"O": "Internet Widgits Pty Ltd"
}
]
}

24
src/test.nghttp2.org.pem Normal file
View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEDjCCAvagAwIBAgIUQBCY8Nre85JT1c7P+HbXUF9yzg8wDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v
cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBkMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMR0wGwYDVQQDExRub3QtdXNlZC5uZ2h0dHAyLm9yZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6eiimt3LUvkq/539rkyjbU7Ibg1c07
AU22hsnY/KtGA5k95wpzj1l77xtTfoyb+aXAFzdmZ1fVKRodzeYPwmkXBU4EEPKP
jZQl2AXTyFwCfipG8ZYpg8N5/cSF33i+vldkLjza6oep9t8cA4OW6CmjZKX+1+Tq
409T4ee0hH19aKdxl28JAD9vt1pccWGLNIbUatoB/e6vSeDJYRzw2qYvrihrPtm9
culMYH+Jw+YxFnT5slanirPn0+I2tp/t4Lo6XTqNpYOdOKedIC+Mz/M4QJXKpUAp
niRi9cRcwrZtsB0KO+lx0lnGuKSe68X9qCHbRO4pL4BZU1V5xtU90xECAwEAAaOB
vTCBujAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0T
AQH/BAIwADAdBgNVHQ4EFgQUGlxgxowH6jrQiyyFpCbwPkCXXIYwHwYDVR0jBBgw
FoAU0DnFVHVlxrFEkv1ULqnO4ua924YwRQYDVR0RBD4wPIIQVEVTVC5OR0hUVFAy
Lk9SR4ISKi50ZXN0Lm5naHR0cDIub3JnghR3KncudGVzdC5uZ2h0dHAyLm9yZzAN
BgkqhkiG9w0BAQsFAAOCAQEANCqM6ocfqOpgDEHYOOQTGFHJIptQhS3kRYAdTIo2
G8XvGCoy+CDYe1GAUWbxE090+a1I1rsYMHcWKJnjKaCBZid7KMhyayIvrmgEsOCh
L8iLf3bxkCoyIAmCpxJwa3LMxm2QQLtRx8AoMXWf+N8are4HY6MLNn6aP4zaTrTZ
H+WkjKIh7WjSHtW/ro666PCXJDCCdRXljOf8v/fff3bYiLg8o70RBp7OFM0HaPtK
wCfcLLxBeoVIncWswB6GtVUFhLeGjepDzWpuDHOdw6DtpghwSXvWFu9bRtl+x02m
LAGfJ0kJrpYGfr9UB51NFX3aM/D3p2zxrjKwR2b59vJEcA==
-----END CERTIFICATE-----