[Openvpn-devel,3/4] cryptoapi.c: remove pre OpenSSL-3.01 support

Message ID 20230128223421.2207802-4-selva.nair@gmail.com
State Superseded
Headers show
Series Improvements for cryptoapi.c | expand

Commit Message

Selva Nair Jan. 28, 2023, 10:34 p.m. UTC
From: Selva Nair <selva.nair@gmail.com>

- Require xkey-provider (thus OpenSSL 3.01+) for --cryptoapicert

Note:
  Ideally we should also make ENABLE_CRYPTOAPI conditional
  on HAVE_XKEY_PROVIDER but that looks hard unless we can agree
  to move HAVE_XKEY_PROVIDER to configure/config.h.
  Or move ENABLE_CRYPTOAPI out of syshead.h ?

Signed-off-by: Selva Nair <selva.nair@gmail.com>
---
 src/openvpn/cryptoapi.c | 555 +---------------------------------------
 src/openvpn/options.c   |   2 +-
 2 files changed, 11 insertions(+), 546 deletions(-)

Comments

Frank Lichtenheld Feb. 1, 2023, 12:01 p.m. UTC | #1
On Sat, Jan 28, 2023 at 05:34:20PM -0500, selva.nair@gmail.com wrote:
> From: Selva Nair <selva.nair@gmail.com>
> 
> - Require xkey-provider (thus OpenSSL 3.01+) for --cryptoapicert
> 
> Note:
>   Ideally we should also make ENABLE_CRYPTOAPI conditional
>   on HAVE_XKEY_PROVIDER but that looks hard unless we can agree
>   to move HAVE_XKEY_PROVIDER to configure/config.h.
>   Or move ENABLE_CRYPTOAPI out of syshead.h ?
> 
> Signed-off-by: Selva Nair <selva.nair@gmail.com>
> ---
>  src/openvpn/cryptoapi.c | 555 +---------------------------------------
>  src/openvpn/options.c   |   2 +-
>  2 files changed, 11 insertions(+), 546 deletions(-)
> 
> diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c
> index e3c0bc99..6ff4fcb5 100644
> --- a/src/openvpn/cryptoapi.c
> +++ b/src/openvpn/cryptoapi.c
> @@ -55,17 +55,17 @@
>  #include "xkey_common.h"
>  
>  #ifndef HAVE_XKEY_PROVIDER
> -/* index for storing external data in EC_KEY: < 0 means uninitialized */
> -static int ec_data_idx = -1;
>  
> -/* Global EVP_PKEY_METHOD used to override the sign operation */
> -static EVP_PKEY_METHOD *pmethod;
> -static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx);
> -static int (*default_pkey_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig,
> -                                 size_t *siglen, const unsigned char *tbs, size_t tbslen);
> -#else  /* ifndef HAVE_XKEY_PROVIDER */
> +int
> +SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
> +{
> +    msg(M_NONFATAL, "ERROR: cryptoapicert not supported in this version");

"in this version" sounds wrong to me. That might indicate to the user that
they need a newer or older version of OpenVPN. Maybe
"this binary was compiled without cryptoapicert support"?

Regards,

Patch

diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c
index e3c0bc99..6ff4fcb5 100644
--- a/src/openvpn/cryptoapi.c
+++ b/src/openvpn/cryptoapi.c
@@ -55,17 +55,17 @@ 
 #include "xkey_common.h"
 
 #ifndef HAVE_XKEY_PROVIDER
-/* index for storing external data in EC_KEY: < 0 means uninitialized */
-static int ec_data_idx = -1;
 
-/* Global EVP_PKEY_METHOD used to override the sign operation */
-static EVP_PKEY_METHOD *pmethod;
-static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx);
-static int (*default_pkey_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig,
-                                 size_t *siglen, const unsigned char *tbs, size_t tbslen);
-#else  /* ifndef HAVE_XKEY_PROVIDER */
+int
+SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
+{
+    msg(M_NONFATAL, "ERROR: cryptoapicert not supported in this version");
+    return 0;
+}
+
+#else /* HAVE_XKEY_PROVIDER */
+
 static XKEY_EXTERNAL_SIGN_fn xkey_cng_sign;
