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_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})
if(HAVE_MRUBY)

View File

@ -22,7 +22,10 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = includes
EXTRA_DIST = CMakeLists.txt
EXTRA_DIST = \
CMakeLists.txt \
test.example.com.pem \
test.nghttp2.org.pem
bin_PROGRAMS =
check_PROGRAMS =
@ -187,7 +190,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
template_test.cc template_test.h \
base64_test.cc base64_test.h
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@
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
if (!CU_add_test(pSuite, "ssl_create_lookup_tree",
shrpx::test_shrpx_ssl_create_lookup_tree) ||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file",
shrpx::test_shrpx_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_x509) ||
!CU_add_test(pSuite, "ssl_tls_hostname_match",
shrpx::test_shrpx_ssl_tls_hostname_match) ||
!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_);
}
SSL_CTX *ConnectionHandler::get_ssl_ctx(size_t idx) const {
return all_ssl_ctx_[idx];
}
} // namespace shrpx

View File

@ -146,6 +146,9 @@ public:
ev_timer *w);
void schedule_next_tls_ticket_key_memcached_get(ev_timer *w);
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
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;
}
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 {
const RNode *match_prefix(size_t *nread, const RNode *node, const char *first,
const char *last) {

View File

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

View File

@ -38,6 +38,8 @@
#include <string>
#include <iomanip>
#include <iostream>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
@ -58,6 +60,7 @@
#include "shrpx_http2_session.h"
#include "shrpx_memcached_request.h"
#include "shrpx_memcached_dispatcher.h"
#include "shrpx_connection_handler.h"
#include "util.h"
#include "ssl.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 worker = handler->get_worker();
auto cert_tree = worker->get_cert_lookup_tree();
if (cert_tree) {
const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (hostname) {
auto len = strlen(hostname);
auto ssl_ctx = cert_tree->lookup(StringRef{hostname, len});
if (ssl_ctx) {
if (!cert_tree) {
return SSL_TLSEXT_ERR_OK;
}
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;
}
} // namespace
@ -1075,181 +1101,129 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) {
return check_cert(ssl, &addr->addr, hostname);
}
CertLookupTree::CertLookupTree() {
root_.ssl_ctx = nullptr;
root_.str = nullptr;
root_.first = root_.last = 0;
}
CertLookupTree::CertLookupTree() {}
namespace {
// The |offset| is the index in the hostname we are examining. We are
// going to scan from |offset| in backwards.
void cert_lookup_tree_add_cert(CertNode *node, SSL_CTX *ssl_ctx,
const char *hostname, size_t len, int offset) {
int i, next_len = node->next.size();
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});
void CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
std::array<uint8_t, NI_MAXHOST> buf;
// NI_MAXHOST includes terminal NULL byte
if (hostname.empty() || hostname.size() + 1 > buf.size()) {
return;
}
int j;
auto new_node = make_unique<CertNode>();
new_node->str = hostname;
new_node->first = offset;
// If wildcard is found, set the region before it because we
// don't include it in [first, last).
for (j = offset; j >= 0 && hostname[j] != '*'; --j)
;
new_node->last = j;
if (j == -1) {
new_node->ssl_ctx = ssl_ctx;
auto wildcard_it = std::find(std::begin(hostname), std::end(hostname), '*');
if (wildcard_it != std::end(hostname) &&
wildcard_it + 1 != std::end(hostname)) {
auto wildcard_prefix = StringRef{std::begin(hostname), wildcard_it};
auto wildcard_suffix = StringRef{wildcard_it + 1, std::end(hostname)};
auto rev_suffix = StringRef{std::begin(buf),
std::reverse_copy(std::begin(wildcard_suffix),
std::end(wildcard_suffix),
std::begin(buf))};
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 {
new_node->ssl_ctx = nullptr;
new_node->wildcard_certs.push_back({ssl_ctx, hostname, len});
wildcard_patterns_.emplace_back();
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;
}
int j;
for (i = cn->first, j = offset;
i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j)
;
if (i == cn->last) {
if (j == -1) {
// If the same hostname already exists, we don't overwrite
// exiting ssl_ctx
if (!cn->ssl_ctx) {
cn->ssl_ctx = ssl_ctx;
}
return;
router_.add_route(hostname, idx);
}
// The existing hostname is a suffix of this hostname. Continue
// matching at potion j.
cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j);
return;
}
ssize_t CertLookupTree::lookup(const StringRef &hostname) {
std::array<uint8_t, NI_MAXHOST> buf;
{
auto new_node = make_unique<CertNode>();
new_node->ssl_ctx = cn->ssl_ctx;
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));
}
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,
const char *certfile) {
auto bio = BIO_new(BIO_s_file());
if (!bio) {
LOG(ERROR) << "BIO_new failed";
// NI_MAXHOST includes terminal NULL byte
if (hostname.empty() || hostname.size() + 1 > buf.size()) {
return -1;
}
auto bio_deleter = defer(BIO_vfree, bio);
if (!BIO_read_filename(bio, certfile)) {
LOG(ERROR) << "Could not read certificate file '" << certfile << "'";
// Always prefer exact match
auto idx = router_.match(hostname);
if (idx != -1) {
return idx;
}
if (wildcard_patterns_.empty()) {
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;
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;
}
auto cert_deleter = defer(X509_free, cert);
// '*' 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;
}
auto prefixlen =
wprefix.prefix.size() +
(reinterpret_cast<const uint8_t *>(&rev_host[0]) - &buf[0]);
// Breaking a tie with longer suffix
if (prefixlen < best_prefixlen) {
continue;
}
best_idx = wprefix.idx;
best_prefixlen = prefixlen;
}
}
}
void CertLookupTree::dump() const {
std::cerr << "exact:" << std::endl;
router_.dump();
std::cerr << "wildcard suffix (reversed):" << std::endl;
rev_wildcard_router_.dump();
}
int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
X509 *cert) {
std::array<uint8_t, NI_MAXHOST> buf;
auto altnames = static_cast<GENERAL_NAMES *>(
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;
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.
@ -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};
}
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()));
util::inp_strlower(std::begin(buf), end_buf);
lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
return 0;
}
@ -1365,6 +1351,13 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_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) {
auto ssl_ctx =
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
);
all_ssl_ctx.push_back(ssl_ctx);
if (ssl::cert_lookup_tree_add_cert_from_file(
cert_tree, ssl_ctx, keycert.second.c_str()) == -1) {
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 sub certificate.";
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;
}

