diff --git a/src/Makefile.am b/src/Makefile.am
index b78e3c0c1c38a90f48b0329e43c76b962252dd6a..b4f1bf83b056c3475ecfa1aa3a672b9c42ecd3ad 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -155,8 +155,9 @@ libknot_la_SOURCES =				\
 	libknot/tsig-op.c			\
 	libknot/binary.h			\
 	libknot/binary.c			\
-	libknot/dnssec/cleanup.h		\
 	libknot/dnssec/config.h			\
+	libknot/dnssec/crypto.c			\
+	libknot/dnssec/crypto.h			\
 	libknot/dnssec/key.c			\
 	libknot/dnssec/key.h			\
 	libknot/dnssec/nsec-bitmap.h		\
diff --git a/src/knot/main.c b/src/knot/main.c
index bc1b6458b4fffff34f383fa33c79d869c77dd4b7..704b5a89d649b3421139ecc6305032e6ad9596cb 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -27,7 +27,7 @@
 #endif /* HAVE_CAP_NG_H */
 
 #include "libknot/common.h"
-#include "libknot/dnssec/cleanup.h"
+#include "libknot/dnssec/crypto.h"
 #include "common/evqueue.h"
 #include "knot/knot.h"
 #include "knot/server/server.h"
@@ -54,6 +54,13 @@ char *zone = NULL;
 // Cleanup handler
 static int do_cleanup(server_t *server, char *configf, char *pidf);
 