-#endif /* HAVE_XKEY_PROVIDER */
 
 typedef struct _CAPI_DATA {
     const CERT_CONTEXT *cert_context;
@@ -146,127 +146,6 @@  CAPI_DATA_free(CAPI_DATA *cd)
     free(cd);
 }
 
-#ifndef HAVE_XKEY_PROVIDER
-
-/* Translate OpenSSL padding type to CNG padding type
- * Returns 0 for unknown/unsupported padding.
- */
-static DWORD
-cng_padding_type(int padding)
-{
-    DWORD pad = 0;
-
-    switch (padding)
-    {
-        case RSA_NO_PADDING:
-            break;
-
-        case RSA_PKCS1_PADDING:
-            pad = BCRYPT_PAD_PKCS1;
-            break;
-
-        case RSA_PKCS1_PSS_PADDING:
-            pad = BCRYPT_PAD_PSS;
-            break;
-
-        default:
-            msg(M_WARN|M_INFO, "cryptoapicert: unknown OpenSSL padding type %d.",
-                padding);
-    }
-
-    return pad;
-}
-
-/**
- * Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT
- * key handle in cd->crypt_prov. On return the signature is in 'to'. Returns
- * the length of the signature or 0 on error.
- * This is used only for RSA and padding should be BCRYPT_PAD_PKCS1 or
- * BCRYPT_PAD_PSS.
- * If the hash_algo is not NULL, PKCS #1 DigestInfo header gets added
- * to |from|, else it is signed as is. Use NULL for MD5 + SHA1 hash used
- * in TLS 1.1 and earlier.
- * In case of PSS padding, |saltlen| should specify the size of salt to use.
- * If |to| is NULL returns the required buffer size.
- */
-static int
-priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char *from,
-             int flen, unsigned char *to, int tlen, DWORD padding, DWORD saltlen)
-{
-    NCRYPT_KEY_HANDLE hkey = cd->crypt_prov;
-    DWORD len = 0;
-    ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC);
-
-    DWORD status;
-
-    msg(D_LOW, "Signing hash using CNG: data size = %d padding = %lu", flen, padding);
-
-    if (padding == BCRYPT_PAD_PKCS1)
-    {
-        BCRYPT_PKCS1_PADDING_INFO padinfo = {hash_algo};
-        status = NCryptSignHash(hkey, &padinfo, (BYTE *)from, flen,
-                                to, tlen, &len, padding);
-    }
-    else if (padding == BCRYPT_PAD_PSS)
-    {
-        BCRYPT_PSS_PADDING_INFO padinfo = {hash_algo, saltlen};
-        status = NCryptSignHash(hkey, &padinfo, (BYTE *)from, flen,
-                                to, tlen, &len, padding);
-    }
-    else
-    {
-        msg(M_NONFATAL, "Error in cryptoapicert: Unknown padding type");
-        return 0;
-    }
-
-    if (status != ERROR_SUCCESS)
-    {
-        SetLastError(status);
-        msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: NCryptSignHash failed");
-        len = 0;
-    }
-
-    /* Unlike CAPI, CNG signature is in big endian order. No reversing needed. */
-    return len;
-}
-
-/* called at RSA_free */
-static int
-rsa_finish(RSA *rsa)
-{
-    const RSA_METHOD *rsa_meth = RSA_get_method(rsa);
-    CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(rsa_meth);
-
-    if (cd == NULL)
-    {
-        return 0;
-    }
-    CAPI_DATA_free(cd);
-    RSA_meth_free((RSA_METHOD *) rsa_meth);
-    return 1;
-}
-
-static EC_KEY_METHOD *ec_method = NULL;
-
-/** EC_KEY_METHOD callback: called when the key is freed */
-static void
-ec_finish(EC_KEY *ec)
-{
-    EC_KEY_METHOD_free(ec_method);
-    ec_method = NULL;
-    CAPI_DATA *cd = EC_KEY_get_ex_data(ec, ec_data_idx);
-    CAPI_DATA_free(cd);
-    EC_KEY_set_ex_data(ec, ec_data_idx, NULL);
-}
-
-/** EC_KEY_METHOD callback sign_setup(): we do nothing here */
-static int
-ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp)
-{
-    return 1;
-}
-#endif /* HAVE_XKEY_PROVIDER */
-
 /**
  * Helper to convert ECDSA signature returned by NCryptSignHash
  * to an ECDSA_SIG structure.
@@ -301,141 +180,6 @@  err:
     return NULL;
 }
 
-#ifndef HAVE_XKEY_PROVIDER
-
-/** EC_KEY_METHOD callback sign_sig(): sign and return an ECDSA_SIG pointer. */
-static ECDSA_SIG *
-ecdsa_sign_sig(const unsigned char *dgst, int dgstlen,
-               const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec)
-{
-    ECDSA_SIG *ecsig = NULL;
-    CAPI_DATA *cd = (CAPI_DATA *)EC_KEY_get_ex_data(ec, ec_data_idx);
-
-    ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC);
-
-    NCRYPT_KEY_HANDLE hkey = cd->crypt_prov;
-    BYTE buf[512]; /* large enough buffer for signature to avoid malloc */
-    DWORD len = _countof(buf);
-
-    msg(D_LOW, "Cryptoapi: signing hash using EC key: data size = %d", dgstlen);
-
-    DWORD status = NCryptSignHash(hkey, NULL, (BYTE *)dgst, dgstlen, (BYTE *)buf, len, &len, 0);
-    if (status != ERROR_SUCCESS)
-    {
-        SetLastError(status);
-        msg(M_NONFATAL|M_ERRNO, "Error in cryptoapticert: NCryptSignHash failed");
-    }
-    else
-    {
-        /* NCryptSignHash returns r, s concatenated in buf[] */
-        ecsig = ecdsa_bin2sig(buf, len);
-    }
-    return ecsig;
-}
-
-/** EC_KEY_METHOD callback sign(): sign and return a DER encoded signature */
-static int
-ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig,
-           unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec)
-{
-    ECDSA_SIG *s;
-
-    *siglen = 0;
-    s = ecdsa_sign_sig(dgst, dgstlen, NULL, NULL, ec);
-    if (s == NULL)
-    {
-        return 0;
-    }
-
-    /* convert internal signature structure 's' to DER encoded byte array in sig */
-    int len = i2d_ECDSA_SIG(s, NULL);
-    if (len > ECDSA_size(ec))
-    {
-        ECDSA_SIG_free(s);
-        msg(M_NONFATAL, "Error in cryptoapicert: DER encoded ECDSA signature is too long (%d bytes)", len);
-        return 0;
-    }
-    *siglen = i2d_ECDSA_SIG(s, &sig);
-    ECDSA_SIG_free(s);
-
-    return 1;
-}
-
-static int
-ssl_ctx_set_eckey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey)
-{
-    EC_KEY *ec = NULL;
-    EVP_PKEY *privkey = NULL;
-
-    /* create a method struct with default callbacks filled in */
-    ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
-    if (!ec_method)
-    {
-        goto err;
-    }
-
-    /* We only need to set finish among init methods, and sign methods */
-    EC_KEY_METHOD_set_init(ec_method, NULL, ec_finish, NULL, NULL, NULL, NULL);
-    EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig);
-
-    ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey));
-    if (!ec)
-    {
-        goto err;
-    }
-    if (!EC_KEY_set_method(ec, ec_method))
-    {
-        goto err;
-    }
-
-    /* get an index to store cd as external data */
-    if (ec_data_idx < 0)
-    {
-        ec_data_idx = EC_KEY_get_ex_new_index(0, "cryptapicert ec key", NULL, NULL, NULL);
-        if (ec_data_idx < 0)
-        {
-            goto err;
-        }
-    }
-    EC_KEY_set_ex_data(ec, ec_data_idx, cd);
-
-    /* cd assigned to ec as ex_data, increase its refcount */
-    cd->ref_count++;
-
-    privkey = EVP_PKEY_new();
-    if (!EVP_PKEY_assign_EC_KEY(privkey, ec))
-    {
-        EC_KEY_free(ec);
-        goto err;
-    }
-    /* from here on ec will get freed with privkey */
-
-    if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
-    {
-        goto err;
-    }
-    EVP_PKEY_free(privkey); /* this will dn_ref or free ec as well */
-    return 1;
-
-err:
-    if (privkey)
-    {
-        EVP_PKEY_free(privkey);
-    }
-    else if (ec)
-    {
-        EC_KEY_free(ec);
-    }
-    if (ec_method) /* do always set ec_method = NULL after freeing it */
-    {
-        EC_KEY_METHOD_free(ec_method);
-        ec_method = NULL;
-    }
-    return 0;
-}
-
-#endif /* !HAVE_XKEY_PROVIDER */
-
 static const CERT_CONTEXT *
 find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
 {
@@ -541,254 +285,6 @@  out:
     return rv;
 }
 