View File

@ -40,6 +40,7 @@
#endif // HAVE_NEVERBLEED
#include "network.h"
#include "shrpx_router.h"
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,
std::vector<std::string> &ip_addrs, std::string &common_name);
// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or
// commonName matches hostname in query. The tree is patricia trie
// data structure formed from the tail of the hostname pattern. Each
// 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 WildcardRevPrefix {
WildcardRevPrefix(const StringRef &prefix, size_t idx)
: prefix(std::begin(prefix), std::end(prefix)), idx(idx) {}
struct WildcardCert {
SSL_CTX *ssl_ctx;
const char *hostname;
size_t hostnamelen;
// "Prefix" of wildcard pattern. It is reversed from original form.
// For example, if the original wildcard is "test*.nghttp2.org",
// prefix would be "tset".
ImmutableString prefix;
// The index of SSL_CTX. See ConnectionHandler::get_ssl_ctx().
size_t idx;
};
struct CertNode {
// list of wildcard domain name and its SSL_CTX pair, the wildcard
// '*' appears in this position.
std::vector<WildcardCert> wildcard_certs;
// 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;
struct WildcardPattern {
// Wildcard host sharing only suffix is probably rare, so we just do
// linear search.
std::vector<WildcardRevPrefix> rev_prefix;
};
class CertLookupTree {
public:
CertLookupTree();
// Adds |ssl_ctx| with hostname pattern |hostname| to the lookup
// tree.
void add_cert(SSL_CTX *ssl_ctx, const StringRef &hostname);
// Adds hostname pattern |hostname| to the lookup tree, associating
// value |index|. When the queried host matches this pattern,
// |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
// SSL_CTX which matches the query, it is undefined which one is
// returned. The |hostname| must be NULL-terminated. If no
// matching SSL_CTX found, returns NULL.
SSL_CTX *lookup(const StringRef &hostname);
// Looks up index using the given |hostname|. The exact match takes
// precedence over wildcard match. For wildcard match, longest
// match (sum of matched suffix and prefix length in bytes) is
// preferred, breaking a tie with longer suffix.
//
// 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:
CertNode root_;
// Stores pointers to copied hostname when adding hostname and
// ssl_ctx pair.
std::vector<std::unique_ptr<char[]>> hosts_;
// Exact match
Router router_;
// Wildcard reversed suffix match. The returned index is into
// 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
// |certfile|. The subjectAltNames and commonName are considered as
// eligible hostname. This function returns 0 if it succeeds, or -1.
// Even if no ssl_ctx is added to tree, this function returns 0.
int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,
const char *certfile);
// Adds hostnames in |cert| to lookup tree |lt|. The subjectAltNames
// and commonName are considered as eligible hostname. If there is at
// least one dNSName in subjectAltNames, commonName is not considered.
// This function returns 0 if it succeeds, or -1.
int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
X509 *cert);
// Returns true if |proto| is included in the
// protocol list |protos|.

View File