+// atexit() handler for server code
+static void deinit(void)
+{
+	knot_crypto_cleanup();
+	knot_crypto_cleanup_threads();
+}
+
 // SIGINT signal handler
 void interrupt_handle(int s)
 {
@@ -100,7 +107,7 @@ void help(void)
 
 int main(int argc, char **argv)
 {
-	atexit(knot_dnssec_cleanup);
+	atexit(deinit);
 
 	// Parse command line arguments
 	int c = 0, li = 0;
@@ -167,6 +174,10 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
+	// Initialize cryptographic backend
+	knot_crypto_init();
+	knot_crypto_init_threads();
+
 	// Now check if we want to daemonize
 	if (daemonize) {
 		if (daemon(1, 0) != 0) {
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index e433f25206a9a54ecf578e01774ca80d7ed28a0a..3d89002de55cfae5fefa839a579d5a9f67959a3b 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -20,7 +20,6 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <errno.h>
-#include <openssl/evp.h>
 #include <assert.h>
 
 #include "common/prng.h"
@@ -35,7 +34,7 @@
 #include "libknot/nameserver/name-server.h"
 #include "libknot/zone/zonedb.h"
 #include "libknot/dname.h"
-#include "libknot/dnssec/cleanup.h"
+#include "libknot/dnssec/crypto.h"
 
 /*! \brief Event scheduler loop. */
 static int evsched_run(dthread_t *thread)
@@ -77,7 +76,7 @@ static int evsched_run(dthread_t *thread)
 /*! \brief Event scheduler thread destructor. */
 static int evsched_destruct(dthread_t *thread)
 {
-	knot_dnssec_thread_cleanup();
+	knot_crypto_cleanup_thread();
 	return KNOT_EOK;
 }
 
@@ -353,8 +352,6 @@ server_t *server_create()
 		return NULL;
 	}
 	knot_ns_set_data(server->nameserver, server);
-	dbg_server("server: initializing OpenSSL\n");
-	OpenSSL_add_all_digests();
 
 	// Create XFR handler
 	server->xfr = xfr_create(XFR_THREADS_COUNT, server->nameserver);
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 8ec350a1bb4f32dcfe377d6a1b20273dd556b2bf..2390208fd8c68e2c4f2d0e7428e353ec18366de6 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -41,8 +41,8 @@
 #include "knot/server/zones.h"
 #include "libknot/nameserver/name-server.h"
 #include "libknot/packet/wire.h"
-#include "libknot/dnssec/cleanup.h"
 #include "libknot/nameserver/ns_proc_query.h"
+#include "libknot/dnssec/crypto.h"
 
 /*! \brief TCP worker data. */
 typedef struct tcp_worker_t {
@@ -568,7 +568,7 @@ finish:
 
 int tcp_handler_destruct(dthread_t *thread)
 {
-	knot_dnssec_thread_cleanup();
+	knot_crypto_cleanup_thread();
 	return KNOT_EOK;
 }
 
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index 8ab9b9ddccf5fbf365a85393ec5e6aab4b829dd2..d5a8a69a83cea3737b38df182962cef22a721a15 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -50,7 +50,7 @@
 #include "libknot/packet/pkt.h"
 #include "knot/server/zones.h"
 #include "knot/server/notify.h"
-#include "libknot/dnssec/cleanup.h"
+#include "libknot/dnssec/crypto.h"
 
 /* Buffer identifiers. */
 enum {
@@ -604,6 +604,6 @@ int udp_master(dthread_t *thread)
 
 int udp_master_destruct(dthread_t *thread)
 {
-	knot_dnssec_thread_cleanup();
+	knot_crypto_cleanup_thread();
 	return KNOT_EOK;
 }
diff --git a/src/knot/server/zone-load.c b/src/knot/server/zone-load.c
index 86be8e6bda294f8098da6ec2db05860b21eec859..e2bed82b7027518be448a3599ff8a8ff37562193 100644
--- a/src/knot/server/zone-load.c
+++ b/src/knot/server/zone-load.c
@@ -26,7 +26,7 @@
 #include "knot/server/zones.h"
 #include "knot/zone/zone-load.h"
 #include "libknot/dname.h"
-#include "libknot/dnssec/cleanup.h"
+#include "libknot/dnssec/crypto.h"
 #include "libknot/nameserver/name-server.h"
 #include "libknot/rdata.h"
 #include "libknot/zone/zone.h"
@@ -695,7 +695,7 @@ static int zone_loader_thread(dthread_t *thread)
 
 static int zone_loader_destruct(dthread_t *thread)
 {
-	knot_dnssec_thread_cleanup();
+	knot_crypto_cleanup_thread();
 	return KNOT_EOK;
 }
 
diff --git a/src/libknot/dnssec/crypto.c b/src/libknot/dnssec/crypto.c
new file mode 100644
index 0000000000000000000000000000000000000000..b85171cdc4e3ef2bf9ed5d73c40b215c9fbcadc0
--- /dev/null
+++ b/src/libknot/dnssec/crypto.c
@@ -0,0 +1,147 @@
+/*  Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <pthread.h>
+
+#include "libknot/common.h"
+#include "libknot/dnssec/crypto.h"
+
+/*!
+ * \brief Mutexes to be used by OpenSSL.
+ */
+static pthread_mutex_t *openssl_mutex = NULL;
+static int openssl_mutex_count = 0;
+
+/*!
+ * \brief Callback for OpenSSL mutex locking and unlocking.
+ *
+ * \see CRYPTO_set_locking_callback() in OpenSSL documentation.
+ *
+ * \param mode  Locking mode.
+ * \param n     Mutex number.
+ * \param file  Source file where locking occurs (for debugging).
+ * \param line  Line number where locking occurs (for debugging).
+ */
+static void openssl_mutex_cb(int mode, int n, const char *file, int line)
+{
+	UNUSED(file);
+	UNUSED(line);
+
+	assert(openssl_mutex);
+	assert(n < openssl_mutex_count);
+
+	pthread_mutex_t *mutex = &openssl_mutex[n];
+
+	if (mode & CRYPTO_LOCK) {
+		pthread_mutex_lock(mutex);
+	} else {
+		pthread_mutex_unlock(mutex);
+	}
+}
+
+/*!
+ * \brief Initialize mutexes for OpenSSL usage.
+ */
+static void openssl_mutexes_init(void)
+{
+	assert(openssl_mutex_count == 0);
+	assert(openssl_mutex == NULL);
+
+	openssl_mutex_count = CRYPTO_num_locks();
+	if (openssl_mutex_count == 0) {
+		return;
+	}
+
+	openssl_mutex = calloc(openssl_mutex_count, sizeof(pthread_mutex_t));
+	for (int i = 0; i < openssl_mutex_count; i++) {
+		pthread_mutex_init(&openssl_mutex[i], NULL);
+	}
+
+	CRYPTO_set_locking_callback(openssl_mutex_cb);
+}
+
+/*!
+ * \brief Destroy mutexes for OpenSSL usage.
+ */
+static void openssl_mutexes_destroy(void)
+{
+	assert(openssl_mutex);
+
+	for (int i = 0; i < openssl_mutex_count; i++) {
+		pthread_mutex_destroy(&openssl_mutex[i]);
+	}
+
+	free(openssl_mutex);
+
+	openssl_mutex_count = 0;
+	openssl_mutex = NULL;
+}
+
+/*!
+ * \brief Callback for thread identification for purpose of OpenSSL.
+ *
+ * \see CRYPTO_THREADID_set_callback() in OpenSSL documentation.
+ *
+ * \param openssl_id  Thread identifier in OpenSSL.
+ */
+static void openssl_threadid_cb(CRYPTO_THREADID *openssl_id)
+{
+	pthread_t id = pthread_self();
+	CRYPTO_THREADID_set_numeric(openssl_id, id);
+}
+
+/*- public API --------------------------------------------------------------*/
+
+void knot_crypto_init(void)
+{
+	OpenSSL_add_all_digests();
+}
+
+void knot_crypto_cleanup(void)
+{
+	EVP_cleanup();
+	CRYPTO_cleanup_all_ex_data();
+	ERR_free_strings();
+
+	knot_crypto_cleanup_thread();
+}
+
+void knot_crypto_cleanup_thread(void)
+{
+	ERR_remove_state(0);
+}
+
+void knot_crypto_init_threads(void)
+{
+	// locking
+	if (!openssl_mutex) {
+		openssl_mutexes_init();
+	}
+
+	// thread identification
+	CRYPTO_THREADID_set_callback(openssl_threadid_cb);
+}
+
+void knot_crypto_cleanup_threads(void)
+{
+	if (openssl_mutex) {
+		openssl_mutexes_destroy();
+	}
+}
diff --git a/src/libknot/dnssec/cleanup.h b/src/libknot/dnssec/crypto.h
similarity index 51%
rename from src/libknot/dnssec/cleanup.h
rename to src/libknot/dnssec/crypto.h
index 40d1948edf718aecb352cd1dd3f52567f46f5680..4273c5aeb59bbc70226d1fa5f275f6a2e0322ff4 100644
--- a/src/libknot/dnssec/cleanup.h
+++ b/src/libknot/dnssec/crypto.h
@@ -14,42 +14,46 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 /*!
- * \file cleanup.h
+ * \file crypto.h
  *
  * \author Jan Vcelak <jan.vcelak@nic.cz>
  *
- * \brief DNSSEC deinitialization
+ * \brief Cryptographic backend initialization and clean up.
  *
  * \addtogroup dnssec
  * @{
  */
 
-#ifndef _KNOT_DNSSEC_CLEANUP_H_
-#define _KNOT_DNSSEC_CLEANUP_H_
+#ifndef _KNOT_DNSSEC_CRYPTO_H_
+#define _KNOT_DNSSEC_CRYPTO_H_
 
-#include <openssl/err.h>
-#include <openssl/evp.h>
+/*!
+ * \brief Initialize cryptographic backend.
+ */
+void knot_crypto_init(void);
+
+/*!
+ * \brief Clean up data allocated by cryptographic backend.
+ */
+void knot_crypto_cleanup(void);
 
 /*!
- * \brief Deinitialize OpenSSL library thread specific data.
+ * \brief Clean up thread specific data allocated by cryptographic backend.
  */
-static inline void knot_dnssec_thread_cleanup(void)
-{
-	ERR_remove_state(0);
-}
+void knot_crypto_cleanup_thread(void);
 
 /*!
- * \brief Deinitialize OpenSSL library.
+ * \brief Initialize data required for thread-safety of cryptographic backend.
+ *
+ * \note Does not include actions performed by knot_crypto_init().
  */
-static inline void knot_dnssec_cleanup(void)
-{
-	EVP_cleanup();
-	CRYPTO_cleanup_all_ex_data();
-	ERR_free_strings();
+void knot_crypto_init_threads(void);
 
-	knot_dnssec_thread_cleanup();
-}
+/*!
+ * \brief Clean up allocated data required for thread-safety of crypto backend.
+ */
+void knot_crypto_cleanup_threads(void);
 
-#endif // _KNOT_DNSSEC_CLEANUP_H_
+#endif // _KNOT_DNSSEC_CRYPTO_H_
 
 /*! @} */
diff --git a/src/utils/nsec3hash/nsec3hash_main.c b/src/utils/nsec3hash/nsec3hash_main.c
index c164571124f0b18fac28f9d28a0cf68411fb8042..59249faabc8328665f36bac83f29543b66577226 100644
--- a/src/utils/nsec3hash/nsec3hash_main.c
+++ b/src/utils/nsec3hash/nsec3hash_main.c
@@ -24,7 +24,7 @@
 #include "common/errcode.h"
 #include "common/hex.h"
 #include "common/strtonum.h"
-#include "libknot/dnssec/cleanup.h"
+#include "libknot/dnssec/crypto.h"
 #include "libknot/dnssec/nsec3.h"
 
 #define PROGRAM_NAME "knsec3hash"
@@ -115,7 +115,7 @@ int main(int argc, char *argv[])
 		return 1;
 	}
 
-	atexit(knot_dnssec_cleanup);
+	atexit(knot_crypto_cleanup);
 
 	int exit_code = 1;
 	knot_nsec3_params_t nsec3_params = { 0 };
diff --git a/src/utils/nsupdate/nsupdate_main.c b/src/utils/nsupdate/nsupdate_main.c
index 174019fd957e8b5beb177c596bf2dd022f452bc3..7d8c74ee662eab04e9bf3a99d6898ca9565d1c96 100644
--- a/src/utils/nsupdate/nsupdate_main.c
+++ b/src/utils/nsupdate/nsupdate_main.c
@@ -20,11 +20,11 @@
 #include "common/errcode.h"			// KNOT_EOK
 #include "utils/nsupdate/nsupdate_params.h"	// params_t
 #include "utils/nsupdate/nsupdate_exec.h"	// host_exec
-#include "libknot/dnssec/cleanup.h"		// knot_dnssec_cleanup
+#include "libknot/dnssec/crypto.h"		// knot_crypto_cleanup
 
 int main(int argc, char *argv[])
 {
-	atexit(knot_dnssec_cleanup);
+	atexit(knot_crypto_cleanup);
 
 	int ret = EXIT_SUCCESS;
 
diff --git a/tests-extra/functional/functests.py b/tests-extra/functional/functests.py
index 898cc03b41a802b9eddb113868eba1ed6d1546dc..660575118977d2afa5dfc72b471d23dbfc39984e 100755
--- a/tests-extra/functional/functests.py
+++ b/tests-extra/functional/functests.py
@@ -57,7 +57,7 @@ def parse_args(cmd_args):
         test = parts[0]
 
         if test in storage:
-            storage[test].append(case)
+            storage[test].extend(case)
         else:
             storage[test] = case
 
diff --git a/tests-extra/functional/tests/basic/nsec/test.py b/tests-extra/functional/tests/basic/nsec/test.py
index c367da207fc6ef36cf383e79d0377a903d7e2a97..cf58dbf44e50fc8d91fe3e79deccc54f983d0937 100644
--- a/tests-extra/functional/tests/basic/nsec/test.py
+++ b/tests-extra/functional/tests/basic/nsec/test.py
@@ -18,34 +18,42 @@ t.start()
 
 # B1. Answer.
 resp = knot.dig("x.w.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B2. Name Error.
-resp = knot.dig("mv.example", "A", dnssec=True)
+resp = knot.dig("ml.example", "A", dnssec=True)
+resp.check(rcode="NXDOMAIN", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B3. No Data Error.
 resp = knot.dig("ns1.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B4. Referral to Signed Zone.
 resp = knot.dig("mc.a.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
 resp.cmp(bind)
 
 # B5. Referral to Unsigned Zone.
 resp = knot.dig("mc.b.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
 resp.cmp(bind)
 
 # B6. Wildcard Expansion.
 resp = knot.dig("a.z.w.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B7. Wildcard No Data Error.
 resp = knot.dig("a.z.w.example", "AAAA", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B8. DS Child Zone No Data Error.
 resp = knot.dig("example", "DS", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 t.end()
diff --git a/tests-extra/functional/tests/basic/nsec3/test.py b/tests-extra/functional/tests/basic/nsec3/test.py
index 1002f013916c2df16b26e64d91669bfceb62c8cd..5287a8ab28ee468a0203dd624370883e3a673596 100644
--- a/tests-extra/functional/tests/basic/nsec3/test.py
+++ b/tests-extra/functional/tests/basic/nsec3/test.py
@@ -18,34 +18,42 @@ t.start()
 
 # B1. Answer.
 resp = knot.dig("x.w.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B2. Name Error.
-resp = knot.dig("mv.example", "A", dnssec=True)
+resp = knot.dig("ml.example", "A", dnssec=True)
+resp.check(rcode="NXDOMAIN", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B3. No Data Error.
 resp = knot.dig("ns1.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B4. Referral to Signed Zone.
 resp = knot.dig("mc.a.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
 resp.cmp(bind)
 
 # B5. Referral to Unsigned Zone.
 resp = knot.dig("mc.b.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
 resp.cmp(bind)
 
 # B6. Wildcard Expansion.
 resp = knot.dig("a.z.w.example", "MX", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B7. Wildcard No Data Error.
 resp = knot.dig("a.z.w.example", "AAAA", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 # B8. DS Child Zone No Data Error.
 resp = knot.dig("example", "DS", dnssec=True)
+resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
 resp.cmp(bind)
 
 t.end()
diff --git a/tests-extra/functional/tools/dnstest/response.py b/tests-extra/functional/tools/dnstest/response.py
index e4535916cb9858e53ce07e8156151a0c11a58319..8a059386c586afa73f7aad73b3827b12963fcda7 100644
--- a/tests-extra/functional/tools/dnstest/response.py
+++ b/tests-extra/functional/tools/dnstest/response.py
@@ -42,11 +42,23 @@ class Response(object):
             flag_val = dns.flags.from_text(flag)
             isset(not(self.resp.flags & flag_val), "no %s flag" % flag)
 
+    def _check_eflags(self, eflags, noeflags):
+        eflag_names = eflags.split()
+        for flag in eflag_names:
+            flag_val = dns.flags.edns_from_text(flag)
+            isset(self.resp.ednsflags & flag_val, "%s flag" % flag)
+
+        eflag_names = noeflags.split()
+        for flag in eflag_names:
+            flag_val = dns.flags.edns_from_text(flag)
+            isset(not(self.resp.ednsflags & flag_val), "no %s flag" % flag)
+
     def check(self, rdata=None, ttl=None, rcode="NOERROR", flags="", \
-              noflags=""):
+              noflags="", eflags="", noeflags=""):
         '''Flags are text strings separated by whitespace character'''
 
         self._check_flags(flags, noflags)
+        self._check_eflags(eflags, noeflags)
         self._check_question()
 
         # Check rcode.
diff --git a/tests/dnssec_sign.c b/tests/dnssec_sign.c
index c043c86a9cff13b5ed693b49bca0143e950e1645..aade0d47224451e3ae70ca0bb1f5921c9a3a8fd8 100644
--- a/tests/dnssec_sign.c
+++ b/tests/dnssec_sign.c
@@ -20,8 +20,8 @@
 #include <tap/basic.h>
 
 #include "common/errcode.h"
-#include "libknot/dnssec/cleanup.h"
 #include "libknot/dnssec/config.h"
+#include "libknot/dnssec/crypto.h"
 #include "libknot/dnssec/sign.h"
 
 #ifdef KNOT_ENABLE_ECDSA
@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
 		knot_free_key_params(&kp);
 	}
 
-	knot_dnssec_cleanup();
+	knot_crypto_cleanup();
 
 	return 0;
 }