-#ifndef HAVE_XKEY_PROVIDER
-
-static const CAPI_DATA *
-retrieve_capi_data(EVP_PKEY *pkey)
-{
-    const CAPI_DATA *cd = NULL;
-
-    if (pkey && EVP_PKEY_id(pkey) == EVP_PKEY_RSA)
-    {
-        RSA *rsa = EVP_PKEY_get0_RSA(pkey);
-        if (rsa)
-        {
-            cd = (CAPI_DATA *)RSA_meth_get0_app_data(RSA_get_method(rsa));
-        }
-    }
-    return cd;
-}
-
-static int
-pkey_rsa_sign_init(EVP_PKEY_CTX *ctx)
-{
-    msg(D_LOW, "cryptoapicert: enter pkey_rsa_sign_init");
-
-    EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx);
-
-    if (pkey && retrieve_capi_data(pkey))
-    {
-        return 1; /* Return success */
-    }
-    else if (default_pkey_sign_init)  /* Not our key. Call the default method */
-    {
-        return default_pkey_sign_init(ctx);
-    }
-    return 1;
-}
-
-/**
- * Implementation of EVP_PKEY_sign() using CNG: sign the digest in |tbs|
- * and save the the signature in |sig| and its size in |*siglen|.
- * If |sig| is NULL the required buffer size is returned in |*siglen|.
- * Returns value is 1 on success, 0 or a negative integer on error.
- */
-static int
-pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
-              const unsigned char *tbs, size_t tbslen)
-{
-    EVP_PKEY *pkey = NULL;
-    const CAPI_DATA *cd = NULL;
-    EVP_MD *md = NULL;
-    const wchar_t *alg = NULL;
-
-    int padding = 0;
-    int hashlen = 0;
-    int saltlen = 0;
-
-    pkey = EVP_PKEY_CTX_get0_pkey(ctx);
-    if (pkey)
-    {
-        cd = retrieve_capi_data(pkey);
-    }
-
-    /*
-     * We intercept all sign requests, not just the one's for our key.
-     * Check the key and call the saved OpenSSL method for unknown keys.
-     */
-    if (!pkey || !cd)
-    {
-        if (default_pkey_sign)
-        {
-            return default_pkey_sign(ctx, sig, siglen, tbs, tbslen);
-        }
-        else  /* This should not happen */
-        {
-            msg(M_FATAL, "Error in cryptoapicert: Unknown key and no default sign operation to fallback on");
-            return -1;
-        }
-    }
-
-    if (!EVP_PKEY_CTX_get_rsa_padding(ctx, &padding))
-    {
-        padding = RSA_PKCS1_PADDING; /* Default padding for RSA */
-    }
-
-    if (EVP_PKEY_CTX_get_signature_md(ctx, &md))
-    {
-        hashlen = EVP_MD_size(md);
-        alg = cng_hash_algo(EVP_MD_type(md));
-
-        /*
-         * alg == NULL indicates legacy MD5+SHA1 hash, else alg should be a valid
-         * digest algorithm.
-         */
-        if (alg && wcscmp(alg, L"UNKNOWN") == 0)
-        {
-            msg(M_NONFATAL, "Error in cryptoapicert: Unknown hash algorithm <%d>", EVP_MD_type(md));
-            return -1;
-        }
-    }
-    else
-    {
-        msg(M_NONFATAL, "Error in cryptoapicert: could not determine the signature digest algorithm");
-        return -1;
-    }
-
-    if (tbslen != (size_t)hashlen)
-    {
-        msg(M_NONFATAL, "Error in cryptoapicert: data size does not match hash");
-        return -1;
-    }
-
-    /* If padding is PSS, determine parameters to pass to CNG */
-    if (padding == RSA_PKCS1_PSS_PADDING)
-    {
-        /*
-         * Ensure the digest type for signature and mask generation match.
-         * In CNG there is no option to specify separate hash functions for
-         * the two, but OpenSSL supports it. However, I have not seen the
-         * two being different in practice. Also the recommended practice is
-         * to use the same for both (rfc 8017 sec 8.1).
-         */
-        EVP_MD *mgf1md;
-        if (!EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md)
-            || EVP_MD_type(mgf1md) != EVP_MD_type(md))
-        {
-            msg(M_NONFATAL, "Error in cryptoapicert: Unknown MGF1 digest type or does"
-                " not match the signature digest type.");
-            return -1;
-        }
-
-        if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen))
-        {
-            msg(M_WARN|M_INFO, "cryptoapicert: unable to get the salt length from context."
-                " Using the default value.");
-            saltlen = -1;
-        }
-
-        /*
-         * In OpenSSL saltlen = -1 indicates to use the size of the digest as
-         * size of the salt. A value of -2 or -3 indicates maximum salt length
-         * that will fit. See RSA_padding_add_PKCS1_PSS_mgf1() of OpenSSL.
-         */
-        if (saltlen == -1)
-        {
-            saltlen = hashlen;
-        }
-        else if (saltlen < 0)
-        {
-            const RSA *rsa = EVP_PKEY_get0_RSA(pkey);
-            saltlen = RSA_size(rsa) - hashlen - 2; /* max salt length for RSASSA-PSS */
-            if (RSA_bits(rsa) &0x7) /* number of bits in the key not a multiple of 8 */
-            {
-                saltlen--;
-            }
-        }
-
-        if (saltlen < 0)
-        {
-            msg(M_NONFATAL, "Error in cryptoapicert: invalid salt length (%d). Digest too large for keysize?", saltlen);
-            return -1;
-        }
-        msg(D_LOW, "cryptoapicert: PSS padding using saltlen = %d", saltlen);
-    }
-
-    msg(D_LOW, "cryptoapicert: calling priv_enc_CNG with alg = %ls", alg);
-    *siglen = priv_enc_CNG(cd, alg, tbs, (int)tbslen, sig, (int)*siglen,
-                           cng_padding_type(padding), (DWORD)saltlen);
-
-    return (*siglen == 0) ? 0 : 1;
-}
-
-static int
-ssl_ctx_set_rsakey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey)
-{
-    RSA *rsa = NULL;
-    RSA_METHOD *my_rsa_method = NULL;
-    EVP_PKEY *privkey = NULL;
-    int ret = 0;
-
-    my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method",
-                                 RSA_METHOD_FLAG_NO_CHECK);
-    check_malloc_return(my_rsa_method);
-    RSA_meth_set_finish(my_rsa_method, rsa_finish); /* we use this callback to cleanup CAPI_DATA */
-    RSA_meth_set0_app_data(my_rsa_method, cd);
-
-    /* pmethod is global -- initialize only if NULL */
-    if (!pmethod)
-    {
-        pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0);
-        if (!pmethod)
-        {
-            msg(M_NONFATAL, "Error in cryptoapicert: failed to create EVP_PKEY_METHOD");
-            return 0;
-        }
-        const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA);
-        EVP_PKEY_meth_copy(pmethod, default_pmethod);
-
-        /* We want to override only sign_init() and sign() */
-        EVP_PKEY_meth_set_sign(pmethod, pkey_rsa_sign_init, pkey_rsa_sign);
-        EVP_PKEY_meth_add0(pmethod);
-
-        /* Keep a copy of the default sign and sign_init methods */
-
-        EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init,
-                               &default_pkey_sign);
-    }
-
-    rsa = EVP_PKEY_get1_RSA(pkey);
-
-    RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY);
-    if (!RSA_set_method(rsa, my_rsa_method))
-    {
-        goto cleanup;
-    }
-    my_rsa_method = NULL;  /* we do not want to free it in cleanup */
-    cd->ref_count++;       /* with method, cd gets assigned to the key as well */
-
-    privkey = EVP_PKEY_new();
-    if (!EVP_PKEY_assign_RSA(privkey, rsa))
-    {
-        goto cleanup;
-    }
-    rsa = NULL; /* privkey has taken ownership */
-
-    if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
-    {
-        goto cleanup;
-    }
-    ret = 1;
-
-cleanup:
-    if (rsa)
-    {
-        RSA_free(rsa);
-    }
-    if (my_rsa_method)
-    {
-        RSA_meth_free(my_rsa_method);
-    }
-    if (privkey)
-    {
-        EVP_PKEY_free(privkey);
-    }
-
-    return ret;
-}
-
-#else /* HAVE_XKEY_PROVIDER */
-
 /** Sign hash in tbs using EC key in cd and NCryptSignHash */
 static int
 xkey_cng_ec_sign(CAPI_DATA *cd, unsigned char *sig, size_t *siglen, const unsigned char *tbs,
@@ -937,8 +433,6 @@  xkey_cng_sign(void *handle, unsigned char *sig, size_t *siglen, const unsigned c
     }
 }
 
