@@ -72,9 +72,6 @@
#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
#endif
-/* Size of an SSL signature: MD5+SHA1 */
-#define SSL_SIG_LENGTH 36
-
/* try to funnel any Windows/CryptoAPI error messages to OpenSSL ERR_... */
#define ERR_LIB_CRYPTOAPI (ERR_LIB_USER + 69) /* 69 is just a number... */
#define CRYPTOAPIerr(f) err_put_ms_error(GetLastError(), (f), __FILE__, __LINE__)
@@ -305,26 +302,6 @@ err_put_ms_error(DWORD ms_err, int func, const char *file, int line)
}
}
-/* encrypt */
-static int
-rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
-{
- /* I haven't been able to trigger this one, but I want to know if it happens... */
- assert(0);
-
- return 0;
-}
-
-/* verify arbitrary data */
-static int
-rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
-{
- /* I haven't been able to trigger this one, but I want to know if it happens... */
- assert(0);
-
- return 0;
-}
-
/**
* 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
@@ -378,152 +355,9 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char
return len;
}
-/* sign arbitrary data */
-static int
-rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
-{
- CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(RSA_get_method(rsa));
- HCRYPTHASH hash;
- DWORD hash_size, len, i;
- unsigned char *buf;
-
- if (cd == NULL)
- {
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
-
- if (padding != RSA_PKCS1_PADDING)
- {
- /* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
- 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
- * from 'from' into it. */
- /* For now, we only support NID_md5_sha1 */
- if (flen != SSL_SIG_LENGTH)
- {
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
- return 0;
- }
- if (!CryptCreateHash(cd->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
- {
- CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_CREATE_HASH);
- return 0;
- }
- len = sizeof(hash_size);
- if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0))
- {
- CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_GET_HASH_PARAM);
- CryptDestroyHash(hash);
- return 0;
- }
- if ((int) hash_size != flen)
- {
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
- CryptDestroyHash(hash);
- return 0;
- }
- if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0))
- {
- CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SET_HASH_PARAM);
- CryptDestroyHash(hash);
- return 0;
- }
-
- len = RSA_size(rsa);
- buf = malloc(len);
- if (buf == NULL)
- {
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
- CryptDestroyHash(hash);
- return 0;
- }
- if (!CryptSignHash(hash, cd->key_spec, NULL, 0, buf, &len))
- {
- CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SIGN_HASH);
- CryptDestroyHash(hash);
- free(buf);
- return 0;
- }
- /* and now, we have to reverse the byte-order in the result from CryptSignHash()... */
- for (i = 0; i < len; i++)
- {
- to[i] = buf[len - i - 1];
- }
- free(buf);
-
- CryptDestroyHash(hash);
- return len;
-}
-
-/**
- * Sign the hash in |m| and return the signature in |sig|.
- * Returns 1 on success, 0 on error.
- * NCryptSignHash() is used to sign and it is instructed to add the
- * the PKCS #1 DigestInfo header to |m| unless the hash algorithm is
- * the MD5/SHA1 combination used in TLS 1.1 and earlier versions.
- * OpenSSL exercises this callback only when padding is PKCS1 v1.5.
- */
-static int
-rsa_sign_CNG(int type, const unsigned char *m, unsigned int m_len,
- 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;
- int padding = RSA_PKCS1_PADDING;
-
- *siglen = 0;
- if (cd == NULL)
- {
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
-
- alg = cng_hash_algo(type);
- if (alg && wcscmp(alg, L"UNKNOWN") == 0)
- {
- RSAerr(RSA_F_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
- return 0;
- }
-
- *siglen = priv_enc_CNG(cd, alg, m, (int)m_len, sig, RSA_size(rsa),
- cng_padding_type(padding), 0);
-
- return (*siglen == 0) ? 0 : 1;
-}
-
-/* decrypt */
-static int
-rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
-{
- /* I haven't been able to trigger this one, but I want to know if it happens... */
- assert(0);
-
- return 0;
-}
-
-/* called at RSA_new */
-static int
-init(RSA *rsa)
-{
-
- return 0;
-}
-
/* called at RSA_free */
static int
-finish(RSA *rsa)
+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);
@@ -537,7 +371,7 @@ finish(RSA *rsa)
return 1;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC)
+#if !defined(OPENSSL_NO_EC)
static EC_KEY_METHOD *ec_method = NULL;
@@ -657,12 +491,6 @@ 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)
@@ -730,7 +558,7 @@ err:
return 0;
}
-#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */
+#endif /* !defined(OPENSSL_NO_EC) */
static const CERT_CONTEXT *
find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
@@ -832,8 +660,6 @@ out:
return rv;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-
static const CAPI_DATA *
retrieve_capi_data(EVP_PKEY *pkey)
{
@@ -1003,125 +829,80 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
return (*siglen == 0) ? 0 : 1;
}
-#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 *rsa = NULL;
RSA_METHOD *my_rsa_method = NULL;
- bool rsa_method_set = false;
+ 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_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_set_finish(my_rsa_method, rsa_finish); /* we use this callback to cleanup CAPI_DATA */
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)
+ /* pmethod is global -- initialize only if NULL */
+ if (!pmethod)
{
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
- RSA_meth_set_sign(my_rsa_method, rsa_sign_CNG);
-#else
- /* pmethod is global -- initialize only if NULL */
+ pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0);
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);
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA);
+ EVP_PKEY_meth_copy(pmethod, default_pmethod);
- /* Keep a copy of the default sign and sign_init methods */
+ /* 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);
-#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);
+ /* Keep a copy of the default sign and sign_init methods */
-#endif
- }
-#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */
+ EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init,
+ &default_pkey_sign);
}
- rsa = RSA_new();
- if (rsa == NULL)
- {
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
- goto err;
- }
+ rsa = EVP_PKEY_get1_RSA(pkey);
- 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;
+ goto cleanup;
}
- rsa_method_set = true; /* flag that method pointer will get freed with the key */
+ 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 */
- if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
+ privkey = EVP_PKEY_new();
+ if (!EVP_PKEY_assign_RSA(privkey, rsa))
{
- goto err;
+ goto cleanup;
}
- /* 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;
+ rsa = NULL; /* privkey has taken ownership */
-err:
+ if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
+ {
+ goto cleanup;
+ }
+ ret = 1;
+
+cleanup:
if (rsa)
{
RSA_free(rsa);
}
- if (my_rsa_method && !rsa_method_set)
+ if (my_rsa_method)
{
RSA_meth_free(my_rsa_method);
}
- return 0;
+ if (privkey)
+ {
+ EVP_PKEY_free(privkey);
+ }
+
+ return ret;
}
int
@@ -1174,9 +955,9 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
}
/* set up stuff to use the private key */
- /* We prefer to get an NCRYPT key handle so that TLS1.2 can be supported */
+ /* We support NCRYPT key handles only */
DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG
- | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
+ | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL,
&cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))
{
@@ -1189,27 +970,6 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
/* here we don't need to do CryptGetUserKey() or anything; all necessary key
* info is in cd->cert_context, and then, in cd->crypt_prov. */
- /* if we do not have an NCRYPT key handle restrict TLS to v1.1 or lower */
- int max_version = SSL_CTX_get_max_proto_version(ssl_ctx);
- if ((!max_version || max_version > TLS1_1_VERSION)
- && cd->key_spec != CERT_NCRYPT_KEY_SPEC)
- {
- msg(M_WARN, "WARNING: cryptoapicert: private key is in a legacy store."
- " Restricting TLS version to 1.1");
- if (SSL_CTX_get_min_proto_version(ssl_ctx) > TLS1_1_VERSION)
- {
- msg(M_NONFATAL,
- "ERROR: cryptoapicert: min TLS version larger than 1.1."
- " Try config option --tls-version-min 1.1");
- goto err;
- }
- if (!SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_1_VERSION))
- {
- msg(M_NONFATAL, "ERROR: cryptoapicert: set max TLS version failed");
- 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))
@@ -1232,7 +992,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
goto err;
}
}
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC)
+#if !defined(OPENSSL_NO_EC)
else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC)
{
if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey))
@@ -1240,7 +1000,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
goto err;
}
}
-#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */
+#endif /* !defined(OPENSSL_NO_EC) */
else
{
msg(M_WARN, "WARNING: cryptoapicert: certificate type not supported");