summaryrefslogtreecommitdiff
path: root/src/openvpn/cryptoapi.c
diff options
context:
space:
mode:
authorBernhard Schmidt <berni@debian.org>2020-08-15 21:29:54 +0200
committerBernhard Schmidt <berni@debian.org>2020-08-15 21:29:54 +0200
commit7c229d538824cb679351220ad8911f7b2daa7c23 (patch)
tree5c4d64b60da9018c7db3a9335a9787d326beade3 /src/openvpn/cryptoapi.c
parentd3986a312f5fbcfd0e78e6b147eef419fb4e5f54 (diff)
parent1079962e4c06f88a54e50d997c1b7e84303d30b4 (diff)
Update upstream source from tag 'upstream/2.5_beta1'
Update to upstream version '2.5~beta1' with Debian dir d53f9a482ac24eb491a294b26c24bb1d87afad24
Diffstat (limited to 'src/openvpn/cryptoapi.c')
-rw-r--r--src/openvpn/cryptoapi.c531
1 files changed, 381 insertions, 150 deletions
diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c
index 0f95d00..6c4df9e 100644
--- a/src/openvpn/cryptoapi.c
+++ b/src/openvpn/cryptoapi.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com>
+ * Copyright (c) 2018 Selva Nair <selva.nair@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifi-
@@ -103,6 +104,9 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = {
{ 0, NULL }
};
+/* 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);
@@ -114,10 +118,10 @@ typedef struct _CAPI_DATA {
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;
DWORD key_spec;
BOOL free_crypt_prov;
+ int ref_count;
} CAPI_DATA;
-/**
- * Translate OpenSSL padding type to CNG padding type
+/* Translate OpenSSL padding type to CNG padding type
* Returns 0 for unknown/unsupported padding.
*/
static DWORD
@@ -128,7 +132,6 @@ cng_padding_type(int padding)
switch (padding)
{
case RSA_NO_PADDING:
- pad = BCRYPT_PAD_NONE;
break;
case RSA_PKCS1_PADDING:
@@ -147,7 +150,7 @@ cng_padding_type(int padding)
return pad;
}
-/**
+/*
* Translate OpenSSL hash OID to CNG algorithm name. Returns
* "UNKNOWN" for unsupported algorithms and NULL for MD5+SHA1
* mixed hash used in TLS 1.1 and earlier.
@@ -190,6 +193,31 @@ cng_hash_algo(int md_type)
return alg;
}
+static void
+CAPI_DATA_free(CAPI_DATA *cd)
+{
+ if (!cd || cd->ref_count-- > 0)
+ {
+ return;
+ }
+ if (cd->free_crypt_prov && cd->crypt_prov)
+ {
+ if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
+ {
+ NCryptFreeObject(cd->crypt_prov);
+ }
+ else
+ {
+ CryptReleaseContext(cd->crypt_prov, 0);
+ }
+ }
+ if (cd->cert_context)
+ {
+ CertFreeCertificateContext(cd->cert_context);
+ }
+ free(cd);
+}
+
static char *
ms_error_text(DWORD ms_err)
{
@@ -211,7 +239,8 @@ ms_error_text(DWORD ms_err)
/* trim to the left */
if (rv)
{
- for (p = rv + strlen(rv) - 1; p >= rv; p--) {
+ for (p = rv + strlen(rv) - 1; p >= rv; p--)
+ {
if (isspace(*p))
{
*p = '\0';
@@ -250,7 +279,8 @@ err_put_ms_error(DWORD ms_err, int func, const char *file, int line)
}
/* since MS error codes are 32 bit, and the ones in the ERR_... system is
* only 12, we must have a mapping table between them. */
- for (i = 0; i < ERR_MAP_SZ; i++) {
+ for (i = 0; i < ERR_MAP_SZ; i++)
+ {
if (err_map[i].ms_err == ms_err)
{
ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line);
@@ -299,7 +329,7 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, in
* 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.
- * Only RSA is supported and padding should be BCRYPT_PAD_PKCS1 or
+ * 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
@@ -363,12 +393,6 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i
return 0;
}
- if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
- {
- return priv_enc_CNG(cd, NULL, from, flen, to, RSA_size(rsa),
- cng_padding_type(padding), 0);
- }
-
if (padding != RSA_PKCS1_PADDING)
{
/* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */
@@ -376,6 +400,12 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i
return 0;
}
+ if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
+ {
+ return priv_enc_CNG(cd, NULL, from, flen, to, RSA_size(rsa),
+ cng_padding_type(padding), 0);
+ }
+
/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would
* be way to straightforward for M$, I guess... So we have to do it this
* tricky way instead, by creating a "Hash", and load the already-made hash
@@ -447,7 +477,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i
*/
static int
rsa_sign_CNG(int type, const unsigned char *m, unsigned int m_len,
- unsigned char *sig, unsigned int *siglen, const RSA *rsa)
+ unsigned char *sig, unsigned int *siglen, const RSA *rsa)
{
CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(RSA_get_method(rsa));
const wchar_t *alg = NULL;
@@ -502,26 +532,206 @@ finish(RSA *rsa)
{
return 0;
}
- if (cd->crypt_prov && cd->free_crypt_prov)
+ CAPI_DATA_free(cd);
+ RSA_meth_free((RSA_METHOD *) rsa_meth);
+ return 1;
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC)
+
+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;
+}
+
+/**
+ * Helper to convert ECDSA signature returned by NCryptSignHash
+ * to an ECDSA_SIG structure.
+ * On entry 'buf[]' of length len contains r and s concatenated.
+ * Returns a newly allocated ECDSA_SIG or NULL (on error).
+ */
+static ECDSA_SIG *
+ecdsa_bin2sig(unsigned char *buf, int len)
+{
+ ECDSA_SIG *ecsig = NULL;
+ DWORD rlen = len/2;
+ BIGNUM *r = BN_bin2bn(buf, rlen, NULL);
+ BIGNUM *s = BN_bin2bn(buf+rlen, rlen, NULL);
+ if (!r || !s)
+ {
+ goto err;
+ }
+ ecsig = ECDSA_SIG_new(); /* in openssl 1.1 this does not allocate r, s */
+ if (!ecsig)
{
- if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
- {
- NCryptFreeObject(cd->crypt_prov);
- }
- else
+ goto err;
+ }
+ if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */
+ {
+ ECDSA_SIG_free(ecsig);
+ goto err;
+ }
+ return ecsig;
+err:
+ BN_free(r); /* it is ok to free NULL BN */
+ BN_free(s);
+ return NULL;
+}
+
+/** 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);
+ CRYPTOAPIerr(CRYPTOAPI_F_NCRYPT_SIGN_HASH);
+ }
+ 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: 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;
+
+ if (cd->key_spec != CERT_NCRYPT_KEY_SPEC)
+ {
+ msg(M_NONFATAL, "ERROR: cryptoapicert with only legacy private key handle available."
+ " EC certificate not supported.");
+ goto err;
+ }
+ /* 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)
{
- CryptReleaseContext(cd->crypt_prov, 0);
+ goto err;
}
}
- if (cd->cert_context)
+ 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))
{
- CertFreeCertificateContext(cd->cert_context);
+ EC_KEY_free(ec);
+ goto err;
}
- free(cd);
- RSA_meth_free((RSA_METHOD*) rsa_meth);
+ /* 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 /* OPENSSL_VERSION_NUMBER >= 1.1.0 */
+
static const CERT_CONTEXT *
find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
{
@@ -599,7 +809,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
goto out;
}
- while(true)
+ while (true)
{
int validity = 1;
/* this frees previous rv, if not NULL */
@@ -643,6 +853,8 @@ retrieve_capi_data(EVP_PKEY *pkey)
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))
@@ -660,7 +872,7 @@ pkey_rsa_sign_init(EVP_PKEY_CTX *ctx)
* 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 1 on success, 0 or a negative integer on error.
+ * 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,
@@ -671,9 +883,9 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
EVP_MD *md = NULL;
const wchar_t *alg = NULL;
- int padding;
- int hashlen;
- int saltlen;
+ int padding = 0;
+ int hashlen = 0;
+ int saltlen = 0;
pkey = EVP_PKEY_CTX_get0_pkey(ctx);
if (pkey)
@@ -752,7 +964,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen))
{
- msg(M_WARN|M_INFO, "cryptoapicert: unable to get the salt length from context."
+ msg(M_WARN, "cryptoapicert: unable to get the salt length from context."
" Using the default value.");
saltlen = -1;
}
@@ -784,6 +996,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
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, *siglen,
cng_padding_type(padding), (DWORD)saltlen);
@@ -792,14 +1005,131 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
#endif /* OPENSSL_VERSION >= 1.1.0 */
+static int
+ssl_ctx_set_rsakey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey)
+{
+ RSA *rsa = NULL, *pub_rsa;
+ RSA_METHOD *my_rsa_method = NULL;
+ bool rsa_method_set = false;
+
+ 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_pub_enc(my_rsa_method, rsa_pub_enc);
+ RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec);
+ RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc);
+ RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec);
+ RSA_meth_set_init(my_rsa_method, NULL);
+ RSA_meth_set_finish(my_rsa_method, finish);
+ RSA_meth_set0_app_data(my_rsa_method, cd);
+
+ /*
+ * For CNG, set the RSA_sign method which gets priority over priv_enc().
+ * This method is called with the raw hash without the digestinfo
+ * header and works better when using NCryptSignHash() with some tokens.
+ * However, if PSS padding is in use, openssl does not call this
+ * function but adds the padding and then calls rsa_priv_enc()
+ * with padding set to NONE which is not supported by CNG.
+ * So, when posisble (OpenSSL 1.1.0 and up), we hook on to the sign
+ * operation in EVP_PKEY_METHOD struct.
+ */
+ if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
+ {
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+ RSA_meth_set_sign(my_rsa_method, rsa_sign_CNG);
+#else
+ /* pmethod is global -- initialize only if NULL */
+ if (!pmethod)
+ {
+ pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0);
+ if (!pmethod)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ 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 */
+
+#if (OPENSSL_VERSION_NUMBER < 0x1010009fL) /* > version 1.1.0i */
+ /* The function signature is not const-correct in these versions */
+ EVP_PKEY_meth_get_sign((EVP_PKEY_METHOD *)default_pmethod, &default_pkey_sign_init,
+ &default_pkey_sign);
+#else
+ EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init,
+ &default_pkey_sign);
+
+#endif
+ }
+#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */
+ }
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ pub_rsa = EVP_PKEY_get0_RSA(pkey);
+ if (!pub_rsa)
+ {
+ goto err;
+ }
+
+ /* Our private key is external, so we fill in only n and e from the public key */
+ const BIGNUM *n = NULL;
+ const BIGNUM *e = NULL;
+ RSA_get0_key(pub_rsa, &n, &e, NULL);
+ BIGNUM *rsa_n = BN_dup(n);
+ BIGNUM *rsa_e = BN_dup(e);
+ if (!rsa_n || !rsa_e || !RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
+ {
+ BN_free(rsa_n); /* ok to free even if NULL */
+ BN_free(rsa_e);
+ msg(M_NONFATAL, "ERROR: %s: out of memory", __func__);
+ goto err;
+ }
+ RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY);
+ if (!RSA_set_method(rsa, my_rsa_method))
+ {
+ goto err;
+ }
+ rsa_method_set = true; /* flag that method pointer will get freed with the key */
+ cd->ref_count++; /* with method, cd gets assigned to the key as well */
+
+ if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
+ {
+ goto err;
+ }
+ /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
+ * we decrease it here with RSA_free(), or it will never be cleaned up. */
+ RSA_free(rsa);
+ return 1;
+
+err:
+ if (rsa)
+ {
+ RSA_free(rsa);
+ }
+ if (my_rsa_method && !rsa_method_set)
+ {
+ RSA_meth_free(my_rsa_method);
+ }
+ return 0;
+}
+
int
SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
{
HCERTSTORE cs;
X509 *cert = NULL;
- RSA *rsa = NULL, *pub_rsa;
CAPI_DATA *cd = calloc(1, sizeof(*cd));
- RSA_METHOD *my_rsa_method = NULL;
if (cd == NULL)
{
@@ -848,7 +1178,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG
| CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL,
- &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))
+ &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))
{
/* if we don't have a smart card reader here, and we try to access a
* smart card certificate, we get:
@@ -880,74 +1210,13 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
}
}
- 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_pub_enc(my_rsa_method, rsa_pub_enc);
- RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec);
- RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc);
- RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec);
- RSA_meth_set_init(my_rsa_method, NULL);
- RSA_meth_set_finish(my_rsa_method, finish);
- RSA_meth_set0_app_data(my_rsa_method, cd);
-
- /* For CNG, set the RSA_sign method which gets priority over priv_enc().
- * This method is called with the raw hash without the digestinfo
- * header and works better when using NCryptSignHash() with some tokens.
- * However, if PSS padding is in use, openssl does not call this
- * function but adds the padding and then calls rsa_priv_enc()
- * with padding set to NONE which is not supported by CNG.
- * So, when posisble (OpenSSL 1.1.0 and up), we hook on to the sign
- * operation in EVP_PKEY_METHOD struct.
- */
- if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
- {
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
- RSA_meth_set_sign(my_rsa_method, rsa_sign_CNG);
-#else
- /* pmethod is global -- initialize only if NULL */
- if (!pmethod)
- {
- pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0);
- if (!pmethod)
- {
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
- goto err;
- }
- 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 */
-
-#if (OPENSSL_VERSION_NUMBER < 0x1010009fL) /* < version 1.1.0i */
- /* The function signature is not const-correct in these versions */
- EVP_PKEY_meth_get_sign((EVP_PKEY_METHOD *)default_pmethod, &default_pkey_sign_init,
- &default_pkey_sign);
-#else
- EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init,
- &default_pkey_sign);
-#endif
- }
-#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */
- }
-
- rsa = RSA_new();
- if (rsa == NULL)
- {
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
- goto err;
- }
-
/* Public key in cert is NULL until we call SSL_CTX_use_certificate(),
* so we do it here then... */
if (!SSL_CTX_use_certificate(ssl_ctx, cert))
{
goto err;
}
+
/* the public key */
EVP_PKEY *pkey = X509_get0_pubkey(cert);
@@ -956,70 +1225,32 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
X509_free(cert);
cert = NULL;
- if (!(pub_rsa = EVP_PKEY_get0_RSA(pkey)))
+ if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA)
{
- msg(M_WARN, "cryptoapicert requires an RSA certificate");
- goto err;
- }
-
- /* Our private key is external, so we fill in only n and e from the public key */
- const BIGNUM *n = NULL;
- const BIGNUM *e = NULL;
- RSA_get0_key(pub_rsa, &n, &e, NULL);
- if (!RSA_set0_key(rsa, BN_dup(n), BN_dup(e), NULL))
- {
- goto err;
+ if (!ssl_ctx_set_rsakey(ssl_ctx, cd, pkey))
+ {
+ goto err;
+ }
}
- RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY);
- if (!RSA_set_method(rsa, my_rsa_method))
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC)
+ else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC)
{
- goto err;
+ if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey))
+ {
+ goto err;
+ }
}
-
- if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
+#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */
+ else
{
+ msg(M_WARN, "WARNING: cryptoapicert: certificate type not supported");
goto err;
}
- /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
- * we decrease it here with RSA_free(), or it will never be cleaned up. */
- RSA_free(rsa);
+ CAPI_DATA_free(cd); /* this will do a ref_count-- */
return 1;
err:
- if (cert)
- {
- X509_free(cert);
- }
- if (rsa)
- {
- RSA_free(rsa);
- }
- else
- {
- if (my_rsa_method)
- {
- free(my_rsa_method);
- }
- if (cd)
- {
- if (cd->free_crypt_prov && cd->crypt_prov)
- {
- if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
- {
- NCryptFreeObject(cd->crypt_prov);
- }
- else
- {
- CryptReleaseContext(cd->crypt_prov, 0);
- }
- }
- if (cd->cert_context)
- {
- CertFreeCertificateContext(cd->cert_context);
- }
- free(cd);
- }
- }
+ CAPI_DATA_free(cd);
return 0;
}