@ -36,83 +36,133 @@ namespace shrpx {
void test_shrpx_ssl_create_lookup_tree(void) {
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[] = {
StringRef::from_lit("example.com"),
StringRef::from_lit("www.example.org"),
StringRef::from_lit("*www.example.org"),
StringRef::from_lit("x*.host.domain"),
StringRef::from_lit("*yy.host.domain"),
StringRef::from_lit("nghttp2.sourceforge.net"),
StringRef::from_lit("sourceforge.net"),
StringRef::from_lit("sourceforge.net"), // duplicate
StringRef::from_lit("*.foo.bar"), // oo.bar is suffix of *.foo.bar
StringRef::from_lit("oo.bar")};
auto num = array_size(ctxs);
for (size_t i = 0; i < num; ++i) {
tree->add_cert(ctxs[i], hostnames[i]);
StringRef::from_lit("example.com"), // 0
StringRef::from_lit("www.example.org"), // 1
StringRef::from_lit("*www.example.org"), // 2
StringRef::from_lit("xy*.host.domain"), // 3
StringRef::from_lit("*yy.host.domain"), // 4
StringRef::from_lit("nghttp2.sourceforge.net"), // 5
StringRef::from_lit("sourceforge.net"), // 6
StringRef::from_lit("sourceforge.net"), // 7, duplicate
StringRef::from_lit("*.foo.bar"), // 8, oo.bar is suffix of *.foo.bar
StringRef::from_lit("oo.bar") // 9
};
auto num = array_size(hostnames);
for (size_t idx = 0; idx < num; ++idx) {
tree->add_cert(hostnames[idx], idx);
}
CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0]));
CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1]));
CU_ASSERT(ctxs[2] == tree->lookup(StringRef::from_lit("2www.example.org")));
CU_ASSERT(nullptr == tree->lookup(StringRef::from_lit("www2.example.org")));
CU_ASSERT(ctxs[3] == tree->lookup(StringRef::from_lit("x1.host.domain")));
tree->dump();
CU_ASSERT(0 == tree->lookup(hostnames[0]));
CU_ASSERT(1 == tree->lookup(hostnames[1]));
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
// character.
CU_ASSERT(nullptr == tree->lookup(StringRef::from_lit("yy.Host.domain")));
CU_ASSERT(ctxs[4] == tree->lookup(StringRef::from_lit("zyy.host.domain")));
CU_ASSERT(nullptr == tree->lookup(StringRef{}));
CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5]));
CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6]));
CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("yy.host.domain")));
CU_ASSERT(4 == tree->lookup(StringRef::from_lit("xyy.host.domain")));
CU_ASSERT(-1 == tree->lookup(StringRef{}));
CU_ASSERT(5 == tree->lookup(hostnames[5]));
CU_ASSERT(6 == tree->lookup(hostnames[6]));
constexpr char h6[] = "pdylay.sourceforge.net";
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(ctxs[9] == tree->lookup(hostnames[9]));
CU_ASSERT(8 == tree->lookup(StringRef::from_lit("x.foo.bar")));
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[] = {
StringRef::from_lit("rab"), StringRef::from_lit("zab"),
StringRef::from_lit("zzub"), StringRef::from_lit("ab")};
num = array_size(ctxs2);
StringRef::from_lit("rab"), // 1
StringRef::from_lit("zab"), // 2
StringRef::from_lit("zzub"), // 3
StringRef::from_lit("ab") // 4
};
num = array_size(names);
tree = make_unique<ssl::CertLookupTree>();
for (size_t i = 0; i < num; ++i) {
tree->add_cert(ctxs2[i], names[i]);
for (size_t idx = 0; idx < num; ++idx) {
tree->add_cert(names[idx], idx);
}
for (size_t i = 0; i < num; ++i) {
CU_ASSERT(ctxs2[i] == tree->lookup(names[i]));
}
for (size_t i = 0; i < num; ++i) {
SSL_CTX_free(ctxs2[i]);
CU_ASSERT(i == tree->lookup(names[i]));
}
}
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) {
namespace {
X509 *load_certificate(const char *filename) {
auto bio = BIO_new(BIO_s_file());
if (!bio) {
fprintf(stderr, "BIO_new() failed\n");
return nullptr;
}
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;
}
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;
ssl::CertLookupTree tree;
auto ssl_ctx = SSL_CTX_new(SSLv23_method());
const char certfile[] = NGHTTP2_TESTS_DIR "/testdata/cacert.pem";
rv = ssl::cert_lookup_tree_add_cert_from_file(&tree, ssl_ctx, certfile);
CU_ASSERT(0 == rv);
CU_ASSERT(ssl_ctx == tree.lookup(StringRef::from_lit("localhost")));
SSL_CTX_free(ssl_ctx);
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>

View File

@ -32,7 +32,7 @@
namespace shrpx {
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);
} // 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-----