summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLipin Dmitriy <blackwithwhite666@gmail.com>2017-05-25 22:56:28 +0300
committerLipin Dmitriy <dldmitry@yandex-team.ru>2017-05-26 12:00:04 +0300
commitf0acdcb8a220811a4bb2025d2477f0860f3ee1e0 (patch)
tree0338bd286b06816e90ddfe82c111e88df9b7cd72
parent29bb45c5083f1166b07aabf91916c6015fc3500f (diff)
downloadpycurl-f0acdcb8a220811a4bb2025d2477f0860f3ee1e0.tar.gz
add set_ca_certs method
-rw-r--r--Makefile1
-rw-r--r--doc/docstrings/curl_set_ca_certs.rst5
-rw-r--r--src/easy.c148
-rw-r--r--src/pycurl.h10
-rw-r--r--tests/appmanager.py19
-rw-r--r--tests/cadata_test.py32
-rw-r--r--tests/certs/ca.crt24
-rw-r--r--tests/certs/ca.key27
-rw-r--r--tests/certs/server.crt28
-rw-r--r--tests/certs/server.key38
10 files changed, 297 insertions, 35 deletions
diff --git a/Makefile b/Makefile
index c2b61dc..c6ac8b5 100644
--- a/Makefile
+++ b/Makefile
@@ -54,6 +54,7 @@ DOCSTRINGS_SOURCES = \
doc/docstrings/curl_reset.rst \
doc/docstrings/curl_setopt.rst \
doc/docstrings/curl_unsetopt.rst \
+ doc/docstrings/curl_set_ca_certs.rst \
doc/docstrings/multi.rst \
doc/docstrings/multi_add_handle.rst \
doc/docstrings/multi_assign.rst \
diff --git a/doc/docstrings/curl_set_ca_certs.rst b/doc/docstrings/curl_set_ca_certs.rst
new file mode 100644
index 0000000..49fa0a5
--- /dev/null
+++ b/doc/docstrings/curl_set_ca_certs.rst
@@ -0,0 +1,5 @@
+set_ca_certs() -> None
+
+Load ca certs from provided unicode string.
+
+Note that certificates will be added only when cURL starts new connection.
diff --git a/src/easy.c b/src/easy.c
index 6c721ad..db58364 100644
--- a/src/easy.c
+++ b/src/easy.c
@@ -178,6 +178,78 @@ check_curl_state(const CurlObject *self, int flags, const char *name)
}
+#if defined(HAVE_CURL_OPENSSL)
+/* internal helper that load certificates from buffer, returns -1 on error */
+static int
+add_ca_certs(SSL_CTX *context, void *data, Py_ssize_t len)
+{
+ // this code was copied from _ssl module
+ BIO *biobuf = NULL;
+ X509_STORE *store;
+ int retval = 0, err, loaded = 0;
+
+ if (len <= 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "Empty certificate data");
+ return -1;
+ } else if (len > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Certificate data is too long.");
+ return -1;
+ }
+
+ biobuf = BIO_new_mem_buf(data, (int)len);
+ if (biobuf == NULL) {
+ PyErr_SetString(PyExc_MemoryError, "Can't allocate buffer");
+ ERR_clear_error();
+ return -1;
+ }
+
+ store = SSL_CTX_get_cert_store(context);
+ assert(store != NULL);
+
+ while (1) {
+ X509 *cert = NULL;
+ int r;
+
+ cert = PEM_read_bio_X509(biobuf, NULL, 0, NULL);
+ if (cert == NULL) {
+ break;
+ }
+ r = X509_STORE_add_cert(store, cert);
+ X509_free(cert);
+ if (!r) {
+ err = ERR_peek_last_error();
+ if ((ERR_GET_LIB(err) == ERR_LIB_X509) &&
+ (ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) {
+ /* cert already in hash table, not an error */
+ ERR_clear_error();
+ } else {
+ break;
+ }
+ }
+ loaded++;
+ }
+
+ err = ERR_peek_last_error();
+ if ((loaded > 0) &&
+ (ERR_GET_LIB(err) == ERR_LIB_PEM) &&
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+ /* EOF PEM file, not an error */
+ ERR_clear_error();
+ retval = 0;
+ } else {
+ PyErr_SetString(ErrorObject, ERR_reason_error_string(err));
+ ERR_clear_error();
+ retval = -1;
+ }
+
+ BIO_free(biobuf);
+ return retval;
+}
+#endif
+
+
/*************************************************************************
// CurlObject
**************************************************************************/
@@ -340,6 +412,11 @@ util_curl_xdecref(CurlObject *self, int flags, CURL *handle)
/* Decrement refcounts for httppost related references. */
Py_CLEAR(self->httppost_ref_list);
}
+
+ if (flags & PYCURL_MEMGROUP_CACERTS) {
+ /* Decrement refcounts for ca certs related references. */
+ Py_CLEAR(self->ca_certs_obj);
+ }
}
@@ -492,6 +569,8 @@ do_curl_traverse(CurlObject *self, visitproc visit, void *arg)
VISIT(self->postfields_obj);
+ VISIT(self->ca_certs_obj);
+
return 0;
#undef VISIT
}
@@ -1356,6 +1435,33 @@ verbose_error:
}
+#if defined(HAVE_CURL_OPENSSL)
+static CURLcode
+ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *ptr)
+{
+ CurlObject *self;
+ PYCURL_DECLARE_THREAD_STATE;
+
+ UNUSED(curl);
+
+ /* acquire thread */
+ self = (CurlObject *)ptr;
+ if (!PYCURL_ACQUIRE_THREAD())
+ return CURLE_FAILED_INIT;
+
+ int r = add_ca_certs((SSL_CTX*)ssl_ctx,
+ PyBytes_AS_STRING(self->ca_certs_obj),
+ PyBytes_GET_SIZE(self->ca_certs_obj));
+
+ if (r != 0)
+ PyErr_Print();
+
+ PYCURL_RELEASE_THREAD();
+ return r == 0 ? CURLE_OK : CURLE_FAILED_INIT;
+}
+#endif
+
+
/* ------------------------ reset ------------------------ */
static PyObject*
@@ -2634,6 +2740,45 @@ do_curl_pause(CurlObject *self, PyObject *args)
}
+#if defined(HAVE_CURL_OPENSSL)
+/* load ca certs from string */
+static PyObject *
+do_curl_set_ca_certs(CurlObject *self, PyObject *args)
+{
+ PyObject *cadata;
+
+ if (!PyArg_ParseTuple(args, "O:cadata", &cadata))
+ return NULL;
+
+ PyObject *cadata_ascii = PyUnicode_AsASCIIString(cadata);
+ if (cadata_ascii == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "cadata should be an ASCII string or a "
+ "bytes-like object");
+ return NULL;
+ }
+
+ Py_CLEAR(self->ca_certs_obj);
+ self->ca_certs_obj = cadata_ascii;
+
+ int res;
+
+ const curl_ssl_ctx_callback ssl_ctx_cb = ssl_ctx_callback;
+ res = curl_easy_setopt(self->handle, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_cb);
+ if (res != CURLE_OK) {
+ CURLERROR_RETVAL();
+ }
+
+ res = curl_easy_setopt(self->handle, CURLOPT_SSL_CTX_DATA, self);
+ if (res != CURLE_OK) {
+ CURLERROR_RETVAL();
+ }
+
+ Py_RETURN_NONE;
+}
+#endif
+
+
static PyObject *do_curl_getstate(CurlObject *self)
{
PyErr_SetString(PyExc_TypeError, "Curl objects do not support serialization");
@@ -2664,6 +2809,9 @@ PYCURL_INTERNAL PyMethodDef curlobject_methods[] = {
{"setopt_string", (PyCFunction)do_curl_setopt_string, METH_VARARGS, curl_setopt_string_doc},
{"unsetopt", (PyCFunction)do_curl_unsetopt, METH_VARARGS, curl_unsetopt_doc},
{"reset", (PyCFunction)do_curl_reset, METH_NOARGS, curl_reset_doc},
+#if defined(HAVE_CURL_OPENSSL)
+ {"set_ca_certs", (PyCFunction)do_curl_set_ca_certs, METH_VARARGS, curl_set_ca_certs_doc},
+#endif
{"__getstate__", (PyCFunction)do_curl_getstate, METH_NOARGS, NULL},
{"__setstate__", (PyCFunction)do_curl_setstate, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
diff --git a/src/pycurl.h b/src/pycurl.h
index 6fe64e0..af10b1c 100644
--- a/src/pycurl.h
+++ b/src/pycurl.h
@@ -167,7 +167,8 @@ typedef int Py_ssize_t;
# if defined(HAVE_CURL_OPENSSL)
# define PYCURL_NEED_SSL_TSL
# define PYCURL_NEED_OPENSSL_TSL
-# include <openssl/crypto.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
# define COMPILE_SSL_LIB "openssl"
# elif defined(HAVE_CURL_GNUTLS)
# include <gnutls/gnutls.h>
@@ -323,10 +324,13 @@ PyText_Check(PyObject *o);
#define PYCURL_MEMGROUP_HTTPPOST 32
/* Postfields object */
#define PYCURL_MEMGROUP_POSTFIELDS 64
+/* CA certs object */
+#define PYCURL_MEMGROUP_CACERTS 128
#define PYCURL_MEMGROUP_EASY \
(PYCURL_MEMGROUP_CALLBACK | PYCURL_MEMGROUP_FILE | \
- PYCURL_MEMGROUP_HTTPPOST | PYCURL_MEMGROUP_POSTFIELDS)
+ PYCURL_MEMGROUP_HTTPPOST | PYCURL_MEMGROUP_POSTFIELDS | \
+ PYCURL_MEMGROUP_CACERTS)
#define PYCURL_MEMGROUP_ALL \
(PYCURL_MEMGROUP_ATTRDICT | PYCURL_MEMGROUP_EASY | \
@@ -382,6 +386,8 @@ typedef struct CurlObject {
PyObject *writeheader_fp;
/* reference to the object used for CURLOPT_POSTFIELDS */
PyObject *postfields_obj;
+ /* reference to the object containing ca certs */
+ PyObject *ca_certs_obj;
/* misc */
char error[CURL_ERROR_SIZE+1];
} CurlObject;
diff --git a/tests/appmanager.py b/tests/appmanager.py
index f01dd07..1857810 100644
--- a/tests/appmanager.py
+++ b/tests/appmanager.py
@@ -11,13 +11,13 @@ def setup(*specs):
def perform_setup(*specs):
from . import runwsgi
-
+
app_specs = []
for spec in specs:
app_module = __import__(spec[0], globals(), locals(), ['app'], 1)
app = getattr(app_module, 'app')
app_specs.append([app] + list(spec[1:]))
-
+
return runwsgi.app_runner_setup(*app_specs)
quit = False
@@ -28,27 +28,28 @@ def sigterm_handler(*args):
def run_standalone():
import signal
-
+
funcs = []
-
+
signal.signal(signal.SIGTERM, sigterm_handler)
-
+
funcs.append(setup(('app', 8380)))
funcs.append(setup(('app', 8381)))
funcs.append(setup(('app', 8382)))
funcs.append(setup(('app', 8383, dict(ssl=True))))
-
+ funcs.append(setup(('app', 8384, dict(ssl=True))))
+
for setup_func, teardown_func in funcs:
setup_func(sys.modules[__name__])
-
+
sys.stdout.write("Running, use SIGTERM or SIGINT to stop\n")
-
+
try:
while not quit:
time.sleep(1)
except KeyboardInterrupt:
pass
-
+
for setup_func, teardown_func in funcs:
teardown_func(sys.modules[__name__])
diff --git a/tests/cadata_test.py b/tests/cadata_test.py
new file mode 100644
index 0000000..5404fd6
--- /dev/null
+++ b/tests/cadata_test.py
@@ -0,0 +1,32 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+# vi:ts=4:et
+
+import os
+import pycurl
+import unittest
+
+from . import appmanager
+from . import util
+
+setup_module, teardown_module = appmanager.setup(('app', 8384, dict(ssl=True)))
+
+class CaCertsTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ @util.only_ssl_backends('openssl')
+ def test_request_with_verifypeer(self):
+ with open(os.path.join(os.path.dirname(__file__), 'certs', 'ca.crt'), 'rb') as stream:
+ cadata = stream.read().decode('ASCII')
+ self.curl.setopt(pycurl.URL, 'https://localhost:8384/success')
+ sio = util.BytesIO()
+ self.curl.set_ca_certs(cadata)
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ # self signed certificate, but ca cert should be loaded
+ self.curl.setopt(pycurl.SSL_VERIFYPEER, 1)
+ self.curl.perform()
+ assert sio.getvalue().decode() == 'success'
diff --git a/tests/certs/ca.crt b/tests/certs/ca.crt
new file mode 100644
index 0000000..116d375
--- /dev/null
+++ b/tests/certs/ca.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEGzCCAwOgAwIBAgIJAJYfjyO6SAVVMA0GCSqGSIb3DQEBBQUAMGYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRowGAYDVQQKExFQeWNVUkwgdGVz
+dCBzdWl0ZTESMBAGA1UECxMJbG9jYWxob3N0MRIwEAYDVQQDEwlsb2NhbGhvc3Qw
+HhcNMTcwNTI2MDcxNzM3WhcNNDQxMDExMDcxNzM3WjBmMQswCQYDVQQGEwJBVTET
+MBEGA1UECBMKU29tZS1TdGF0ZTEaMBgGA1UEChMRUHljVVJMIHRlc3Qgc3VpdGUx
+EjAQBgNVBAsTCWxvY2FsaG9zdDESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAra7SYD1pzWQhxJryYKf9AuKPJfLamkcq
+Y062YBUgoSDCx0ZjFfMTa2u0zqG2Vt2ETuvVkDhBAw/0AF7vLChU9tjANKXuhRIK
+zGrqEk/wOMFxtbVYGwc+VufB3NBB3fOXFjwe/yAP5N/Zw4+P3Lkn4OAVRFvpRrNh
+RgixJUVpqzjzzELMj+ZZcLlYWkUOUDfMEIJHeZ9sFxdrbENnTmJieX1Q47ND9H9E
+zQloUvc8Xf6K1e1KliIhlombdo3RLAGUzZlWlG2WorFzX161E2hY2FHFId9SusWH
+NJxKsmOUerNv0rWYez54OOwXWh/2OKcNV1hK1IFKkEHUP7UMLhvAUwIDAQABo4HL
+MIHIMB0GA1UdDgQWBBQu1NQH28vT0GEQe3ZIFM2R/ZpGwTCBmAYDVR0jBIGQMIGN
+gBQu1NQH28vT0GEQe3ZIFM2R/ZpGwaFqpGgwZjELMAkGA1UEBhMCQVUxEzARBgNV
+BAgTClNvbWUtU3RhdGUxGjAYBgNVBAoTEVB5Y1VSTCB0ZXN0IHN1aXRlMRIwEAYD
+VQQLEwlsb2NhbGhvc3QxEjAQBgNVBAMTCWxvY2FsaG9zdIIJAJYfjyO6SAVVMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAYSpSiFOhdPC1Q6wvLlHC+R
+6m4L9B++wvnZd0MUVgUf0zlxCVZwitkj+39SX5mTrn4SZTEKt73M+bT/pfdjUaKd
+qSztbp4xVCnMN5QBt9UH7W565t296pzgwhv97PGvBgMhW91dQIoi4TeloYL1O41z
+bPgIrTg+W87+ugorZNFPWHAgKbxhiKx8X2w1Z/ohDZhyzh5VC40hD97jRXePq4hy
+C9fq6zKbF0lPPNJyjMnc5Ppj03eUTsChqd6j8S1JmdeQYeubdkQYnyt2vLYdrxEy
+21iULWPWIMc5RVUfVHtjn/o2NWeR7y/2TDRrVK7AlrbrcRCH2J7JgA2PWSffvxE=
+-----END CERTIFICATE-----
diff --git a/tests/certs/ca.key b/tests/certs/ca.key
new file mode 100644
index 0000000..75a9ba4
--- /dev/null
+++ b/tests/certs/ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAra7SYD1pzWQhxJryYKf9AuKPJfLamkcqY062YBUgoSDCx0Zj
+FfMTa2u0zqG2Vt2ETuvVkDhBAw/0AF7vLChU9tjANKXuhRIKzGrqEk/wOMFxtbVY
+Gwc+VufB3NBB3fOXFjwe/yAP5N/Zw4+P3Lkn4OAVRFvpRrNhRgixJUVpqzjzzELM
+j+ZZcLlYWkUOUDfMEIJHeZ9sFxdrbENnTmJieX1Q47ND9H9EzQloUvc8Xf6K1e1K
+liIhlombdo3RLAGUzZlWlG2WorFzX161E2hY2FHFId9SusWHNJxKsmOUerNv0rWY
+ez54OOwXWh/2OKcNV1hK1IFKkEHUP7UMLhvAUwIDAQABAoIBAGLsEpiMAgngwTbo
+hao1o+6TubKEiquaYvMi7s702ZvMPAQh++eRhfsF4npaMq9xBZ2pxv6Ye7bRzEi1
+yYWeBx59P6P86khSiWH6dw0tCIZa73fuLJtgWcpHv+wTlaBj0Cby4TiwOz1Bnhc7
+WlX+A0+acaJ4svn4yyuHYdX3ngLNwe0WkP0a6M0KOv+rxRW4FFITrO98Yz9PtPZP
+Z4nLvt6dsX6m879WIcFA+wkab4aMrWMI6b80wKN72sKAVC3LNU4iJ7PTYmbyIwJP
+YDOD/+UAp4T/VV+CIKOlS6YEozMpOD4kt4SxzEZQu8cuAovtKdKcOn0WhJYBJVGc
+LCJpSfkCgYEA2+JC/VCtF91d4/70S+Jswqc3T3ldifZXYRVH+iFeBSGrg++GHeKW
+JKRk0+v1Ul+P/6Ygd3ycsw/leKUr5c7+Fi3z2vk8q0i0815yQJuqFJIg+WTbeutv
+DiUj3UTycvFQtAreEMyAMB99fWmm4CuAuYVQIHojhm5SI3lQUjhBnu8CgYEAyjXj
+0DygA8obXQaB2ljoj75ukDMN+0JCmZ+WqI2+obREAR2f+X7wHtbdwyQiwRBIV+b7
+wCnAXAACXFDEoFboN+0Ex7aFVjAc2BawdHmwilhxk2nYaCTgxQZLYUzEWSNp20WA
+BTUU5Tjs4fdDoKT7G6xMEaBqvooaImXxFVmjNN0CgYBeJPZBt3UlLqawo8y9YOjo
+Pugzoucl1s96xb3Xnsm+sLfa+YcW7JkUfz6cbf7PkhL5houIHVaKZFf/29h7wLCR
+loM+UlBjlfHD8cBBYWTlAdwUa9Z9PqiCCezdJFQaWrAPJkgGMUkBUbpNJBtLB9VJ
+mYbBIQps2HdasOpvCZ8vCQKBgQDF9jwxgSimjRZ83AIEYUZMc4KKaXEmqpfJDhPQ
+r/QRGwn4jagv+bXae0Bf6uCbYfVxGREd78ICT4AAIJJe5rYxCjnDy0x+NFwIsS3O
+2dObnTqTtuvGCVSDjsX9W8pd+e2IXWIXtv/d6Pz/u7LZcqrjTKqsFwBpyYoMYwDC
+hh7hgQKBgCI2wr3QrDfabD9vZd/pO8v7jD3mk84fj4pO6D1c8wg4n6IffOqQih/z
+1AU/RBIUIPERJweNGeG+YOlA2pE02u/J/0UpH5663vM76GQ7nY/Vr6rd4OcNJsR3
+xLlOz8XMzkqt+BcTsLfzjO4wAFEutUywDrT8DBkQR5nuUqHjVj8f
+-----END RSA PRIVATE KEY-----
diff --git a/tests/certs/server.crt b/tests/certs/server.crt
index f904889..9049a51 100644
--- a/tests/certs/server.crt
+++ b/tests/certs/server.crt
@@ -1,14 +1,20 @@
-----BEGIN CERTIFICATE-----
-MIICGzCCAYQCCQCdeJzNRLLLvDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJB
+MIIDNDCCAhwCCQDgdMxIoTFgtTANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEaMBgGA1UEChMRUHljVVJMIHRlc3Qgc3Vp
-dGUxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMzEyMjIxMzA0MTVaFw0xNzA5MTcx
-MzA0MTVaMFIxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRowGAYD
-VQQKExFQeWNVUkwgdGVzdCBzdWl0ZTESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0G
-CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7
-FjhJYh+vvwV8JK12qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSx
-HgNvTXI8y0xT3EfODfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tH
-SJhRgs4iAwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAGhX0iIMCrRcI6T6S4YydkXf
-7LrXZpjSJwVGSCd5ehx3Qvc1LQNJjpB68F0MhVTqbDP1o3CAHaTa2s/8NC9j3tV7
-bUynoKJT3srpHisfdd/SV538mWvFDtGRctbmmqp8qT4On+kr76dKj+/d3HyfOKIK
-Aasa7ODxFKbbY542yYHu
+dGUxEjAQBgNVBAsTCWxvY2FsaG9zdDESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE3
+MDUyNjA3MTkyM1oXDTQ0MTAxMDA3MTkyM1owUjELMAkGA1UEBhMCQVUxEzARBgNV
+BAgTClNvbWUtU3RhdGUxGjAYBgNVBAoTEVB5Y1VSTCB0ZXN0IHN1aXRlMRIwEAYD
+VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDm
+Jy7+5TjQyQym2GISHFX1jUXanxpPoRaTx1YvEXw+PYPdFinUr4ZiKzwrX6Hl1Wb0
+oIfrV7g2UYrnM7qEatLjjOKZDTF8kGp5Q5+QBbYQTZw23OA12+pQVyHVGyWQfmF6
+YjEL0LJjtFxTA/Z2fAnnmbrZMRd+E3AxNWBOMgmSIKbbS19VRvDlRSaJl3mr630P
+H460aQ3zn5rjOCO2IApeSMTgFcoRTxhzYaRPDP3yZ5O1Ge4rgdPEHGvO/29Ib5Rm
+nC9gMFn8AHc9iXMVXZrSjIXdN/jzKm1DWA+15RyYisPvPVS7/ZWRiyRDvdpTUZSd
+OrLuR80EhMnC6YirwxwRAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAGxhzIkTNXW7
+C/3qhudGHJO5SX2t7Am3ObyjrHDrhEQj7bpUg3xze/WMhxJ87Rq9UC8aZwQ019Eu
+XTBHWwaDJJhoRAWvxgb1F1i1pvK6ZknXCvoFTYejlflbOTPJlJB09Pj3XrMCPCEW
+yZZsvI+JDd+LrChFmLUEaNgl1H58blXKoMeEmjE4QOb2ttemCJb0FfndvqiniVW1
+h35AjhkJBfy9NGYB6FpMfj+NJYhXji0fVg0CYPeO7lRZNJ/Dg5GhdxcmGhlHkx8u
+WqYZZLD4ovsphhuP0spsxPi4Uw+pXKHKHh5MVngWPHO237g/+JpD4IY4G2jgNruV
+Wu5cKxU79Iw=
-----END CERTIFICATE-----
diff --git a/tests/certs/server.key b/tests/certs/server.key
index 5bdbbf9..12103d3 100644
--- a/tests/certs/server.key
+++ b/tests/certs/server.key
@@ -1,15 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
-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
+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
-----END RSA PRIVATE KEY-----