diff options
Diffstat (limited to 'src/openvpn/cryptoapi.c')
-rw-r--r-- | src/openvpn/cryptoapi.c | 531 |
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; } |