38 #ifdef ENABLE_CRYPTOAPI
40 #include <openssl/ssl.h>
41 #include <openssl/evp.h>
42 #include <openssl/err.h>
56 #ifndef HAVE_XKEY_PROVIDER
61 msg(
M_NONFATAL,
"ERROR: this binary was built without cryptoapicert support");
67 static XKEY_EXTERNAL_SIGN_fn xkey_cng_sign;
69 typedef struct _CAPI_DATA {
70 const CERT_CONTEXT *cert_context;
71 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;
83 static const wchar_t *
84 cng_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);
122 CAPI_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);
157 parse_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])
173 || sscanf(p,
"%2hhx", &arr[i++]) != 1)
181 static const CERT_CONTEXT *
182 find_certificate_in_store(
const char *cert_prop, HCERTSTORE cert_store)
191 const CERT_CONTEXT *rv = NULL;
193 const void *find_param;
194 unsigned char hash[255];
195 CRYPT_HASH_BLOB blob = {.cbData = 0, .pbData =
hash};
198 if (!strncmp(cert_prop,
"SUBJ:", 5))
202 find_type = CERT_FIND_SUBJECT_STR_W;
204 else if (!strncmp(cert_prop,
"ISSUER:", 7))
207 find_type = CERT_FIND_ISSUER_STR_W;
209 else if (!strncmp(cert_prop,
"THUMB:", 6))
211 find_type = CERT_FIND_HASH;
214 blob.cbData = parse_hexstring(cert_prop + 6,
hash,
sizeof(
hash));
215 if (blob.cbData == 0)
217 msg(
M_WARN|
M_INFO,
"WARNING: cryptoapicert: error parsing <%s>.", cert_prop);
223 msg(
M_NONFATAL,
"Error in cryptoapicert: unsupported certificate specification <%s>", cert_prop);
231 rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
232 0, find_type, find_param, rv);
235 validity = CertVerifyTimeValidity(NULL, rv->pCertInfo);
237 if (!rv || validity == 0)
241 msg(
M_WARN|
M_INFO,
"WARNING: cryptoapicert: ignoring certificate in store %s.",
242 validity < 0 ?
"not yet valid" :
"that has expired");
252 xkey_cng_ec_sign(CAPI_DATA *cd,
unsigned char *sig,
size_t *siglen,
const unsigned char *tbs,
257 msg(
D_LOW,
"Signing using NCryptSignHash with EC key");
259 DWORD
status = NCryptSignHash(cd->crypt_prov, NULL, (BYTE *)tbs, tbslen, sig, len, &len, 0);
261 if (
status != ERROR_SUCCESS)
269 int derlen = ecdsa_bin2der(sig, (
int) len, *siglen);
280 xkey_cng_rsa_sign(CAPI_DATA *cd,
unsigned char *sig,
size_t *siglen,
const unsigned char *tbs,
281 size_t tbslen, XKEY_SIGALG sigalg)
289 DWORD
status = ERROR_SUCCESS;
292 const wchar_t *hashalg = cng_hash_algo(OBJ_sn2nid(sigalg.mdname));
294 if (hashalg && wcscmp(hashalg, L
"UNKNOWN") == 0)
296 msg(
M_NONFATAL,
"Error in cryptoapicert: Unknown hash name <%s>", sigalg.mdname);
300 if (!strcmp(sigalg.padmode,
"pkcs1"))
302 msg(
D_LOW,
"Signing using NCryptSignHash with PKCS1 padding: hashalg <%s>", sigalg.mdname);
304 BCRYPT_PKCS1_PADDING_INFO padinfo = {hashalg};
305 status = NCryptSignHash(cd->crypt_prov, &padinfo, (BYTE *)tbs, (DWORD)tbslen,
306 sig, (DWORD)*siglen, &len, BCRYPT_PAD_PKCS1);
308 else if (!strcmp(sigalg.padmode,
"pss"))
310 int saltlen = tbslen;
311 if (!strcmp(sigalg.saltlen,
"max"))
313 saltlen = xkey_max_saltlen(EVP_PKEY_bits(cd->pubkey), tbslen);
316 msg(
M_NONFATAL,
"Error in cryptoapicert: invalid salt length (%d)", saltlen);
321 msg(
D_LOW,
"Signing using NCryptSignHash with PSS padding: hashalg <%s>, saltlen <%d>",
322 sigalg.mdname, saltlen);
324 BCRYPT_PSS_PADDING_INFO padinfo = {hashalg, (DWORD) saltlen};
325 status = NCryptSignHash(cd->crypt_prov, &padinfo, (BYTE *)tbs, (DWORD) tbslen,
326 sig, (DWORD)*siglen, &len, BCRYPT_PAD_PSS);
330 msg(
M_NONFATAL,
"Error in cryptoapicert: Unsupported padding mode <%s>", sigalg.padmode);
334 if (
status != ERROR_SUCCESS)
342 return (*siglen > 0);
347 xkey_cng_sign(
void *handle,
unsigned char *sig,
size_t *siglen,
const unsigned char *tbs,
348 size_t tbslen, XKEY_SIGALG sigalg)
352 CAPI_DATA *cd = handle;
357 unsigned char mdbuf[EVP_MAX_MD_SIZE];
358 size_t buflen = _countof(mdbuf);
361 if (!strcmp(sigalg.op,
"DigestSign"))
363 if (!xkey_digest(tbs, tbslen, mdbuf, &buflen, sigalg.mdname))
371 if (!strcmp(sigalg.keytype,
"EC"))
373 return xkey_cng_ec_sign(cd, sig, siglen, tbs, tbslen);
375 else if (!strcmp(sigalg.keytype,
"RSA"))
377 return xkey_cng_rsa_sign(cd, sig, siglen, tbs, tbslen, sigalg);
386 get_cert_name(
const CERT_CONTEXT *cc,
struct gc_arena *gc)
388 DWORD len = CertGetNameStringW(cc, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, NULL, 0);
392 wchar_t *wname =
gc_malloc(len*
sizeof(
wchar_t),
false, gc);
394 || CertGetNameStringW(cc, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, wname, len) == 0)
410 Load_CryptoAPI_certificate(
const char *cert_prop, X509 **cert, EVP_PKEY **privkey)
414 CAPI_DATA *cd = calloc(1,
sizeof(*cd));
423 cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER
424 |CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L
"MY");
430 cd->cert_context = find_certificate_in_store(cert_prop, cs);
431 CertCloseStore(cs, 0);
432 if (!cd->cert_context)
434 cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE
435 |CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L
"MY");
441 cd->cert_context = find_certificate_in_store(cert_prop, cs);
442 CertCloseStore(cs, 0);
443 if (cd->cert_context == NULL)
445 msg(
M_NONFATAL,
"Error in cryptoapicert: certificate matching <%s> not found", cert_prop);
451 char *cert_name = get_cert_name(cd->cert_context, &gc);
454 msg(
D_LOW,
"cryptapicert: using certificate with name <%s>", cert_name);
458 *cert = d2i_X509(NULL, (
const unsigned char **) &cd->cert_context->pbCertEncoded,
459 cd->cert_context->cbCertEncoded);
462 msg(
M_NONFATAL,
"Error in cryptoapicert: X509 certificate decode failed");
468 DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG
469 | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
470 if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL,
471 &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))
475 "is in a legacy token not supported by Windows CNG API");
481 EVP_PKEY *pkey = X509_get_pubkey(*cert);
484 *privkey = xkey_load_generic_key(
tls_libctx, cd, pkey,
485 xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free);
499 EVP_PKEY *privkey = NULL;
502 if (!Load_CryptoAPI_certificate(cert_prop, &cert, &privkey))
506 if (SSL_CTX_use_certificate(ssl_ctx, cert)
507 && SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
516 EVP_PKEY_free(privkey);