-#endif /* HAVE_XKEY_PROVIDER */
-
 static char *
 get_cert_name(const CERT_CONTEXT *cc, struct gc_arena *gc)
 {
@@ -1043,45 +537,16 @@  SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
     X509_free(cert);
     cert = NULL;
 
-#ifdef HAVE_XKEY_PROVIDER
-
     EVP_PKEY *privkey = xkey_load_generic_key(tls_libctx, cd, pkey,
                                               xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free);
     SSL_CTX_use_PrivateKey(ssl_ctx, privkey);
     gc_free(&gc);
     return 1; /* do not free cd -- its kept by xkey provider */
 
-#else  /* ifdef HAVE_XKEY_PROVIDER */
-
-    if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA)
-    {
-        if (!ssl_ctx_set_rsakey(ssl_ctx, cd, pkey))
-        {
-            goto err;
-        }
-    }
-    else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC)
-    {
-        if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey))
-        {
-            goto err;
-        }
-    }
-    else
-    {
-        msg(M_WARN|M_INFO, "WARNING: cryptoapicert: key type <%d> not supported",
-            EVP_PKEY_id(pkey));
-        goto err;
-    }
-    CAPI_DATA_free(cd); /* this will do a ref_count-- */
-    gc_free(gc);
-    return 1;
-
-#endif /* HAVE_XKEY_PROVIDER */
-
 err:
     CAPI_DATA_free(cd);
     gc_free(&gc);
     return 0;
 }
+#endif  /* HAVE_XKEY_PROVIDER */
 #endif                          /* _WIN32 */
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index f24af3d7..e18b3b39 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8864,7 +8864,7 @@  add_option(struct options *options,
             listend->next = newlist;
         }
     }
-#ifdef ENABLE_CRYPTOAPI
+#if defined(ENABLE_CRYPTOAPI) && defined(HAVE_XKEY_PROVIDER)
     else if (streq(p[0], "cryptoapicert") && p[1] && !p[2])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);