38#ifdef ENABLE_CRYPTOAPI
40#include <openssl/ssl.h>
41#include <openssl/evp.h>
42#include <openssl/err.h>
55#ifndef HAVE_XKEY_PROVIDER
60 msg(
M_NONFATAL,
"ERROR: this binary was built without cryptoapicert support");
66static XKEY_EXTERNAL_SIGN_fn xkey_cng_sign;
68typedef struct _CAPI_DATA
70 const CERT_CONTEXT *cert_context;
71 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;
84cng_hash_algo(
int md_type)
86 const wchar_t *alg = L
"UNKNOWN";
90 alg = BCRYPT_MD5_ALGORITHM;
94 alg = BCRYPT_SHA1_ALGORITHM;
98 alg = BCRYPT_SHA256_ALGORITHM;
102 alg = BCRYPT_SHA384_ALGORITHM;
106 alg = BCRYPT_SHA512_ALGORITHM;
115 msg(
M_WARN |
M_INFO,
"cryptoapicert: Unknown hash type NID=0x%x", md_type);
122CAPI_DATA_free(CAPI_DATA *cd)
124 if (!cd || cd->ref_count-- > 0)
128 if (cd->free_crypt_prov && cd->crypt_prov)
130 if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
132 NCryptFreeObject(cd->crypt_prov);
136 CryptReleaseContext(cd->crypt_prov, 0);
139 if (cd->cert_context)
141 CertFreeCertificateContext(cd->cert_context);
143 EVP_PKEY_free(cd->pubkey);
157parse_hexstring(
const char *p,
unsigned char *arr,
size_t capacity)
160 for (; *p && i < capacity; p += 2)
172 if (!isxdigit(p[0]) || !isxdigit(p[1]) || sscanf(p,
"%2hhx", &arr[i++]) != 1)
181decode_object(
struct gc_arena *
gc, LPCSTR struct_type,
const CRYPT_OBJID_BLOB *val, DWORD flags,
186 if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, struct_type, val->pbData,
187 val->cbData, flags, NULL, cb))
194 if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, struct_type, val->pbData,
195 val->cbData, flags, buf, cb))
203static const CRYPT_OID_INFO *
204find_oid(DWORD keytype,
const void *
key, DWORD groupid)
206 const CRYPT_OID_INFO *info = NULL;
209 info = CryptFindOIDInfo(keytype, (
void *)
key, groupid);
212 if (!info && groupid)
214 info = CryptFindOIDInfo(keytype, (
void *)
key, 0);
221test_certificate_template(
const char *cert_prop,
const CERT_CONTEXT *cert_ctx)
223 const CERT_INFO *info = cert_ctx->pCertInfo;
224 const CERT_EXTENSION *ext;
231 ext = CertFindExtension(szOID_CERTIFICATE_TEMPLATE, info->cExtension, info->rgExtension);
234 pvext = decode_object(&
gc, X509_CERTIFICATE_TEMPLATE, &ext->Value, 0, &cbext);
235 if (pvext && cbext >=
sizeof(CERT_TEMPLATE_EXT))
237 const CERT_TEMPLATE_EXT *cte = (
const CERT_TEMPLATE_EXT *)pvext;
238 if (!stricmp(cert_prop, cte->pszObjId))
245 const CRYPT_OID_INFO *tmpl_oid =
246 find_oid(CRYPT_OID_INFO_NAME_KEY, tmpl_name, CRYPT_TEMPLATE_OID_GROUP_ID);
247 if (tmpl_oid && !stricmp(tmpl_oid->pszOID, cte->pszObjId))
261static const CERT_CONTEXT *
262find_certificate_in_store(
const char *cert_prop, HCERTSTORE cert_store)
272 const CERT_CONTEXT *rv = NULL;
274 const void *find_param;
275 unsigned char hash[255];
276 CRYPT_HASH_BLOB blob = { .cbData = 0, .pbData =
hash };
279 if (!strncmp(cert_prop,
"SUBJ:", 5))
283 find_type = CERT_FIND_SUBJECT_STR_W;
285 else if (!strncmp(cert_prop,
"ISSUER:", 7))
288 find_type = CERT_FIND_ISSUER_STR_W;
290 else if (!strncmp(cert_prop,
"THUMB:", 6))
292 find_type = CERT_FIND_HASH;
295 blob.cbData = parse_hexstring(cert_prop + 6,
hash,
sizeof(
hash));
296 if (blob.cbData == 0)
298 msg(
M_WARN |
M_INFO,
"WARNING: cryptoapicert: error parsing <%s>.", cert_prop);
302 else if (!strncmp(cert_prop,
"TMPL:", 5))
306 find_type = CERT_FIND_HAS_PRIVATE_KEY;
310 msg(
M_NONFATAL,
"Error in cryptoapicert: unsupported certificate specification <%s>",
319 rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
320 find_type, find_param, rv);
326 if (find_type == CERT_FIND_HAS_PRIVATE_KEY && !test_certificate_template(cert_prop, rv))
330 validity = CertVerifyTimeValidity(NULL, rv->pCertInfo);
335 msg(
M_WARN |
M_INFO,
"WARNING: cryptoapicert: ignoring certificate in store %s.",
336 validity < 0 ?
"not yet valid" :
"that has expired");
344#if defined(__GNUC__) || defined(__clang__)
345#pragma GCC diagnostic push
346#pragma GCC diagnostic ignored "-Wconversion"
351xkey_cng_ec_sign(CAPI_DATA *cd,
unsigned char *sig,
size_t *siglen,
const unsigned char *tbs,
356 msg(
D_LOW,
"Signing using NCryptSignHash with EC key");
358 DWORD
status = NCryptSignHash(cd->crypt_prov, NULL, (BYTE *)tbs, tbslen, sig, len, &len, 0);
360 if (
status != ERROR_SUCCESS)
368 int derlen = ecdsa_bin2der(sig, (
int)len, *siglen);
379xkey_cng_rsa_sign(CAPI_DATA *cd,
unsigned char *sig,
size_t *siglen,
const unsigned char *tbs,
380 size_t tbslen, XKEY_SIGALG sigalg)
388 DWORD
status = ERROR_SUCCESS;
391 const wchar_t *hashalg = cng_hash_algo(OBJ_sn2nid(sigalg.mdname));
393 if (hashalg && wcscmp(hashalg, L
"UNKNOWN") == 0)
395 msg(
M_NONFATAL,
"Error in cryptoapicert: Unknown hash name <%s>", sigalg.mdname);
399 if (!strcmp(sigalg.padmode,
"pkcs1"))
401 msg(
D_LOW,
"Signing using NCryptSignHash with PKCS1 padding: hashalg <%s>", sigalg.mdname);
403 BCRYPT_PKCS1_PADDING_INFO padinfo = { hashalg };
404 status = NCryptSignHash(cd->crypt_prov, &padinfo, (BYTE *)tbs, (DWORD)tbslen, sig,
405 (DWORD)*siglen, &len, BCRYPT_PAD_PKCS1);
407 else if (!strcmp(sigalg.padmode,
"pss"))
409 int saltlen = tbslen;
410 if (!strcmp(sigalg.saltlen,
"max"))
412 saltlen = xkey_max_saltlen(EVP_PKEY_bits(cd->pubkey), tbslen);
415 msg(
M_NONFATAL,
"Error in cryptoapicert: invalid salt length (%d)", saltlen);
420 msg(
D_LOW,
"Signing using NCryptSignHash with PSS padding: hashalg <%s>, saltlen <%d>",
421 sigalg.mdname, saltlen);
423 BCRYPT_PSS_PADDING_INFO padinfo = { hashalg,
425 status = NCryptSignHash(cd->crypt_prov, &padinfo, (BYTE *)tbs, (DWORD)tbslen, sig,
426 (DWORD)*siglen, &len, BCRYPT_PAD_PSS);
430 msg(
M_NONFATAL,
"Error in cryptoapicert: Unsupported padding mode <%s>", sigalg.padmode);
434 if (
status != ERROR_SUCCESS)
442 return (*siglen > 0);
445#if defined(__GNUC__) || defined(__clang__)
446#pragma GCC diagnostic pop
451xkey_cng_sign(
void *handle,
unsigned char *sig,
size_t *siglen,
const unsigned char *tbs,
452 size_t tbslen, XKEY_SIGALG sigalg)
456 CAPI_DATA *cd = handle;
461 unsigned char mdbuf[EVP_MAX_MD_SIZE];
462 size_t buflen = _countof(mdbuf);
465 if (!strcmp(sigalg.op,
"DigestSign"))
467 if (!xkey_digest(tbs, tbslen, mdbuf, &buflen, sigalg.mdname))
475 if (!strcmp(sigalg.keytype,
"EC"))
477 return xkey_cng_ec_sign(cd, sig, siglen, tbs, tbslen);
479 else if (!strcmp(sigalg.keytype,
"RSA"))
481 return xkey_cng_rsa_sign(cd, sig, siglen, tbs, tbslen, sigalg);
490get_cert_name(
const CERT_CONTEXT *cc,
struct gc_arena *
gc)
492 DWORD len = CertGetNameStringW(cc, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, NULL, 0);
496 wchar_t *wname =
gc_malloc(len *
sizeof(
wchar_t),
false,
gc);
498 || CertGetNameStringW(cc, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, wname, len) == 0)
514Load_CryptoAPI_certificate(
const char *cert_prop, X509 **cert, EVP_PKEY **privkey)
517 CAPI_DATA *cd = calloc(1,
sizeof(*cd));
526 cs = CertOpenStore((LPCSTR)CERT_STORE_PROV_SYSTEM, 0, 0,
527 CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG
528 | CERT_STORE_READONLY_FLAG,
535 cd->cert_context = find_certificate_in_store(cert_prop, cs);
536 CertCloseStore(cs, 0);
537 if (!cd->cert_context)
539 cs = CertOpenStore((LPCSTR)CERT_STORE_PROV_SYSTEM, 0, 0,
540 CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG
541 | CERT_STORE_READONLY_FLAG,
546 "Error in cryptoapicert: failed to open machine certficate store");
549 cd->cert_context = find_certificate_in_store(cert_prop, cs);
550 CertCloseStore(cs, 0);
551 if (cd->cert_context == NULL)
553 msg(
M_NONFATAL,
"Error in cryptoapicert: certificate matching <%s> not found",
560 char *cert_name = get_cert_name(cd->cert_context, &
gc);
563 msg(
D_LOW,
"cryptapicert: using certificate with name <%s>", cert_name);
567 *cert = d2i_X509(NULL, (
const unsigned char **)&cd->cert_context->pbCertEncoded,
568 cd->cert_context->cbCertEncoded);
571 msg(
M_NONFATAL,
"Error in cryptoapicert: X509 certificate decode failed");
577 DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
578 if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL, &cd->crypt_prov,
579 &cd->key_spec, &cd->free_crypt_prov))
583 "Error in cryptoapicert: failed to acquire key. Key not present or "
584 "is in a legacy token not supported by Windows CNG API");
590 EVP_PKEY *pkey = X509_get_pubkey(*cert);
593 *privkey = xkey_load_generic_key(
tls_libctx, cd, pkey, xkey_cng_sign,
594 (XKEY_PRIVKEY_FREE_fn *)CAPI_DATA_free);
608 EVP_PKEY *privkey = NULL;
611 if (!Load_CryptoAPI_certificate(cert_prop, &cert, &privkey))
615 if (SSL_CTX_use_certificate(ssl_ctx, cert) && SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
624 EVP_PKEY_free(privkey);
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
static void gc_free(struct gc_arena *a)
static struct gc_arena gc_new(void)
void crypto_print_openssl_errors(const unsigned int flags)
Retrieve any occurred OpenSSL errors and print those errors.
Data Channel Cryptography OpenSSL-specific backend interface.
int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
static SERVICE_STATUS status
OpenSSL compatibility stub.
OSSL_LIB_CTX * tls_libctx
Garbage collection arena used to keep track of dynamically allocated memory.
Container for unidirectional cipher and HMAC key material.
char * utf16to8(const wchar_t *utf16, struct gc_arena *gc)
WCHAR * wide_string(const char *utf8, struct gc_arena *gc)