diff --git a/configure.ac b/configure.ac
index 7c2ead6a..0390a05e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -821,6 +821,17 @@ if test "${with_crypto_library}" = "openssl"; then
 		AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [OpenSSL engine support available])
 	fi
 
+	have_openssl_provider="yes"
+	AC_CHECK_FUNCS(
+		[OSSL_PROVIDER_load]
+		,
+		,
+		[have_openssl_provider="no"; break]
+	)
+	if test "${have_openssl_provider}" = "yes"; then
+		AC_DEFINE([HAVE_XKEY_PROVIDER], [1], [External key loading provider can be used])
+	fi
+
 	AC_CHECK_FUNC(
 		[EVP_aes_256_gcm],
 		,
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 5883c291..432efe73 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -128,6 +128,7 @@ openvpn_SOURCES = \
 	tls_crypt.c tls_crypt.h \
 	tun.c tun.h \
 	vlan.c vlan.h \
+	xkey_provider.c xkey_common.h \
 	win32.h win32.c \
 	win32-util.h win32-util.c \
 	cryptoapi.h cryptoapi.c
diff --git a/src/openvpn/xkey_common.h b/src/openvpn/xkey_common.h
new file mode 100644
index 00000000..eb31604f
--- /dev/null
+++ b/src/openvpn/xkey_common.h
@@ -0,0 +1,42 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2021 Selva Nair <selva.nair@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef XKEY_PUBLIC_H_
+#define XKEY_PUBLIC_H_
+
+#ifdef HAVE_XKEY_PROVIDER
+
+#include <openssl/provider.h>
+#include <openssl/core_dispatch.h>
+
+/**
+ * Initialization function for OpenVPN external key provider for OpenSSL
+ * Follows the signature of OSSL_PROVIDER init
+ */
+OSSL_provider_init_fn xkey_provider_init;
+
+#endif /* HAVE_XKEY_PROVIDER */
+
+#define XKEY_PROV_PROPS "provider=ovpn.xkey"
+
+#endif /* XKEY_PUBLIC_H_ */
diff --git a/src/openvpn/xkey_provider.c b/src/openvpn/xkey_provider.c
new file mode 100644
index 00000000..9d19d37d
--- /dev/null
+++ b/src/openvpn/xkey_provider.c
@@ -0,0 +1,177 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2021 Selva Nair <selva.nair@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#ifdef HAVE_XKEY_PROVIDER
+
+#include "syshead.h"
+#include "error.h"
+#include "buffer.h"
+#include "xkey_common.h"
+
+#include <openssl/provider.h>
+#include <openssl/params.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_object.h>
+#include <openssl/core_names.h>
+#include <openssl/store.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+/* A descriptive name */
+static const char *provname = "OpenVPN External Key Provider";
+
+typedef struct
+{
+    const OSSL_CORE_HANDLE *core;
+    OSSL_PROVIDER *deflt;  /* default provider that we load for delegating some ops */
+    OSSL_LIB_CTX *libctx;  /* libctx of the core context in which we are running */
+} XKEY_PROVIDER_CTX;
+
+/* main provider interface */
+
+/* provider callbacks we implement */
+static OSSL_FUNC_provider_query_operation_fn query_operation;
+static OSSL_FUNC_provider_gettable_params_fn gettable_params;
+static OSSL_FUNC_provider_get_params_fn get_params;
+static OSSL_FUNC_provider_teardown_fn teardown;
+
+static const OSSL_ALGORITHM *
+query_operation(void *provctx, int op, int *no_store)
+{
+    dmsg(D_LOW, "In xkey provider query op with op = %d", op);
+
+    *no_store = 0;
+
+    switch (op)
+    {
+        case OSSL_OP_SIGNATURE:
+            return NULL;
+
+        case OSSL_OP_KEYMGMT:
+            return NULL;
+
+        default:
+            break;
+    }
+    return NULL;
+}
+
+static const OSSL_PARAM *
+gettable_params(void *provctx)
+{
+    dmsg(D_LOW, "In xkey provider gettable_params");
+
+    static const OSSL_PARAM param_types[] = {
+        OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0),
+        OSSL_PARAM_END
+    };
+
+    return param_types;
+}
+static int
+get_params(void *provctx, OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    dmsg(D_LOW, "In xkey provider get_params");
+
+    p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_NAME);
+    if (p)
+    {
+        return (OSSL_PARAM_set_utf8_ptr(p, provname) != 0);
+    }
+
+    return 0;
+}
+
+static void
+teardown(void *provctx)
+{
+    dmsg(D_LOW, "In xkey provider teardown");
+
+    XKEY_PROVIDER_CTX *prov = provctx;
+    if (prov && prov->deflt)
+    {
+        OSSL_PROVIDER_unload(prov->deflt);
+    }
+    free(prov);
+}
+
+static const OSSL_DISPATCH dispatch_table[] = {
+    {OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))gettable_params},
+    {OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))get_params},
+    {OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))query_operation},
+    {OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))teardown},
+    {0, NULL}
+};
+
+int
+xkey_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in,
+                   const OSSL_DISPATCH **out, void **provctx)
+{
+    XKEY_PROVIDER_CTX *prov;
+    OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL;
+
+    dmsg(D_LOW, "In xkey provider init");
+
+    prov = calloc(sizeof(*prov), 1);
+    if (!prov)
+    {
+        msg(M_NONFATAL, "xkey_provider_init: out of memory");
+        return 0;
+    }
+
+    /* get our libctx */
+    for (; in->function_id != 0; in++)
+    {
+        if (in->function_id == OSSL_FUNC_CORE_GET_LIBCTX)
+        {
+            c_get_libctx = OSSL_FUNC_core_get_libctx(in);
+        }
+    }
+
+    if (c_get_libctx)
+    {
+        prov->libctx = (OSSL_LIB_CTX *)c_get_libctx(handle);
+    }
+    prov->core = handle;
+
+    prov->deflt = OSSL_PROVIDER_load(prov->libctx, "default");
+    if (!prov->deflt)
+    {
+        msg(M_NONFATAL, "xkey_provider_init: default provider could not be loaded");
+    }
+
+    *out = dispatch_table;
+    *provctx = prov;
+
+    return 1;
+}
+
+#endif /* HAVE_XKEY_PROVIDER */
